diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..08b8d5815ddc1d8dc8c2420af52ad3b323f3e856 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,3 +33,10 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +blocks/Backdrops/beach_malibu.sb3/050615fe992a00d6af0e664e497ebf53.png filter=lfs diff=lfs merge=lfs -text +blocks/Backdrops/castle2.sb3/951765ee7f7370f120c9df20b577c22f.png filter=lfs diff=lfs merge=lfs -text +blocks/Backdrops/hall.sb3/ea86ca30b346f27ca5faf1254f6a31e3.png filter=lfs diff=lfs merge=lfs -text +blocks/Backdrops/jungle.sb3/f4f908da19e2753f3ed679d7b37650ca.png filter=lfs diff=lfs merge=lfs -text +static/assets/backdrops/ef9973bcff6d4cbc558e946028ec7d23.png filter=lfs diff=lfs merge=lfs -text +uploads/documents/LLM_based_QA_chatbot_builder.pdf filter=lfs diff=lfs merge=lfs -text +utils/__pycache__/block_relation_builder.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text diff --git a/app_main[P-D].py b/app_main[P-D].py new file mode 100644 index 0000000000000000000000000000000000000000..2e18f14fab6db324783a4da0c73dc394fd200959 --- /dev/null +++ b/app_main[P-D].py @@ -0,0 +1,1111 @@ +from flask import Flask, request, jsonify, render_template, send_from_directory +import cv2, json,base64,io,os,tempfile,logging, re +import numpy as np +from unstructured.partition.pdf import partition_pdf +from PIL import Image +# from imutils.perspective import four_point_transform +from dotenv import load_dotenv +import pytesseract +# from transformers import AutoProcessor, AutoModelForImageTextToText, AutoModelForVision2Seq +# from langchain_community.document_loaders.image_captions import ImageCaptionLoader +from werkzeug.utils import secure_filename +from langchain_groq import ChatGroq +from langgraph.prebuilt import create_react_agent +from pdf2image import convert_from_path +from concurrent.futures import ThreadPoolExecutor +from pdf2image.exceptions import PDFInfoNotInstalledError +from typing import Dict, TypedDict, Optional, Any +from langgraph.graph import StateGraph, END +import uuid +import shutil, time +from langchain_experimental.open_clip.open_clip import OpenCLIPEmbeddings +# from matplotlib.offsetbox import OffsetImage, AnnotationBbox +from io import BytesIO +from pathlib import Path +import os + +# ============================== # +# INITIALIZE CLIP EMBEDDER # +# ============================== # +clip_embd = OpenCLIPEmbeddings() + +# Configure logging +logging.basicConfig( + level=logging.DEBUG, # Use INFO or ERROR in production + format="%(asctime)s [%(levelname)s] %(message)s", + handlers=[ + logging.FileHandler("app.log"), + logging.StreamHandler() + ] +) + +logger = logging.getLogger(__name__) + +load_dotenv() +# os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY") +groq_api_key = os.getenv("GROQ_API_KEY") + +llm = ChatGroq( + model="meta-llama/llama-4-scout-17b-16e-instruct", + temperature=0, + max_tokens=None, +) + +app = Flask(__name__) + +# ============================== # +# TESSERACT CONFIGURATION # +# ============================== # +# Set the Tesseract executable path +pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe" +# Set the TESSDATA_PREFIX environment variable to the directory containing the 'tessdata' folder +# This is crucial for Tesseract to find its language data files (e.g., eng.traineddata) +os.environ['TESSDATA_PREFIX'] = r'C:\Program Files\Tesseract-OCR' + +poppler_path = r"C:\poppler\Library\bin" +backdrop_images_path = r"blocks\Backdrops" +sprite_images_path = r"blocks\sprites" + +count = 0 +BASE_DIR = Path(__file__).parent +BLOCKS_DIR = BASE_DIR / "blocks" +STATIC_DIR = BASE_DIR / "static" +GEN_PROJECT_DIR = BASE_DIR / "generated_projects" +BACKDROP_DIR = BLOCKS_DIR / "Backdrops" +SPRITE_DIR = BLOCKS_DIR / "sprites" +OUTPUT_DIR = BASE_DIR / "OUTPUTS" +DETECTED_IMAGE_DIR = OUTPUT_DIR / "DETECTED_IMAGE" +IMAGE_DIR = OUTPUT_DIR / "SCANNED_IMAGE" +JSON_DIR = OUTPUT_DIR / "EXTRACTED_JSON" + +for d in (BLOCKS_DIR, STATIC_DIR, GEN_PROJECT_DIR, OUTPUT_DIR, BACKDROP_DIR, SPRITE_DIR, DETECTED_IMAGE_DIR, IMAGE_DIR, ): + d.mkdir(parents=True, exist_ok=True) + +def classify_image_type(description_or_name: str) -> str: + desc = description_or_name.lower() + + sprite_keywords = ["sprite", "character", "animal", "person", "creature", "robot", "figure"] + backdrop_keywords = ["background", "scene", "forest", "city", "room", "sky", "mountain", "village"] + code_block_keywords = [ + "move", "turn", "wait", "repeat", "if", "else", "broadcast", + "glide", "change", "forever", "when", "switch", "costume", + "say", "think", "stop", "clone", "touching", "sensing", + "scratch", "block", "code", "set", "variable" + ] + + if any(kw in desc for kw in code_block_keywords): + return "code-block" + elif any(kw in desc for kw in sprite_keywords): + return "sprite" + elif any(kw in desc for kw in backdrop_keywords): + return "backdrop" + else: + return "unknown" + +class GameState(TypedDict): + project_json: dict + description: str + project_id: str + project_image: str + action_plan: Optional[Dict] + pseudo_node: Optional[Dict] + temporary_node: Optional[Dict] + + +# Refined SYSTEM_PROMPT with more explicit Scratch JSON rules, especially for variables +SYSTEM_PROMPT = """ +You are an expert AI assistant named GameScratchAgent, specialized in generating and modifying Scratch-VM 3.x game project JSON. +Your core task is to process game descriptions and existing Scratch JSON structures, then produce or update JSON segments accurately. +You possess deep knowledge of Scratch 3.0 project schema, informed by comprehensive reference materials. When generating or modifying the `blocks` section, pay extremely close attention to the following: + +**Scratch Project JSON Schema Rules:** + +1. **Target Structure (`project.json`'s `targets` array):** + * Each object in the `targets` array represents a Stage or a Sprite. + * `isStage`: A boolean indicating if the target is the Stage (`true`) or a Sprite (`false`). + * `name`: The name of the Stage (e.g., `"Stage"`) or the Sprite (e.g., `"Cat"`). This property replaces `objName` found in older Scratch versions. + * `variables` dictionary: This dictionary maps unique variable IDs to arrays `[variable_name, initial_value, isCloudVariable?]`. + * `variable_name`: The user-defined name of the variable. + * `initial_value`: The variable's initial value, which can be a number or a string. + * `isCloudVariable?`: (Optional) A boolean indicating if it's a cloud variable (`true`) or a local variable (`false` or absent for regular variables). + * Example: `"myVarId123": ["score", 0]`, `"cloudVarId456": ["☁ High Score", "54", true]` + * `lists` dictionary: This dictionary maps unique list IDs to arrays `[list_name, [item1, item2, ...]]`. + * Example: `"myListId789": ["my list", ["apple", "banana"]]` + * `broadcasts` dictionary: This dictionary maps unique broadcast IDs to their names. + * Example: `"myBroadcastId": "Game Over"` + * `blocks` dictionary: This dictionary contains all the blocks belonging to this target. Keys are block IDs, values are block objects. + +2. **Block Structure (within a `target`'s `blocks` dictionary):** + * Every block object must have the following core properties: + * [cite_start]`opcode`: A unique internal identifier for the block's specific functionality (e.g., `"motion_movesteps"`, `"event_whenflagclicked"`)[cite: 31, 18, 439, 452]. + * `parent`: The ID of the block directly above it in the script stack (or `null` for a top-level block). + * `next`: The ID of the block directly below it in the script stack (or `null` for the end of a stack). + * `inputs`: An object defining values or blocks plugged into the block's input slots. Values are **arrays**. + * `fields`: An object defining dropdown menu selections or direct internal values within the block. Values are **arrays**. + * `shadow`: `true` if it's a shadow block (e.g., a default number input that can be replaced by another block), `false` otherwise. + * `topLevel`: `true` if it's a hat block or a standalone block (not connected to a parent), `false` otherwise. + +3. **`inputs` Property Details (for blocks plugged into input slots):** + * **Direct Block Connection (Reporter/Boolean block plugged in):** + * Format: `"": [1, ""]` + * Example: `"CONDITION": [1, "someBooleanBlockId"]` (e.g., for an `if` block). + * **Literal Value Input (Shadow block with a literal):** + * Format: `"": [1, [, ""]]` + * `type_code`: A numeric code representing the data type. Common codes include: `4` for number, `7` for string/text, `10` for string/message. + * `value_string`: The literal value as a string. + * Examples: + * Number: `"STEPS": [1, [4, "10"]]` (for `move 10 steps` block). + * String/Text: `"MESSAGE": [1, [7, "Hello"]]` (for `say Hello` block). + * String/Message (common for text inputs): `"MESSAGE": [1, [10, "Hello!"]]` (for `say Hello! for 2 secs`). + * **C-Block Substack (blocks within a loop or conditional):** + * Format: `"": [2, ""]` + * Common `SUBSTACK_NAME` values are `SUBSTACK` (for `if`, `forever`, `repeat`) and `SUBSTACK2` (for `else` in `if else`). + * Example: `"SUBSTACK": [2, "firstBlockInLoopId"]` + +4. **`fields` Property Details (for dropdowns or direct internal values):** + * Used for dropdown menus, variable names, list names, or other static selections directly within the block. + * Format: `"": ["", null]` + * Examples: + * Dropdown: `"KEY_OPTION": ["space", null]` (for `when space key pressed`). + * Variable Name: `"VARIABLE": ["score", null]` (for `set score to 0`). + * Direction (specific motion block): `"FORWARD_BACKWARD": ["forward", null]` (for `go forward layers`). + +5. **Unique IDs:** + * All block IDs, variable IDs, and list IDs must be unique strings (e.g., "myBlock123", "myVarId456", "myListId789"). Do NOT use placeholder strings like "block_id_here". + +6. **No Nested `blocks` Dictionary:** + * The `blocks` dictionary should only appear once per `target` (sprite/stage). Do NOT nest a `blocks` dictionary inside an individual block definition. Blocks that are part of a substack are linked via the `SUBSTACK` input. + +7. **Asset Properties (for Costumes/Sounds):** + * `assetId`, `md5ext`, `bitmapResolution`, `rotationCenterX`/`rotationCenterY` should be correctly associated with costume and sound objects within the `costumes` and `sounds` arrays. + +**General Principles and Important Considerations:** +* **Backward Compatibility:** Adhere strictly to existing Scratch 3.0 opcodes and schema to ensure backward compatibility with older projects. [cite_start]Opcodes must remain consistent to prevent previously saved projects from failing to load or behaving unexpectedly[cite: 18, 19, 25, 65]. +* **Forgiving Inputs:** Recognize that Scratch is designed to be "forgiving in its interpretation of inputs." [cite_start]The Scratch VM handles potentially "invalid" inputs gracefully (e.g., converting a number to a string if expected, returning default values like zero or empty strings, or performing no action) rather than crashing[cite: 20, 21, 22, 38, 39, 41]. This implies that precise type matching for inputs might be handled internally by Scratch, allowing for some flexibility in how values are provided, but the agent should aim for the most common and logical type. +""" + +SYSTEM_PROMPT_JSON_CORRECTOR =""" +You are an assistant that outputs JSON responses strictly following the given schema. +If the JSON you produce has any formatting errors, missing required fields, or invalid structure, you must identify the problems and correct them. +Always return only valid JSON that fully conforms to the schema below, enclosed in triple backticks (```), without any extra text or explanation. + +If you receive an invalid or incomplete JSON response, fix it by: +- Adding any missing required fields with appropriate values. +- Correcting syntax errors such as missing commas, brackets, or quotes. +- Ensuring the JSON structure matches the schema exactly. + +Remember: Your output must be valid JSON only, ready to be parsed without errors. +""" +# debugger and resolver agent for Scratch 3.0 +# Main agent of the system agent for Scratch 3.0 +agent = create_react_agent( + model=llm, + tools=[], # No specific tools are defined here, but could be added later + prompt=SYSTEM_PROMPT +) + +agent_json_resolver = create_react_agent( + model=llm, + tools=[], # No specific tools are defined here, but could be added later + prompt=SYSTEM_PROMPT_JSON_CORRECTOR +) + +# Helper function to load the block catalog from a JSON file +def _load_block_catalog(file_path: str) -> Dict: + """Loads the Scratch block catalog from a specified JSON file.""" + try: + with open(file_path, 'r') as f: + catalog = json.load(f) + logger.info(f"Successfully loaded block catalog from {file_path}") + return catalog + except FileNotFoundError: + logger.error(f"Error: Block catalog file not found at {file_path}") + # Return an empty dict or raise an error, depending on desired behavior + return {} + except json.JSONDecodeError as e: + logger.error(f"Error decoding JSON from {file_path}: {e}") + return {} + except Exception as e: + logger.error(f"An unexpected error occurred while loading {file_path}: {e}") + return {} + +# --- Global variable for the block catalog --- +# --- Global variable for the block catalog --- +ALL_SCRATCH_BLOCKS_CATALOG = {} +BLOCK_CATALOG_PATH = r"blocks\blocks.json" # Define the path to your JSON file +HAT_BLOCKS_PATH = r"blocks\hat_blocks.json" # Path to the hat blocks JSON file +STACK_BLOCKS_PATH = r"blocks\stack_blocks.json" # Path to the stack blocks JSON file +REPORTER_BLOCKS_PATH = r"blocks\reporter_blocks.json" # Path to the reporter blocks JSON file +BOOLEAN_BLOCKS_PATH = r"blocks\boolean_blocks.json" # Path to the boolean blocks JSON file +C_BLOCKS_PATH = r"blocks\c_blocks.json" # Path to the C blocks JSON file +CAP_BLOCKS_PATH = r"blocks\cap_blocks.json" # Path to the cap blocks JSON file + +# Load the block catalogs from their respective JSON files +hat_block_data = _load_block_catalog(HAT_BLOCKS_PATH) +hat_description = hat_block_data["description"] +hat_opcodes_functionalities = "\n".join([f" - Opcode: {block['op_code']}, functionality: {block['functionality']} example: standalone use: {block['example_standalone']}" for block in hat_block_data["blocks"]]) +print("Hat blocks loaded successfully.", hat_description) +boolean_block_data = _load_block_catalog(BOOLEAN_BLOCKS_PATH) +boolean_description = boolean_block_data["description"] +boolean_opcodes_functionalities = "\n".join([f" - Opcode: {block['op_code']}, functionality: {block['functionality']} example: standalone use: {block['example_standalone']}" for block in boolean_block_data["blocks"]]) + +c_block_data = _load_block_catalog(C_BLOCKS_PATH) +c_description = c_block_data["description"] +c_opcodes_functionalities = "\n".join([f" - Opcode: {block['op_code']}, functionality: {block['functionality']} example: standalone use: {block['example_standalone']}" for block in c_block_data["blocks"]]) + +cap_block_data = _load_block_catalog(CAP_BLOCKS_PATH) +cap_description = cap_block_data["description"] +cap_opcodes_functionalities = "\n".join([f" - Opcode: {block['op_code']}, functionality: {block['functionality']} example: standalone use: {block['example_standalone']}" for block in cap_block_data["blocks"]]) + +reporter_block_data = _load_block_catalog(REPORTER_BLOCKS_PATH) +reporter_description = reporter_block_data["description"] +reporter_opcodes_functionalities = "\n".join([f" - Opcode: {block['op_code']}, functionality: {block['functionality']} example: standalone use: {block['example_standalone']}" for block in reporter_block_data["blocks"]]) + +stack_block_data = _load_block_catalog(STACK_BLOCKS_PATH) +stack_description = stack_block_data["description"] +stack_opcodes_functionalities = "\n".join([f" - Opcode: {block['op_code']}, functionality: {block['functionality']} example: standalone use: {block['example_standalone']}" for block in stack_block_data["blocks"]]) + +# This makes ALL_SCRATCH_BLOCKS_CATALOG available globally +ALL_SCRATCH_BLOCKS_CATALOG = _load_block_catalog(BLOCK_CATALOG_PATH) + +# Helper function to extract JSON from LLM response +def extract_json_from_llm_response(raw_response: str) -> dict: + # --- 1) Pull out the JSON code‑block if present --- + md = re.search(r"```(?:json)?\s*([\s\S]*?)\s*```", raw_response) + json_string = md.group(1).strip() if md else raw_response + + # --- 2) Trim to the outermost { … } so we drop any prefix/suffix junk --- + first, last = json_string.find('{'), json_string.rfind('}') + if 0 <= first < last: + json_string = json_string[first:last+1] + + # --- 3) PRE‑CLEANUP: remove stray assistant{…}, rogue assistant keys, fix boolean quotes --- + json_string = re.sub(r'\b\w+\s*{', '{', json_string) + json_string = re.sub(r'"assistant"\s*:', '', json_string) + json_string = re.sub(r'\b(false|true)"', r'\1', json_string) + logger.debug("Ran pre‑cleanup for stray tokens and boolean quotes.") + + # --- 3.1) Fix stray inner quotes at start of name/list values --- + # e.g., { "name": " \"recent_scoress\"", ... } → "recent_scoress" + json_string = re.sub( + r'("name"\s*:\s*")\s*"', + r'\1', + json_string + ) + + # --- 4) Escape all embedded quotes in any `logic` value up to the next key --- + def _esc(m): + prefix, body = m.group(1), m.group(2) + return prefix + body.replace('"', r'\"') + json_string = re.sub( + r'("logic"\s*:\s*")([\s\S]+?)(?=",\s*"[A-Za-z_]\w*"\s*:\s*)', + _esc, + json_string + ) + logger.debug("Escaped embedded quotes in logic fields.") + + logger.debug("Quoted unquoted keys.") + + # --- 6) Remove trailing commas before } or ] --- + json_string = re.sub(r',\s*(?=[}\],])', '', json_string) + json_string = re.sub(r',\s*,', ',', json_string) + logger.debug("Removed trailing commas.") + + # --- 7) Balance braces: drop extra } at end if needed --- + ob, cb = json_string.count('{'), json_string.count('}') + if cb > ob: + excess = cb - ob + json_string = json_string.rstrip()[:-excess] + logger.debug(f"Stripped {excess} extra closing brace(s).") + + # --- 8) Escape literal newlines in *all* string values --- + json_string = re.sub( + r'"((?:[^"\\]|\\.)*?)"', + lambda m: '"' + m.group(1).replace('\n', '\\n').replace('\r', '\\r') + '"', + json_string, + flags=re.DOTALL + ) + logger.debug("Escaped newlines in strings.") + + # --- 9) Final parse attempt --- + try: + return json.loads(json_string) + except json.JSONDecodeError: + logger.error("Sanitized JSON still invalid:\n%s", json_string) + raise + +# Node 1: Logic updating if any issue here +def pseudo_generator_node(state: GameState): + logger.info("--- Running plan_logic_aligner_node ---") + image = state.get("image", "") + + project_json = state["project_json"] + + # MODIFICATION 1: Include 'Stage' in the list of names to plan for. + # It's crucial to ensure 'Stage' is always present for its global role. + target_names = [t["name"] for t in project_json["targets"]] + + refinement_prompt = f""" +You are an expert in Scratch 3.0 game development, specializing in understanding block relationships (stacked, nested). +"Analyze the Scratch code-block image and generate Pseudo-Code for what this logic appears to be doing." +From Image, you also have to detect a value of Key given in Text form "Script for: ". Below is the example +Example: "Script for: Bear", "Script for:" is a key and "Bear" is value and check if there is related target name available. + +**Targets in Game (Sprites and Stage) available in project_json:** {', '.join(target_names)} + +--- Scratch 3.0 Block Reference --- + ### Hat Blocks + Description: {hat_description} + Blocks: + {hat_opcodes_functionalities} + + ### Boolean Blocks + Description: {boolean_description} + Blocks: + {boolean_opcodes_functionalities} + + ### C Blocks + Description: {c_description} + Blocks: + {c_opcodes_functionalities} + + ### Cap Blocks + Description: {cap_description} + Blocks: + {cap_opcodes_functionalities} + + ### Reporter Blocks + Description: {reporter_description} + Blocks: + {reporter_opcodes_functionalities} + + ### Stack Blocks + Description: {stack_description} + Blocks: + {stack_opcodes_functionalities} +----------------------------------- + +Your task is to: +If you don't find any "Code-Blocks" then, + **Don't generate Pseudo Code, and pass the message "No Code-blocks" +If you find any "Code-Blocks" then, +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. + +2. **Structural requirements**: + - **Numeric values** `(e.g., 0, 5, 0.2, -130)` **must** be in parentheses: `(0)`, `(5)`, `(0.2)`, `(-130)`. + - **AlphaNumeric values** `(e.g., hello, say 5, 4, hi!)` **must** be in parentheses: `(hello)`, `(say 5)`, `(4)`, `(hi!)`. + - **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])`. + - **Dropdown options** must be in the form `[option v]` (e.g., `[Game Start v]`, `[blue sky v]`). example use `when [space v] key pressed`. + - **Reporter blocks** used as inputs must be double‑wrapped: `((x position))`, `((y position))`. example use `if <((y position)) = (-130)> then` or `(((x position)) * (1))`. + - **Boolean blocks** in conditions must be inside `< >`, including nested ones: `>`, `< and >`,`< or >`. + - **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]?>`. + - **Operator expressions** must use explicit Scratch operator blocks, e.g.: + ``` + (([ballSpeed v]) * (1.1)) + ``` + - **Every hat block script must end** with a final `end` on its own line. + +3. **Pseudo‑code formatting**: + - Represent each block or nested block on its own line. + - Indent nested blocks by 4 spaces under their parent (`forever`, `if`, etc.). + - No comments or explanatory text—just the block sequence. + - 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. + +4. **Logic content**: + - Build clear flow for mechanics (movement, jumping, flying, scoring, collisions). + - Match each action closely to a Scratch block or tight sequence. + - Do **NOT** include any justification or comments—only the raw logic. + +5. **Examples for reference**: +**Correct** pattern for a simple start script: + ``` + when green flag clicked + switch backdrop to [blue sky v] + set [score v] to (0) + show variable [score v] + broadcast [Game Start v] + end + ``` +**Correct** pattern for updating the high score variable handling: + ``` + when I receive [Game Over v] + if <((score)) > (([High Score v]))> then + set [High Score v] to ([score v]) + end + switch backdrop to [Game Over v] + end + ``` +**Correct** pattern for level up and increase difficulty use: + ``` + when I receive [Level Up v] + change [level v] by (1) + set [ballSpeed v] to ((([ballSpeed v]) * (1.1))) + end + ``` +**Correct** pattern for jumping mechanics use: + ``` + when [space v] key pressed + if <((y position)) = (-100)> then + repeat (5) + change y by (100) + wait (0.1) seconds + change y by (-100) + wait (0.1) seconds + end + end + end + ``` +**Correct** pattern for continuos moving objects use: + ``` + when green flag clicked + go to x: (240) y: (-100) + set [speed v] to (-5) + show variable [speed v] + forever + change x by ([speed v]) + if <((x position)) < (-240)> then + go to x: (240) y: (-100) + end + end + end + ``` +**Correct** pattern for continuos moving objects use: + ``` + when green flag clicked + go to x: (240) y: (-100) + set [speed v] to (-5) + show variable [speed v] + forever + change x by ([speed v]) + if <((x position)) < (-240)> then + go to x: (240) y: (-100) + end + end + end + ``` +6. **Donot** add any explaination of logic or comments to justify or explain just put the logic content in the json. +7. **Output**: +Return **only** a JSON object, using double quotes everywhere, where the key for the pseudo-code will be the target name closest to the "Script for:" value. If no code-blocks are found, return `{"refined_logic": "No Code-blocks"}`. + +```json +{{ +"refined_logic":{{ + "[Target name similar to script for]": {{ + "plan":[ + {{"pseudocode":"…your fully‑formatted pseudo‑code here…"}}, + {{"pseudocode":"…your fully‑formatted pseudo‑code here…"}} + ] + }} + }} +}} +``` + """ + image_input = { + "type": "image_url", + "image_url": { + "url": f"data:image/png;base64,{image}" + } + } + + content = [ + {"type": "text", "text": refinement_prompt}, + image_input + ] + + try: + # Invoke the main agent for logic refinement and relationship identification + response = agent.invoke({"messages": [{"role": "user", "content": content}]}) + llm_output_raw = response["messages"][-1].content.strip() + parsed_llm_output = extract_json_from_llm_response(llm_output_raw) + result = parsed_llm_output + print(f"result:\n\n {result}") + + except json.JSONDecodeError as error_json: + # If JSON parsing fails, use the json resolver agent + correction_prompt = ( + "Your task is to correct the provided JSON string to ensure it is **syntactically perfect and adheres strictly to JSON rules**.\n" + "It must be a JSON object with `refined_logic` (string) and `block_relationships` (array of objects).\n" + f"- **Error Details**: {error_json}\n\n" + "**Strict Instructions for your response:**\n" + "1. **ONLY** output the corrected JSON. Do not include any other text or explanations.\n" + "2. Ensure all keys and string values are enclosed in **double quotes**. Escape internal quotes (`\\`).\n" + "3. No trailing commas. Correct nesting.\n\n" + "Here is the problematic JSON string to correct:\n" + f"```json\n{llm_output_raw}\n```\n" + "Corrected JSON:\n" + ) + try: + correction_response = agent_json_resolver.invoke({"messages": [{"role": "user", "content": correction_prompt}]}) + corrected_output = extract_json_from_llm_response(correction_response["messages"][-1].content) + #block_relationships = corrected_output.get("block_relationships", []) + result = corrected_output + except Exception as e_corr: + logger.error(f"Failed to correct JSON output for even after retry: {e_corr}") + + + # Update the original action_plan in the state with the refined version + state["pseudo_code"] = result + print(f"[OVREALL REFINED PSEUDO CODE LOGIC]: {result}") + logger.info("Plan refinement and block relation analysis completed for all plans.") + return state + +scratch_keywords = [ + "move", "turn", "wait", "repeat", "if", "else", "broadcast", + "glide", "change", "forever", "when", "switch", + "next costume", "set", "show", "hide", "play sound", + "go to", "x position", "y position", "think", "say", + "variable", "stop", "clone", + "touching", "sensing", "pen", "clear","Scratch","Code","scratch blocks" + ] + +# --- FUNCTION: Extract images from saved PDF --- +def extract_images_from_pdf(pdf_path, final_json_path_2): + ''' Extract images from PDF and generate structured sprite JSON ''' + try: + pdf_filename = os.path.splitext(os.path.basename(pdf_path))[0] # e.g., "scratch_crab" + pdf_dir_path = os.path.dirname(pdf_path).replace("/", "\\") + + # Create subfolders + extracted_image_subdir = os.path.join( + DETECTED_IMAGE_DIR, pdf_filename) + json_subdir = os.path.join(JSON_DIR, pdf_filename) + os.makedirs(extracted_image_subdir, exist_ok=True) + os.makedirs(json_subdir, exist_ok=True) + + # Output paths + output_json_path = os.path.join(json_subdir, "extracted.json") + final_json_path = os.path.join(json_subdir, "extracted_sprites.json") + final_json_path_2 = os.path.join(json_subdir, "extracted_sprites_2.json") + + try: + elements = partition_pdf( + filename=pdf_path, + strategy="hi_res", + extract_image_block_types=["Image"], + extract_image_block_to_payload=True, # Set to True to get base64 in output + ) + except Exception as e: + raise RuntimeError( + f"❌ Failed to extract images from PDF: {str(e)}") + + try: + with open(output_json_path, "w") as f: + json.dump([element.to_dict() + for element in elements], f, indent=4) + except Exception as e: + raise RuntimeError(f"❌ Failed to write extracted.json: {str(e)}") + + try: + # Display extracted images + with open(output_json_path, 'r') as file: + file_elements = json.load(file) + except Exception as e: + raise RuntimeError(f"❌ Failed to read extracted.json: {str(e)}") + + # Prepare manipulated sprite JSON structure + manipulated_json = {} + + # SET A SYSTEM PROMPT + system_prompt = """ + You are an expert in visual scene understanding. + Your Job is to analyze an image and respond acoording if asked for name give simple name by analyzing it and if ask for descrption generate a short description covering its elements. + + Guidelines: + - Focus only the images given in Square Shape. + - Don't Consider Blank areas in Image as. + - Don't include generic summary or explanation outside the fields. + Return only string. + """ + agent = create_react_agent( + model=llm, + tools=[], + prompt=system_prompt + ) + + # If JSON already exists, load it and find the next available Sprite number + if os.path.exists(final_json_path): + with open(final_json_path, "r") as existing_file: + manipulated = json.load(existing_file) + # Determine the next available index (e.g., Sprite 4 if 1–3 already exist) + existing_keys = [int(k.replace("Sprite ", "")) + for k in manipulated.keys()] + start_count = max(existing_keys, default=0) + 1 + else: + start_count = 1 + + sprite_count = start_count + for i, element in enumerate(file_elements): + if "image_base64" in element["metadata"]: + try: + image_data = base64.b64decode( + element["metadata"]["image_base64"]) + image = Image.open(io.BytesIO(image_data)).convert("RGB") + + image = upscale_image(image, scale=2) + # image.show(title=f"Extracted Image {i+1}") + image_path = os.path.join(extracted_image_subdir, f"Sprite_{i+1}.png") + image.save(image_path) # don't need to store image in local folder, process it from variable + + with open(image_path, "rb") as image_file: + image_bytes = image_file.read() + img_base64 = base64.b64encode(image_bytes).decode("utf-8") + + prompt_combined = """ + Analyze this image and return JSON with keys:# modify prompt for "name", if it detects "code-blocks only then give name as 'scratch-block'" + { + "name": "" , + "description": "" + } + Guidelines: + - If image contains logical/code blocks from Scratch (e.g., move, turn, repeat, when clicked, etc.), use 'scratch-block' as the name. + - If image is a character, object, or backdrop, give an appropriate descriptive name instead. + - Avoid generic names like 'image1' or 'picture'. + - Keep the response strictly in JSON format. + """ + + content = [ + {"type": "text", "text": prompt_combined}, + {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{img_base64}"}} + ] + + response = agent.invoke({"messages": [{"role": "user", "content": content}]}) + result_json = json.loads(response["messages"][-1].content) + try: + name = result_json.get("name", "").strip() + description = result_json.get("description", "").strip() + except Exception as e: + logger.error(f"⚠️ Failed to extract name/description: {str(e)}") + name = "unknown" + description = "unknown" + + manipulated_json[f"Sprite {sprite_count}"] = { + "name": name, + "base64": element["metadata"]["image_base64"], + "file-path": pdf_dir_path, + "description": description + } + sprite_count += 1 + except Exception as e: + print(f"⚠️ Error processing Sprite {i+1}: {str(e)}") + + # Save manipulated JSON + with open(final_json_path, "w") as sprite_file: + json.dump(manipulated_json, sprite_file, indent=4) + + def is_code_block(name: str) -> bool: + for kw in scratch_keywords: + if kw.lower() in name.lower(): + return True + return False + + # Filter out code block images + filtered_sprites = {} + for key, value in manipulated_json.items(): + sprite_name = value.get("name", "") + if not is_code_block(sprite_name): + filtered_sprites[key] = value + else: + logger.info(f"🛑 Excluded code block-like image: {key}") + + # if not any(is_code_block(value.get("name","")) for value in manipulated_json.values()): + # return jsonify({"message":"Invalid Content"}), 400 + # if not filtered_sprites: + # return "Invalid Content", {} + + # Overwrite with filtered content + with open(final_json_path_2, "w") as sprite_file: + json.dump(filtered_sprites, sprite_file, indent=4) + # print(f"✅ Manipulated sprite JSON saved: {final_json_path}") + + return final_json_path, manipulated_json + except Exception as e: + raise RuntimeError(f"❌ Error in extract_images_from_pdf: {str(e)}") + +def similarity_matching(input_json_path: str, project_folder:str) -> str: + + logger.info("🔍 Running similarity matching...") + + # ============================== # + # DEFINE PATHS # + # ============================== # + + image_dirs = [backdrop_images_path, sprite_images_path] + + project_json_path = os.path.join(project_folder, "project.json") + + # ============================== # + # READ SPRITE METADATA # + # ============================== # + with open(input_json_path, 'r') as f: + sprites_data = json.load(f) + + sprite_ids, texts, sprite_base64 = [], [], [] + for sid, sprite in sprites_data.items(): + sprite_ids.append(sid) + texts.append( + "This is " + sprite.get("description", sprite.get("name", ""))) + sprite_base64.append(sprite["base64"]) + + # ========================================= # + # Walk folders to collect all image paths # + # ========================================= # + folder_image_paths = [ + #backdrops + BACKDROP_DIR / "badroom3.sb3" / "8cc0b88d53345b3e337e8f028a32a4e7.png", + BACKDROP_DIR / "baseball2.sb3" / "7be1f5b3e682813dac1f297e52ff7dca.png", + BACKDROP_DIR / "beach_malibu.sb3" / "050615fe992a00d6af0e664e497ebf53.png", + BACKDROP_DIR / "castle2.sb3" / "951765ee7f7370f120c9df20b577c22f.png", + BACKDROP_DIR / "hall.sb3" / "ea86ca30b346f27ca5faf1254f6a31e3.png", + BACKDROP_DIR / "jungle.sb3" / "f4f908da19e2753f3ed679d7b37650ca.png", + #sprite + SPRITE_DIR / "Batter.sprite3" / "baseball_sprite_motion_1.png", + SPRITE_DIR / "Bear.sprite3" / "bear_motion_2.png", + SPRITE_DIR / "Beetle.sprite3" / "46d0dfd4ae7e9bfe3a6a2e35a4905eae.png", + SPRITE_DIR / "cat" / "cat_motion_1.png", + SPRITE_DIR / "Centaur.sprite3" / "2373556e776cad3ba4d6ee04fc34550b.png", + SPRITE_DIR / "Crab.sprite3" / "bear_element.png", + SPRITE_DIR / "Soccer Ball.sprite3" / "cat_football.png", + ] + # ============================== # + # DECODE SPRITE IMAGES # + # ============================== # + # temp_dir = tempfile.mkdtemp() + # sprite_image_paths = [] + # for idx, b64 in enumerate(sprite_base64): + # image_data = base64.b64decode(b64.split(",")[-1]) + # img = Image.open(BytesIO(image_data)).convert("RGB") + # temp_path = os.path.join(temp_dir, f"sprite_{idx}.png") + # img.save(temp_path) + # sprite_image_paths.append(temp_path) + # print(f"\n\n\nSPRITE IMAGE PATHS: \n{sprite_image_paths}") + + # ============================== # + # EMBED SPRITE IMAGES # + # ============================== # + sprite_features = clip_embd.embed_image(sprite_base64) + + # ============================== # + # COMPUTE SIMILARITIES # + # ============================== # + with open(f"{BLOCKS_DIR}/embeddings.json", "r") as f: + embedding_json = json.load(f) + # print(f"\n\n EMBEDDING JSON: {embedding_json}") + + img_matrix = np.array([img["embeddings"] for img in embedding_json]) + sprite_matrix = np.array(sprite_features) + + similarity = np.matmul(sprite_matrix, img_matrix.T) + + most_similar_indices = np.argmax(similarity, axis=1) + print(f"") + # ============= Match and copy =============== + project_data = [] + copied_folders = set() + + # =============================================================== # + # Loop through most similar images from Sprites folder # + # → Copy sprite assets (excluding matched image + sprite.json) # + # → Load sprite.json and append its data to project_data # + # =============================================================== # + for sprite_idx, matched_idx in enumerate(most_similar_indices): + matched_image_path = folder_image_paths[matched_idx] + matched_image_path = os.path.normpath(matched_image_path) + + matched_folder = os.path.dirname(matched_image_path) + #folder_name = os.path.basename(matched_folder) + + if matched_folder in copied_folders: + continue + copied_folders.add(matched_folder) + logger.info(f"Matched image path: {matched_image_path}") + + sprite_json_path = os.path.join(matched_folder, 'sprite.json') + if not os.path.exists(sprite_json_path): + logger.warning(f"sprite.json not found in: {matched_folder}") + continue + + with open(sprite_json_path, 'r') as f: + sprite_data = json.load(f) + # print(f"SPRITE DATA: \n{sprite_data}") + # Copy only non-matched files + for fname in os.listdir(matched_folder): + fpath = os.path.join(matched_folder, fname) + if os.path.isfile(fpath) and fname not in {os.path.basename(matched_image_path), 'sprite.json'}: + shutil.copy2(fpath, os.path.join(project_folder, fname)) + # logger.info(f"Copied Sprite asset: {fname}") + project_data.append(sprite_data) + + # ================================================================== # + # Loop through most similar images from Backdrops folder # + # → Copy Backdrop assets (excluding matched image + project.json) # + # → Load project.json and append its data to project_data # + # ================================================================== # + backdrop_data = [] # for backdrop-related entries + + for backdrop_idx, matched_idx in enumerate(most_similar_indices): + matched_image_path = os.path.normpath(folder_image_paths[matched_idx]) + + # Check if the match is from the Backdrops folder + if matched_image_path.startswith(os.path.normpath(backdrop_images_path)): + matched_folder = os.path.dirname(matched_image_path) + folder_name = os.path.basename(matched_folder) + + logger.info(f"Backdrop matched image: {matched_image_path}") + + # Copy only non-matched files + for fname in os.listdir(matched_folder): + fpath = os.path.join(matched_folder, fname) + if os.path.isfile(fpath) and fname not in {os.path.basename(matched_image_path), 'project.json'}: + shutil.copy2(fpath, os.path.join(project_folder, fname)) + # logger.info(f"Copied Backdrop asset: {fname}") + + # Append backdrop's project.json + backdrop_json_path = os.path.join(matched_folder, 'project.json') + if os.path.exists(backdrop_json_path): + with open(backdrop_json_path, 'r') as f: + backdrop_json_data = json.load(f) + # print(f"SPRITE DATA: \n{backdrop_json_data}") + if "targets" in backdrop_json_data: + for target in backdrop_json_data["targets"]: + if target.get("isStage") == True: + backdrop_data.append(target) + else: + logger.warning(f"project.json not found in: {matched_folder}") + + # Merge JSON structure + final_project = { + "targets": [], + "monitors": [], + "extensions": [], + "meta": { + "semver": "3.0.0", + "vm": "11.3.0", + "agent": "OpenAI ScratchVision Agent" + } + } + + for sprite in project_data: + if not sprite.get("isStage", False): + final_project["targets"].append(sprite) + + if backdrop_data: + all_costumes, sounds = [], [] + for idx, bd in enumerate(backdrop_data): + all_costumes.extend(bd.get("costumes", [])) + if idx == 0 and "sounds" in bd: + sounds = bd["sounds"] + final_project["targets"].append({ + "isStage": True, + "name": "Stage", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 1 if len(all_costumes) > 1 else 0, + "costumes": all_costumes, + "sounds": sounds, + "volume": 100, + "layerOrder": 0, + "tempo": 60, + "videoTransparency": 50, + "videoState": "on", + "textToSpeechLanguage": None + }) + + with open(project_json_path, 'w') as f: + json.dump(final_project, f, indent=2) + + # logger.info(f"🎉 Final project saved: {project_json_path}") + return project_json_path + +def delay_for_tpm_node(state: GameState): + logger.info("--- Running DelayForTPMNode ---") + time.sleep(10) # Adjust the delay as needed + logger.info("Delay completed.") + return state + +# Build the LangGraph workflow +workflow = StateGraph(GameState) + +# Add all nodes to the workflow +workflow.add_node("time_delay_1", delay_for_tpm_node) +workflow.add_node("opcode_counter", pseudo_generator_node) +workflow.set_entry_point("time_delay_1") +workflow.add_edge("time_delay_1","opcode_counter") +workflow.add_edge("opcode_counter", END) +app_graph = workflow.compile() + +# ============== Helper function to Upscale an Image ============== # +def upscale_image(image: Image.Image, scale: int = 2) -> Image.Image: + """ + Upscales a PIL image by a given scale factor. + """ + try: + width, height = image.size + new_size = (width * scale, height * scale) + upscaled_image = image.resize(new_size, Image.LANCZOS) + logger.info(f"✅ Upscaled image to {new_size}") + return upscaled_image + except Exception as e: + logger.error(f"❌ Error during image upscaling: {str(e)}") + return image + +def create_sb3_archive(project_folder, project_id): + """ + Zips the project folder and renames it to an .sb3 file. + + Args: + project_folder (str): The path to the directory containing the project.json and assets. + project_id (str): The unique ID for the project, used for naming the .sb3 file. + + Returns: + str: The path to the created .sb3 file, or None if an error occurred. + """ + output_filename = os.path.join("generated_projects", project_id) + zip_path = None + sb3_path = None + try: + zip_path = shutil.make_archive(output_filename, 'zip', root_dir=project_folder) + logger.info(f"Project folder zipped to: {zip_path}") + + # 2. Rename the .zip file to .sb3 + sb3_path = f"{output_filename}.sb3" + os.rename(zip_path, sb3_path) + logger.info(f"Renamed {zip_path} to {sb3_path}") + + return sb3_path + except Exception as e: + logger.error(f"Error creating SB3 archive for {project_id}: {e}") + # Clean up any partial files if an error occurs + if zip_path and os.path.exists(zip_path): + os.remove(zip_path) + if sb3_path and os.path.exists(sb3_path): + os.remove(sb3_path) + return None + + +@app.route('/') +def index(): + return render_template('app_index.html') + +@app.route("/download_sb3/", methods=["GET"]) +def download_sb3(project_id): + """ + Allows users to download the generated .sb3 Scratch project file. + """ + sb3_filename = f"{project_id}.sb3" + sb3_filepath = os.path.join("generated_projects", sb3_filename) + + try: + if os.path.exists(sb3_filepath): + logger.info(f"Serving SB3 file for project ID: {project_id}") + # send_from_directory serves the file and handles content-disposition for download + return send_from_directory( + directory="generated_projects", + path=sb3_filename, + as_attachment=True, # This makes the browser download the file + download_name=sb3_filename # This sets the filename for the download + ) + else: + logger.warning(f"SB3 file not found for ID: {project_id}") + return jsonify({"error": "Scratch project file not found"}), 404 + except Exception as e: + logger.error(f"Error serving SB3 file for ID {project_id}: {e}") + return jsonify({"error": "Failed to retrieve Scratch project file"}), 500 + +# API endpoint +@app.route('/process_pdf', methods=['POST']) +def process_pdf(): + try: + logger.info("Received request to process PDF.") + if 'pdf_file' not in request.files: + logger.warning("No PDF file found in request.") + return jsonify({"error": "Missing PDF file in form-data with key 'pdf_file'"}), 400 + + pdf_file = request.files['pdf_file'] + if pdf_file.filename == '': + return jsonify({"error": "Empty filename"}), 400 + + # ================================================= # + # Generate Random UUID for project folder name # + # ================================================= # + project_id = str(uuid.uuid4()).replace('-', '') + project_folder = os.path.join("outputs", f"{project_id}") + + # =========================================================================== # + # Create empty json in project_{random_id} folder # + # =========================================================================== # + os.makedirs(project_folder, exist_ok=True) + + # Save the uploaded PDF temporarily + filename = secure_filename(pdf_file.filename) + temp_dir = tempfile.mkdtemp() + saved_pdf_path = os.path.join(temp_dir, filename) + pdf_file.save(saved_pdf_path) + + # logger.info(f"Created project folder: {project_folder}") + logger.info(f"Saved uploaded PDF to: {saved_pdf_path}") + + # Extract & process + json_path = None + output_path, result = extract_images_from_pdf(saved_pdf_path, json_path) + + # Check extracted_sprites.json for "scratch block" in any 'name' + extracted_dir = os.path.join(JSON_DIR, os.path.splitext(filename)[0]) + extracted_sprites_json = os.path.join(extracted_dir, "extracted_sprites.json") + + if not os.path.exists(extracted_sprites_json): + return jsonify({"error": "No extracted_sprites.json found"}), 500 + + with open(extracted_sprites_json, 'r') as f: + sprite_data = json.load(f) + + project_output = similarity_matching(output_path, project_folder) + logger.info("Received request to process PDF.") + + with open(project_output, 'r') as f: + project_skeleton = json.load(f) + + + images = convert_from_path(saved_pdf_path, dpi=300, poppler_path=poppler_path) + img_base64 = base64.b64encode(images).decode("utf-8") + #image_paths = await convert_pdf_to_images_async(saved_pdf_path) + + #updating logic here [Dev Patel] + initial_state_dict = { + "project_json": project_skeleton, + "description": "The pseudo code for the script", + "project_id": project_id, + "project_image": img_base64, + "action_plan": {}, + "pseudo_code": {}, + "temporary_node": {}, + } + + final_state_dict = app_graph.invoke(initial_state_dict) # Pass dictionary + + final_project_json = final_state_dict['project_json'] # Access as dict + + # Save the *final* filled project JSON, overwriting the skeleton + with open(project_output, "w") as f: + json.dump(final_project_json, f, indent=2) + logger.info(f"Final project JSON saved to {project_output}") + + # --- Call the new function to create the .sb3 file --- + sb3_file_path = create_sb3_archive(project_folder, project_id) + if sb3_file_path: + logger.info(f"Successfully created SB3 file: {sb3_file_path}") + # Instead of returning the local path, return a URL to the download endpoint + download_url = f"/download_sb3/{project_id}" + return jsonify({"message": "Procesed PDF and Game sb3 generated successfully", "project_id": project_id, "download_url": download_url}) + else: + return jsonify(error="Failed to create SB3 archive"), 500 + + except Exception as e: + logger.error(f"Error during processing the pdf workflow for project ID {project_id}: {e}", exc_info=True) + return jsonify({"error": f"❌ Failed to process PDF: {str(e)}"}), 500 + +if __name__ == '__main__': + os.makedirs("outputs", exist_ok=True) + app.run(host='0.0.0.0', port=7860, debug=True) \ No newline at end of file diff --git a/blocks/Backdrops/badroom3.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav b/blocks/Backdrops/badroom3.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/blocks/Backdrops/badroom3.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/blocks/Backdrops/badroom3.sb3/8cc0b88d53345b3e337e8f028a32a4e7.png b/blocks/Backdrops/badroom3.sb3/8cc0b88d53345b3e337e8f028a32a4e7.png new file mode 100644 index 0000000000000000000000000000000000000000..12002b086d858040aafe0cfa174f7bfb7bd529b5 Binary files /dev/null and b/blocks/Backdrops/badroom3.sb3/8cc0b88d53345b3e337e8f028a32a4e7.png differ diff --git a/blocks/Backdrops/badroom3.sb3/cd21514d0531fdffb22204e0ec5ed84a.svg b/blocks/Backdrops/badroom3.sb3/cd21514d0531fdffb22204e0ec5ed84a.svg new file mode 100644 index 0000000000000000000000000000000000000000..15f73119b9c3271c6c411fc4233090e37f42ec37 --- /dev/null +++ b/blocks/Backdrops/badroom3.sb3/cd21514d0531fdffb22204e0ec5ed84a.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/blocks/Backdrops/badroom3.sb3/project.json b/blocks/Backdrops/badroom3.sb3/project.json new file mode 100644 index 0000000000000000000000000000000000000000..0220f492a259d216547e52b4a53c644bc5cb07ba --- /dev/null +++ b/blocks/Backdrops/badroom3.sb3/project.json @@ -0,0 +1 @@ +{"targets":[{"isStage":true,"name":"Stage","variables":{"`jEk@4|i[#Fk?(8x)AV.-my variable":["my variable",0]},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":1,"costumes":[{"name":"backdrop1","dataFormat":"svg","assetId":"cd21514d0531fdffb22204e0ec5ed84a","md5ext":"cd21514d0531fdffb22204e0ec5ed84a.svg","rotationCenterX":240,"rotationCenterY":180},{"name":"Bedroom 3","bitmapResolution":2,"dataFormat":"png","assetId":"8cc0b88d53345b3e337e8f028a32a4e7","rotationCenterX":480,"rotationCenterY":360}],"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":null}],"monitors":[],"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 Edg/138.0.0.0"}} \ No newline at end of file diff --git a/blocks/Backdrops/baseball2.sb3/7be1f5b3e682813dac1f297e52ff7dca.png b/blocks/Backdrops/baseball2.sb3/7be1f5b3e682813dac1f297e52ff7dca.png new file mode 100644 index 0000000000000000000000000000000000000000..fb5f2e960b5d97d51daf29eb72c2d185fc3c0f6a Binary files /dev/null and b/blocks/Backdrops/baseball2.sb3/7be1f5b3e682813dac1f297e52ff7dca.png differ diff --git a/blocks/Backdrops/baseball2.sb3/7be1f5b3e682813dac1f297e52ff7dca.svg b/blocks/Backdrops/baseball2.sb3/7be1f5b3e682813dac1f297e52ff7dca.svg new file mode 100644 index 0000000000000000000000000000000000000000..bcd3245515937eea52f3d6626e8c784b506cd081 --- /dev/null +++ b/blocks/Backdrops/baseball2.sb3/7be1f5b3e682813dac1f297e52ff7dca.svg @@ -0,0 +1,156 @@ + + + + + baseball-b + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/Backdrops/baseball2.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav b/blocks/Backdrops/baseball2.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/blocks/Backdrops/baseball2.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/blocks/Backdrops/baseball2.sb3/cd21514d0531fdffb22204e0ec5ed84a.svg b/blocks/Backdrops/baseball2.sb3/cd21514d0531fdffb22204e0ec5ed84a.svg new file mode 100644 index 0000000000000000000000000000000000000000..15f73119b9c3271c6c411fc4233090e37f42ec37 --- /dev/null +++ b/blocks/Backdrops/baseball2.sb3/cd21514d0531fdffb22204e0ec5ed84a.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/blocks/Backdrops/baseball2.sb3/project.json b/blocks/Backdrops/baseball2.sb3/project.json new file mode 100644 index 0000000000000000000000000000000000000000..f6afbdc32f773a124d2a4bc234035ff399687d29 --- /dev/null +++ b/blocks/Backdrops/baseball2.sb3/project.json @@ -0,0 +1,56 @@ +{ + "targets": [ + { + "isStage": true, + "name": "Stage", + "variables": { "`jEk@4|i[#Fk?(8x)AV.-my variable": ["my variable", 0] }, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 1, + "costumes": [ + { + "name": "backdrop1", + "dataFormat": "svg", + "assetId": "cd21514d0531fdffb22204e0ec5ed84a", + "md5ext": "cd21514d0531fdffb22204e0ec5ed84a.svg", + "rotationCenterX": 240, + "rotationCenterY": 180 + }, + { + "name": "Baseball 2", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "7be1f5b3e682813dac1f297e52ff7dca", + "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": null + } + ], + "monitors": [], + "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 Edg/138.0.0.0" + } +} diff --git a/blocks/Backdrops/beach_malibu.sb3/050615fe992a00d6af0e664e497ebf53.png b/blocks/Backdrops/beach_malibu.sb3/050615fe992a00d6af0e664e497ebf53.png new file mode 100644 index 0000000000000000000000000000000000000000..73707e53770a5addeef72d6a1fdc3a55cabef774 --- /dev/null +++ b/blocks/Backdrops/beach_malibu.sb3/050615fe992a00d6af0e664e497ebf53.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de6124f0c43213e5245d67d409b84fafd257534be0155fd614a1fdec07b7ff06 +size 337288 diff --git a/blocks/Backdrops/beach_malibu.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav b/blocks/Backdrops/beach_malibu.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/blocks/Backdrops/beach_malibu.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/blocks/Backdrops/beach_malibu.sb3/project.json b/blocks/Backdrops/beach_malibu.sb3/project.json new file mode 100644 index 0000000000000000000000000000000000000000..8899c9945e3e844373915a0d7bf3cd9a030cdad2 --- /dev/null +++ b/blocks/Backdrops/beach_malibu.sb3/project.json @@ -0,0 +1 @@ +{"targets":[{"isStage":true,"name":"Stage","variables":{"`jEk@4|i[#Fk?(8x)AV.-my variable":["my variable",0]},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"name":"Beach Malibu","bitmapResolution":2,"dataFormat":"png","assetId":"050615fe992a00d6af0e664e497ebf53","rotationCenterX":480,"rotationCenterY":360}],"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":null}],"monitors":[],"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 Edg/138.0.0.0"}} \ No newline at end of file diff --git a/blocks/Backdrops/castle2.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav b/blocks/Backdrops/castle2.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/blocks/Backdrops/castle2.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/blocks/Backdrops/castle2.sb3/951765ee7f7370f120c9df20b577c22f.png b/blocks/Backdrops/castle2.sb3/951765ee7f7370f120c9df20b577c22f.png new file mode 100644 index 0000000000000000000000000000000000000000..93450ea725ce9a38f98e02dcc81b7365a3d11a2d --- /dev/null +++ b/blocks/Backdrops/castle2.sb3/951765ee7f7370f120c9df20b577c22f.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:627b85a32b739d0ee852398d0f449b4825af54c0ece91a64e0bec1a7fec56596 +size 447975 diff --git a/blocks/Backdrops/castle2.sb3/project.json b/blocks/Backdrops/castle2.sb3/project.json new file mode 100644 index 0000000000000000000000000000000000000000..dfd7a084960f4f78a179e1dfe3cd13f1de759a74 --- /dev/null +++ b/blocks/Backdrops/castle2.sb3/project.json @@ -0,0 +1 @@ +{"targets":[{"isStage":true,"name":"Stage","variables":{"`jEk@4|i[#Fk?(8x)AV.-my variable":["my variable",0]},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"name":"Castle 2","bitmapResolution":2,"dataFormat":"png","assetId":"951765ee7f7370f120c9df20b577c22f","rotationCenterX":480,"rotationCenterY":360}],"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":null}],"monitors":[],"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 Edg/138.0.0.0"}} \ No newline at end of file diff --git a/blocks/Backdrops/hall.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav b/blocks/Backdrops/hall.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/blocks/Backdrops/hall.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/blocks/Backdrops/hall.sb3/ea86ca30b346f27ca5faf1254f6a31e3.png b/blocks/Backdrops/hall.sb3/ea86ca30b346f27ca5faf1254f6a31e3.png new file mode 100644 index 0000000000000000000000000000000000000000..53bef3297418124210f83c95a9cbd114e04ca8d7 --- /dev/null +++ b/blocks/Backdrops/hall.sb3/ea86ca30b346f27ca5faf1254f6a31e3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7af7d95cc8200c7c121b6e2fcbb2de07585c57147a97a358ef18d600fc4c8990 +size 258241 diff --git a/blocks/Backdrops/hall.sb3/project.json b/blocks/Backdrops/hall.sb3/project.json new file mode 100644 index 0000000000000000000000000000000000000000..291b0b2d390a67685ee1b7a7bafd51a0ab169eb7 --- /dev/null +++ b/blocks/Backdrops/hall.sb3/project.json @@ -0,0 +1 @@ +{"targets":[{"isStage":true,"name":"Stage","variables":{"`jEk@4|i[#Fk?(8x)AV.-my variable":["my variable",0]},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"name":"Hall","bitmapResolution":2,"dataFormat":"png","assetId":"ea86ca30b346f27ca5faf1254f6a31e3","rotationCenterX":480,"rotationCenterY":360}],"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":null}],"monitors":[],"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 Edg/138.0.0.0"}} \ No newline at end of file diff --git a/blocks/Backdrops/jungle.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav b/blocks/Backdrops/jungle.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/blocks/Backdrops/jungle.sb3/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/blocks/Backdrops/jungle.sb3/f4f908da19e2753f3ed679d7b37650ca.png b/blocks/Backdrops/jungle.sb3/f4f908da19e2753f3ed679d7b37650ca.png new file mode 100644 index 0000000000000000000000000000000000000000..02329c63fecc4be5bf5aedd3e0281f982556d0e7 --- /dev/null +++ b/blocks/Backdrops/jungle.sb3/f4f908da19e2753f3ed679d7b37650ca.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6abbee08bcbf2ca479150cd355a1fc1d8d7bd89624bead1c515200438428a3d5 +size 322015 diff --git a/blocks/Backdrops/jungle.sb3/project.json b/blocks/Backdrops/jungle.sb3/project.json new file mode 100644 index 0000000000000000000000000000000000000000..8e16751b5e84223292f25a7bd80f40a7b7677f84 --- /dev/null +++ b/blocks/Backdrops/jungle.sb3/project.json @@ -0,0 +1 @@ +{"targets":[{"isStage":true,"name":"Stage","variables":{"`jEk@4|i[#Fk?(8x)AV.-my variable":["my variable",0]},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"name":"Jungle","bitmapResolution":2,"dataFormat":"png","assetId":"f4f908da19e2753f3ed679d7b37650ca","rotationCenterX":480,"rotationCenterY":360}],"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":null}],"monitors":[],"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 Edg/138.0.0.0"}} \ No newline at end of file diff --git a/blocks/blocks.json b/blocks/blocks.json new file mode 100644 index 0000000000000000000000000000000000000000..22c9e9ae7643832795da94fecc488814c8e393e4 --- /dev/null +++ b/blocks/blocks.json @@ -0,0 +1,2221 @@ +{ + "motion_movesteps": { + "opcode": "motion_movesteps", + "next": null, + "parent": null, + "inputs": { + "STEPS": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 464, + "y": -416 + }, + "motion_turnright": { + "opcode": "motion_turnright", + "next": null, + "parent": null, + "inputs": { + "DEGREES": [ + 1, + [ + 4, + "15" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 467, + "y": -316 + }, + "motion_turnleft": { + "opcode": "motion_turnleft", + "next": null, + "parent": null, + "inputs": { + "DEGREES": [ + 1, + [ + 4, + "15" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 464, + "y": -210 + }, + "motion_goto": { + "opcode": "motion_goto", + "next": null, + "parent": null, + "inputs": { + "TO": [ + 1, + "@iM=Z?~GCbpC}gT7KAKY" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 465, + "y": -95 + }, + "motion_goto_menu": { + "opcode": "motion_goto_menu", + "next": null, + "parent": "d|J?C902/xy6tD5,|dmB", + "inputs": {}, + "fields": { + "TO": [ + "_random_", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "motion_gotoxy": { + "opcode": "motion_gotoxy", + "next": null, + "parent": null, + "inputs": { + "X": [ + 1, + [ + 4, + "0" + ] + ], + "Y": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 468, + "y": 12 + }, + "motion_glideto": { + "opcode": "motion_glideto", + "next": null, + "parent": null, + "inputs": { + "SECS": [ + 1, + [ + 4, + "1" + ] + ], + "TO": [ + 1, + "{id to destination position}" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 470, + "y": 129 + }, + "motion_glideto_menu": { + "opcode": "motion_glideto_menu", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "TO": [ + "_random_", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "motion_glidesecstoxy": { + "opcode": "motion_glidesecstoxy", + "next": null, + "parent": null, + "inputs": { + "SECS": [ + 1, + [ + 4, + "1" + ] + ], + "X": [ + 1, + [ + 4, + "0" + ] + ], + "Y": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 476, + "y": 239 + }, + "motion_pointindirection": { + "opcode": "motion_pointindirection", + "next": null, + "parent": null, + "inputs": { + "DIRECTION": [ + 1, + [ + 8, + "90" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 493, + "y": 361 + }, + "motion_pointtowards": { + "opcode": "motion_pointtowards", + "next": null, + "parent": null, + "inputs": { + "TOWARDS": [ + 1, + "6xQl1pPk%9E~Znhm*:ng" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 492, + "y": 463 + }, + "motion_pointtowards_menu": { + "opcode": "motion_pointtowards_menu", + "next": null, + "parent": "Ucm$YBs*^9GFTGXCbal@", + "inputs": {}, + "fields": { + "TOWARDS": [ + "_mouse_", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "motion_changexby": { + "opcode": "motion_changexby", + "next": null, + "parent": null, + "inputs": { + "DX": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 851, + "y": -409 + }, + "motion_setx": { + "opcode": "motion_setx", + "next": null, + "parent": null, + "inputs": { + "X": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 864, + "y": -194 + }, + "motion_changeyby": { + "opcode": "motion_changeyby", + "next": null, + "parent": null, + "inputs": { + "DY": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 861, + "y": -61 + }, + "motion_sety": { + "opcode": "motion_sety", + "next": null, + "parent": null, + "inputs": { + "Y": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 864, + "y": 66 + }, + "motion_ifonedgebounce": { + "opcode": "motion_ifonedgebounce", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1131, + "y": -397 + }, + "motion_setrotationstyle": { + "opcode": "motion_setrotationstyle", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "STYLE": [ + "left-right", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 1128, + "y": -287 + }, + "motion_xposition": { + "opcode": "motion_xposition", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1193, + "y": -136 + }, + "motion_yposition": { + "opcode": "motion_yposition", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1181, + "y": -64 + }, + "motion_direction": { + "opcode": "motion_direction", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1188, + "y": 21 + }, + "control_wait": { + "opcode": "control_wait", + "next": null, + "parent": null, + "inputs": { + "DURATION": [ + 1, + [ + 5, + "1" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 337, + "y": 129 + }, + "control_repeat": { + "opcode": "control_repeat", + "next": null, + "parent": null, + "inputs": { + "TIMES": [ + 1, + [ + 6, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 348, + "y": 265 + }, + "control_forever": { + "opcode": "control_forever", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 334, + "y": 439 + }, + "control_if": { + "opcode": "control_if", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 331, + "y": 597 + }, + "control_if_else": { + "opcode": "control_if_else", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 335, + "y": 779 + }, + "control_wait_until": { + "opcode": "control_wait_until", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 676, + "y": 285 + }, + "control_repeat_until": { + "opcode": "control_repeat_until", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 692, + "y": 381 + }, + "control_stop": { + "opcode": "control_stop", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "STOP_OPTION": [ + "all", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 708, + "y": 545, + "mutation": { + "tagName": "mutation", + "children": [], + "hasnext": "false" + } + }, + "control_start_as_clone": { + "opcode": "control_start_as_clone", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 665, + "y": 672 + }, + "control_create_clone_of": { + "opcode": "control_create_clone_of", + "next": null, + "parent": null, + "inputs": { + "CLONE_OPTION": [ + 1, + "t))DW9(QSKB]3C/3Ou+J" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 648, + "y": 797 + }, + "control_create_clone_of_menu": { + "opcode": "control_create_clone_of_menu", + "next": null, + "parent": "80yo/}Cw++Z.;x[ohh|7", + "inputs": {}, + "fields": { + "CLONE_OPTION": [ + "_myself_", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "control_delete_this_clone": { + "opcode": "control_delete_this_clone", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 642, + "y": 914 + }, + "event_whenflagclicked": { + "opcode": "event_whenflagclicked", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 166, + "y": -422 + }, + "event_whenkeypressed": { + "opcode": "event_whenkeypressed", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "KEY_OPTION": [ + "space", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 151, + "y": -329 + }, + "event_whenthisspriteclicked": { + "opcode": "event_whenthisspriteclicked", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 156, + "y": -223 + }, + "event_whenbackdropswitchesto": { + "opcode": "event_whenbackdropswitchesto", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "BACKDROP": [ + "backdrop1", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 148, + "y": -101 + }, + "event_whengreaterthan": { + "opcode": "event_whengreaterthan", + "next": null, + "parent": null, + "inputs": { + "VALUE": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": { + "WHENGREATERTHANMENU": [ + "LOUDNESS", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 150, + "y": 10 + }, + "event_whenbroadcastreceived": { + "opcode": "event_whenbroadcastreceived", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "BROADCAST_OPTION": [ + "message1", + "5O!nei;S$!c!=hCT}0:a" + ] + }, + "shadow": false, + "topLevel": true, + "x": 141, + "y": 118 + }, + "event_broadcast": { + "opcode": "event_broadcast", + "next": null, + "parent": null, + "inputs": { + "BROADCAST_INPUT": [ + 1, + [ + 11, + "message1", + "5O!nei;S$!c!=hCT}0:a" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 151, + "y": 229 + }, + "event_broadcastandwait": { + "opcode": "event_broadcastandwait", + "next": null, + "parent": null, + "inputs": { + "BROADCAST_INPUT": [ + 1, + [ + 11, + "message1", + "5O!nei;S$!c!=hCT}0:a" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 157, + "y": 340 + }, + "sensing_touchingobject": { + "opcode": "sensing_touchingobject", + "next": null, + "parent": null, + "inputs": { + "TOUCHINGOBJECTMENU": [ + 1, + "xSKW9a+wTnM~h~So8Jc]" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 359, + "y": 116 + }, + "sensing_touchingobjectmenu": { + "opcode": "sensing_touchingobjectmenu", + "next": null, + "parent": "Y(n,F@BYzwd4CiN|Bh[P", + "inputs": {}, + "fields": { + "TOUCHINGOBJECTMENU": [ + "_mouse_", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "sensing_touchingcolor": { + "opcode": "sensing_touchingcolor", + "next": null, + "parent": null, + "inputs": { + "COLOR": [ + 1, + [ + 9, + "#55b888" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 360, + "y": 188 + }, + "sensing_coloristouchingcolor": { + "opcode": "sensing_coloristouchingcolor", + "next": null, + "parent": null, + "inputs": { + "COLOR": [ + 1, + [ + 9, + "#d019f2" + ] + ], + "COLOR2": [ + 1, + [ + 9, + "#2b0de3" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 348, + "y": 277 + }, + "sensing_askandwait": { + "opcode": "sensing_askandwait", + "next": null, + "parent": null, + "inputs": { + "QUESTION": [ + 1, + [ + 10, + "What's your name?" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 338, + "y": 354 + }, + "sensing_answer": { + "opcode": "sensing_answer", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 782, + "y": 111 + }, + "sensing_keypressed": { + "opcode": "sensing_keypressed", + "next": null, + "parent": null, + "inputs": { + "KEY_OPTION": [ + 1, + "SNlf@Im$sv%.6ULi-f3i" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 762, + "y": 207 + }, + "sensing_keyoptions": { + "opcode": "sensing_keyoptions", + "next": null, + "parent": "7$xEUO.2hH2R6vh!$(Uj", + "inputs": {}, + "fields": { + "KEY_OPTION": [ + "space", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "sensing_mousedown": { + "opcode": "sensing_mousedown", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 822, + "y": 422 + }, + "sensing_mousex": { + "opcode": "sensing_mousex", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 302, + "y": 528 + }, + "sensing_mousey": { + "opcode": "sensing_mousey", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 668, + "y": 547 + }, + "sensing_setdragmode": { + "opcode": "sensing_setdragmode", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "DRAG_MODE": [ + "draggable", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 950, + "y": 574 + }, + "sensing_loudness": { + "opcode": "sensing_loudness", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 658, + "y": 703 + }, + "sensing_timer": { + "opcode": "sensing_timer", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 459, + "y": 671 + }, + "sensing_resettimer": { + "opcode": "sensing_resettimer", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 462, + "y": 781 + }, + "sensing_of": { + "opcode": "sensing_of", + "next": null, + "parent": null, + "inputs": { + "OBJECT": [ + 1, + "t+o*y;iz,!O#aT|qM_+O" + ] + }, + "fields": { + "PROPERTY": [ + "backdrop #", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 997, + "y": 754 + }, + "sensing_of_object_menu": { + "opcode": "sensing_of_object_menu", + "next": null, + "parent": "[4I2wIG/tNc@LQ-;FbsB", + "inputs": {}, + "fields": { + "OBJECT": [ + "_stage_", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "sensing_current": { + "opcode": "sensing_current", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "CURRENTMENU": [ + "YEAR", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 627, + "y": 884 + }, + "sensing_dayssince2000": { + "opcode": "sensing_dayssince2000", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 959, + "y": 903 + }, + "sensing_username": { + "opcode": "sensing_username", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 833, + "y": 757 + }, + "operator_add": { + "opcode": "operator_add", + "next": null, + "parent": null, + "inputs": { + "NUM1": [ + 1, + [ + 4, + "" + ] + ], + "NUM2": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 128, + "y": 153 + }, + "operator_subtract": { + "opcode": "operator_subtract", + "next": null, + "parent": null, + "inputs": { + "NUM1": [ + 1, + [ + 4, + "" + ] + ], + "NUM2": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 134, + "y": 214 + }, + "operator_multiply": { + "opcode": "operator_multiply", + "next": null, + "parent": null, + "inputs": { + "NUM1": [ + 1, + [ + 4, + "" + ] + ], + "NUM2": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 134, + "y": 278 + }, + "operator_divide": { + "opcode": "operator_divide", + "next": null, + "parent": null, + "inputs": { + "NUM1": [ + 1, + [ + 4, + "" + ] + ], + "NUM2": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 138, + "y": 359 + }, + "operator_random": { + "opcode": "operator_random", + "next": null, + "parent": null, + "inputs": { + "FROM": [ + 1, + [ + 4, + "1" + ] + ], + "TO": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 311, + "y": 157 + }, + "operator_gt": { + "opcode": "operator_gt", + "next": null, + "parent": null, + "inputs": { + "OPERAND1": [ + 1, + [ + 10, + "" + ] + ], + "OPERAND2": [ + 1, + [ + 10, + "50" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 348, + "y": 217 + }, + "operator_lt": { + "opcode": "operator_lt", + "next": null, + "parent": null, + "inputs": { + "OPERAND1": [ + 1, + [ + 10, + "" + ] + ], + "OPERAND2": [ + 1, + [ + 10, + "50" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 345, + "y": 286 + }, + "operator_equals": { + "opcode": "operator_equals", + "next": null, + "parent": null, + "inputs": { + "OPERAND1": [ + 1, + [ + 10, + "" + ] + ], + "OPERAND2": [ + 1, + [ + 10, + "50" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 345, + "y": 372 + }, + "operator_and": { + "opcode": "operator_and", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 701, + "y": 158 + }, + "operator_or": { + "opcode": "operator_or", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 705, + "y": 222 + }, + "operator_not": { + "opcode": "operator_not", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 734, + "y": 283 + }, + "operator_join": { + "opcode": "operator_join", + "next": null, + "parent": null, + "inputs": { + "STRING1": [ + 1, + [ + 10, + "apple " + ] + ], + "STRING2": [ + 1, + [ + 10, + "banana" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 663, + "y": 378 + }, + "operator_letter_of": { + "opcode": "operator_letter_of", + "next": null, + "parent": null, + "inputs": { + "LETTER": [ + 1, + [ + 6, + "1" + ] + ], + "STRING": [ + 1, + [ + 10, + "apple" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 664, + "y": 445 + }, + "operator_length": { + "opcode": "operator_length", + "next": null, + "parent": null, + "inputs": { + "STRING": [ + 1, + [ + 10, + "apple" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 664, + "y": 521 + }, + "operator_contains": { + "opcode": "operator_contains", + "next": null, + "parent": null, + "inputs": { + "STRING1": [ + 1, + [ + 10, + "apple" + ] + ], + "STRING2": [ + 1, + [ + 10, + "a" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 634, + "y": 599 + }, + "operator_mod": { + "opcode": "operator_mod", + "next": null, + "parent": null, + "inputs": { + "NUM1": [ + 1, + [ + 4, + "" + ] + ], + "NUM2": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 295, + "y": 594 + }, + "operator_round": { + "opcode": "operator_round", + "next": null, + "parent": null, + "inputs": { + "NUM": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 307, + "y": 674 + }, + "operator_mathop": { + "opcode": "operator_mathop", + "next": null, + "parent": null, + "inputs": { + "NUM": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": { + "OPERATOR": [ + "abs", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 280, + "y": 754 + }, + "data_setvariableto": { + "opcode": "data_setvariableto", + "next": null, + "parent": null, + "inputs": { + "VALUE": [ + 1, + [ + 10, + "0" + ] + ] + }, + "fields": { + "VARIABLE": [ + "my variable", + "`jEk@4|i[#Fk?(8x)AV.-my variable" + ] + }, + "shadow": false, + "topLevel": true, + "x": 348, + "y": 241 + }, + "data_changevariableby": { + "opcode": "data_changevariableby", + "next": null, + "parent": null, + "inputs": { + "VALUE": [ + 1, + [ + 4, + "1" + ] + ] + }, + "fields": { + "VARIABLE": [ + "my variable", + "`jEk@4|i[#Fk?(8x)AV.-my variable" + ] + }, + "shadow": false, + "topLevel": true, + "x": 313, + "y": 363 + }, + "data_showvariable": { + "opcode": "data_showvariable", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "VARIABLE": [ + "my variable", + "`jEk@4|i[#Fk?(8x)AV.-my variable" + ] + }, + "shadow": false, + "topLevel": true, + "x": 415, + "y": 473 + }, + "data_hidevariable": { + "opcode": "data_hidevariable", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "VARIABLE": [ + "my variable", + "`jEk@4|i[#Fk?(8x)AV.-my variable" + ] + }, + "shadow": false, + "topLevel": true, + "x": 319, + "y": 587 + }, + "data_addtolist": { + "opcode": "data_addtolist", + "next": null, + "parent": null, + "inputs": { + "ITEM": [ + 1, + [ + 10, + "thing" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 385, + "y": 109 + }, + "data_deleteoflist": { + "opcode": "data_deleteoflist", + "next": null, + "parent": null, + "inputs": { + "INDEX": [ + 1, + [ + 7, + "1" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 384, + "y": 244 + }, + "data_deletealloflist": { + "opcode": "data_deletealloflist", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 387, + "y": 374 + }, + "data_insertatlist": { + "opcode": "data_insertatlist", + "next": null, + "parent": null, + "inputs": { + "ITEM": [ + 1, + [ + 10, + "thing" + ] + ], + "INDEX": [ + 1, + [ + 7, + "1" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 366, + "y": 527 + }, + "data_replaceitemoflist": { + "opcode": "data_replaceitemoflist", + "next": null, + "parent": null, + "inputs": { + "INDEX": [ + 1, + [ + 7, + "1" + ] + ], + "ITEM": [ + 1, + [ + 10, + "thing" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 365, + "y": 657 + }, + "data_itemoflist": { + "opcode": "data_itemoflist", + "next": null, + "parent": null, + "inputs": { + "INDEX": [ + 1, + [ + 7, + "1" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 862, + "y": 117 + }, + "data_itemnumoflist": { + "opcode": "data_itemnumoflist", + "next": null, + "parent": null, + "inputs": { + "ITEM": [ + 1, + [ + 10, + "thing" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 883, + "y": 238 + }, + "data_lengthoflist": { + "opcode": "data_lengthoflist", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 876, + "y": 342 + }, + "data_listcontainsitem": { + "opcode": "data_listcontainsitem", + "next": null, + "parent": null, + "inputs": { + "ITEM": [ + 1, + [ + 10, + "thing" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 871, + "y": 463 + }, + "data_showlist": { + "opcode": "data_showlist", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 931, + "y": 563 + }, + "data_hidelist": { + "opcode": "data_hidelist", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 962, + "y": 716 + }, + "sound_playuntildone": { + "opcode": "sound_playuntildone", + "next": null, + "parent": null, + "inputs": { + "SOUND_MENU": [ + 1, + "4w%pR8G.yD%g-BwCj=uK" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 253, + "y": 17 + }, + "sound_sounds_menu": { + "opcode": "sound_sounds_menu", + "next": null, + "parent": "Pdc$U;s8e_uUfTX`}jOo", + "inputs": {}, + "fields": { + "SOUND_MENU": [ + "Meow", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "sound_play": { + "opcode": "sound_play", + "next": null, + "parent": null, + "inputs": { + "SOUND_MENU": [ + 1, + "i1U{^VHb*2`9?l}=:L)/" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 245, + "y": 122 + }, + "sound_stopallsounds": { + "opcode": "sound_stopallsounds", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 253, + "y": 245 + }, + "sound_changeeffectby": { + "opcode": "sound_changeeffectby", + "next": null, + "parent": null, + "inputs": { + "VALUE": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": { + "EFFECT": [ + "PITCH", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 653, + "y": 14 + }, + "sound_seteffectto": { + "opcode": "sound_seteffectto", + "next": null, + "parent": null, + "inputs": { + "VALUE": [ + 1, + [ + 4, + "100" + ] + ] + }, + "fields": { + "EFFECT": [ + "PITCH", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 653, + "y": 139 + }, + "sound_cleareffects": { + "opcode": "sound_cleareffects", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 651, + "y": 242 + }, + "sound_changevolumeby": { + "opcode": "sound_changevolumeby", + "next": null, + "parent": null, + "inputs": { + "VOLUME": [ + 1, + [ + 4, + "-10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 645, + "y": 353 + }, + "sound_setvolumeto": { + "opcode": "sound_setvolumeto", + "next": null, + "parent": null, + "inputs": { + "VOLUME": [ + 1, + [ + 4, + "100" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1108, + "y": 5 + }, + "sound_volume": { + "opcode": "sound_volume", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1136, + "y": 123 + }, + "looks_sayforsecs": { + "opcode": "looks_sayforsecs", + "next": null, + "parent": null, + "inputs": { + "MESSAGE": [ + 1, + [ + 10, + "Hello!" + ] + ], + "SECS": [ + 1, + [ + 4, + "2" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 408, + "y": 91 + }, + "looks_say": { + "opcode": "looks_say", + "next": null, + "parent": null, + "inputs": { + "MESSAGE": [ + 1, + [ + 10, + "Hello!" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 413, + "y": 213 + }, + "looks_thinkforsecs": { + "opcode": "looks_thinkforsecs", + "next": null, + "parent": null, + "inputs": { + "MESSAGE": [ + 1, + [ + 10, + "Hmm..." + ] + ], + "SECS": [ + 1, + [ + 4, + "2" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 413, + "y": 317 + }, + "looks_think": { + "opcode": "looks_think", + "next": null, + "parent": null, + "inputs": { + "MESSAGE": [ + 1, + [ + 10, + "Hmm..." + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 412, + "y": 432 + }, + "looks_switchcostumeto": { + "opcode": "looks_switchcostumeto", + "next": null, + "parent": null, + "inputs": { + "COSTUME": [ + 1, + "8;bti4wv(iH9nkOacCJ|" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 411, + "y": 555 + }, + "looks_costume": { + "opcode": "looks_costume", + "next": null, + "parent": "Q#a,6LPWHqo9-0Nu*[SV", + "inputs": {}, + "fields": { + "COSTUME": [ + "costume2", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "looks_nextcostume": { + "opcode": "looks_nextcostume", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 419, + "y": 687 + }, + "looks_switchbackdropto": { + "opcode": "looks_switchbackdropto", + "next": null, + "parent": null, + "inputs": { + "BACKDROP": [ + 1, + "-?yeX}29V*wd6W:unW0i" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 901, + "y": 91 + }, + "looks_backdrops": { + "opcode": "looks_backdrops", + "next": null, + "parent": "`Wm^p~l[(IWzc1|wNv*.", + "inputs": {}, + "fields": { + "BACKDROP": [ + "backdrop1", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "looks_changesizeby": { + "opcode": "looks_changesizeby", + "next": null, + "parent": null, + "inputs": { + "CHANGE": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 895, + "y": 192 + }, + "looks_setsizeto": { + "opcode": "looks_setsizeto", + "next": null, + "parent": null, + "inputs": { + "SIZE": [ + 1, + [ + 4, + "100" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 896, + "y": 303 + }, + "looks_changeeffectby": { + "opcode": "looks_changeeffectby", + "next": null, + "parent": null, + "inputs": { + "CHANGE": [ + 1, + [ + 4, + "25" + ] + ] + }, + "fields": { + "EFFECT": [ + "COLOR", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 892, + "y": 416 + }, + "looks_seteffectto": { + "opcode": "looks_seteffectto", + "next": null, + "parent": null, + "inputs": { + "VALUE": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": { + "EFFECT": [ + "COLOR", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 902, + "y": 527 + }, + "looks_cleargraphiceffects": { + "opcode": "looks_cleargraphiceffects", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 902, + "y": 638 + }, + "looks_show": { + "opcode": "looks_show", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 908, + "y": 758 + }, + "looks_hide": { + "opcode": "looks_hide", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 455, + "y": 861 + }, + "looks_gotofrontback": { + "opcode": "looks_gotofrontback", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "FRONT_BACK": [ + "front", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 853, + "y": 878 + }, + "looks_goforwardbackwardlayers": { + "opcode": "looks_goforwardbackwardlayers", + "next": null, + "parent": null, + "inputs": { + "NUM": [ + 1, + [ + 7, + "1" + ] + ] + }, + "fields": { + "FORWARD_BACKWARD": [ + "forward", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 851, + "y": 999 + }, + "looks_costumenumbername": { + "opcode": "looks_costumenumbername", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "NUMBER_NAME": [ + "number", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 458, + "y": 1007 + }, + "looks_backdropnumbername": { + "opcode": "looks_backdropnumbername", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "NUMBER_NAME": [ + "number", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 1242, + "y": 753 + }, + "looks_size": { + "opcode": "looks_size", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1249, + "y": 876 + } +} \ No newline at end of file diff --git a/blocks/boolean_blocks.json b/blocks/boolean_blocks.json new file mode 100644 index 0000000000000000000000000000000000000000..63a850dad22b037e849a1c5b668d71b02bfa86d7 --- /dev/null +++ b/blocks/boolean_blocks.json @@ -0,0 +1,281 @@ +{ + "block_category": "Boolean Blocks", + "description": "Boolean blocks are hexagonal in shape. They represent conditions that evaluate to either 'true' or 'false' and are typically used as inputs for control flow blocks.", + "blocks": [ + { + "block_name": "<() < ()>", + "block_type": "operator", + "op_code": "operator_lt", + "block_shape": "Boolean Block", + "functionality": "Checks if the first value is less than the second.", + "inputs": [ + {"name": "OPERAND1", "type": "any"}, + {"name": "OPERAND2", "type": "any"} + ], + "example_standalone": "<(score) < (10)>", + "example_with_other_blocks": [ + { + "script": "if <(score) < (10)> then\n say [Keep trying!] \nend", + "explanation": "This script causes the sprite to say 'Keep trying!' if the 'score' variable is less than 10." + } + ] + }, + { + "block_name": "<() = ()>", + "block_type": "operator", + "op_code": "operator_equals", + "block_shape": "Boolean Block", + "functionality": "Checks if two values are equal.", + "inputs": [ + {"name": "OPERAND1", "type": "any"}, + {"name": "OPERAND2", "type": "any"} + ], + "example_standalone": "<(answer) = (5)>", + "example_with_other_blocks": [ + { + "script": "if <(answer) = (5)> then\n say [Correct!] \nend", + "explanation": "This script makes the sprite say 'Correct!' if the value of the 'answer' variable is exactly 5." + } + ] + }, + { + "block_name": "<() > ()>", + "block_type": "operator", + "op_code": "operator_gt", + "block_shape": "Boolean Block", + "functionality": "Checks if the first value is greater than the second.", + "inputs": [ + {"name": "OPERAND1", "type": "any"}, + {"name": "OPERAND2", "type": "any"} + ], + "example_standalone": "<([health v]) > (0)>", + "example_with_other_blocks": [ + { + "script": "if <([health v]) > (0)> then\n move (10) steps\nelse\n stop [all v]\nend", + "explanation": "This script moves the sprite if its 'health' is greater than 0; otherwise, it stops all scripts." + } + ] + }, + { + "block_name": "<<> and <>>", + "block_type": "operator", + "op_code": "operator_and", + "block_shape": "Boolean Block", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": [ + {"name": "OPERAND1", "type": "boolean"}, + {"name": "OPERAND2", "type": "boolean"} + ], + "example_standalone": "< and >", + "example_with_other_blocks": [ + { + "script": "if < and > then\n say [You're clicking me!]\nend", + "explanation": "This script makes the sprite say 'You're clicking me!' only if the mouse button is pressed AND the mouse pointer is touching the sprite." + } + ] + }, + { + "block_name": "<<> or <>>", + "block_type": "operator", + "op_code": "operator_or", + "block_shape": "Boolean Block", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": [ + {"name": "OPERAND1", "type": "boolean"}, + {"name": "OPERAND2", "type": "boolean"} + ], + "example_standalone": "< or >", + "example_with_other_blocks": [ + { + "script": "if < or > then\n change x by (-10)\nend", + "explanation": "This script moves the sprite left if either the left arrow key OR the 'a' key is pressed." + } + ] + }, + { + "block_name": ">", + "block_type": "operator", + "op_code": "operator_not", + "block_shape": "Boolean Block", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": [ + {"name": "OPERAND", "type": "boolean"} + ], + "example_standalone": ">", + "example_with_other_blocks": [ + { + "script": "if > then\n say [I'm safe!]\nend", + "explanation": "This script makes the sprite say 'I'm safe!' if it is NOT touching 'Sprite2'." + } + ] + }, + { + "block_name": "<() contains ()?>", + "block_type": "operator", + "op_code": "operator_contains", + "block_shape": "Boolean Block", + "functionality": "Checks if one string contains another string.", + "inputs": [ + {"name": "STRING1", "type": "string"}, + {"name": "STRING2", "type": "string"} + ], + "example_standalone": "<[apple v] contains [a v]?>", + "example_with_other_blocks": [ + { + "script": "if <[answer] contains [yes]?> then\n say [Great!]\nend", + "explanation": "This script makes the sprite say 'Great!' if the 'answer' variable contains the substring 'yes'." + } + ] + }, + { + "block_name": "", + "block_type": "Sensing", + "op_code": "sensing_touchingobject", + "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": [ + {"name": "TOUCHINGOBJECTMENU", "type": "dropdown", "options": ["mouse-pointer", "edge", "Sprite1", "..." ]} + ], + "example_standalone": "", + "example_with_other_blocks": [ + { + "script": "if then\n broadcast [Game Over v] \nend", + "explanation": "This script makes the broadcast message 'Game Over' in script if it comes into contact with the sprite." + }, + { + "script": "if then\n bounce off edge\nend", + "explanation": "This script makes the sprite reverse direction if it comes into contact with the edge of the stage." + } + ] + }, + { + "block_name": "", + "block_type": "Sensing", + "op_code": "sensing_touchingcolor", + "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": [ + {"name": "COLOR", "type": "color"} + ], + "example_standalone": "", + "example_with_other_blocks": [ + { + "script": "if then\n change [health v] by (-1)\nend", + "explanation": "This script decreases the 'health' variable by 1 if the sprite touches any red color on the stage." + } + ] + }, + { + "block_name": "", + "block_type": "Sensing", + "op_code": "sensing_coloristouchingcolor", + "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": [ + {"name": "COLOR1", "type": "color"}, + {"name": "COLOR2", "type": "color"} + ], + "example_standalone": "", + "example_with_other_blocks": [ + { + "script": "if then\n say [Collision!]\nend", + "explanation": "This script makes the sprite say 'Collision!' if a green part of the sprite touches a red color elsewhere in the project." + } + ] + }, + { + "block_name": "", + "block_type": "Sensing", + "op_code": "sensing_keypressed", + "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": [ + {"name": "KEY_OPTION", "type": "dropdown", + "options": [ + "space", + "up arrow", + "down arrow", + "right arrow", + "left arrow", + "any", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9" + ]} + ], + "example_standalone": "", + "example_with_other_blocks": [ + { + "script": "forever\n if then\n broadcast [shoot v]\n end\nend", + "explanation": "This script continuously checks if the space key is pressed and, if so, sends a 'shoot' broadcast." + } + ] + }, + { + "block_name": "", + "block_type": "Sensing", + "op_code": "sensing_mousedown", + "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": null, + "example_standalone": "", + "example_with_other_blocks": [ + { + "script": "if then\n go to mouse-pointer\nend", + "explanation": "This script makes the sprite follow the mouse pointer only when the mouse button is held down." + } + ] + }, + { + "block_name": "<[my list v] contains ()?>", + "block_type": "Data", + "op_code": "data_listcontainsitem", + "block_shape": "Boolean Block", + "functionality": "Checks if a list includes a specific item.", + "inputs": [ + {"name": "LIST", "type": "dropdown"}, + {"name": "ITEM", "type": "any"} + ], + "example_standalone": "<[inventory v] contains [key]?>", + "example_with_other_blocks": [ + { + "script": "if <[inventory v] contains [key]?> then\n say [You have the key!]\nend", + "explanation": "This script makes the sprite say 'You have the key!' if the 'inventory' list contains the item 'key'." + } + ] + } + ] +} diff --git a/blocks/c_blocks.json b/blocks/c_blocks.json new file mode 100644 index 0000000000000000000000000000000000000000..fd0c7ab375a42393fb876ec98390bc1246a57dc6 --- /dev/null +++ b/blocks/c_blocks.json @@ -0,0 +1,105 @@ +{ + "block_category": "C Blocks", + "description": "C blocks are shaped like the letter 'C'. They are used to loop or conditionally execute blocks that are placed within their opening, managing the flow of scripts.", + "blocks": [ + { + "block_name": "repeat ()", + "block_type": "Control", + "block_shape": "C-Block", + "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": [ + { + "name": "times", + "type": "number" + } + ], + "example_standalone": "repeat (10)", + "example_with_other_blocks": [ + { + "script": "when [space v] key pressed\n repeat (10)\n move (10) steps\n wait (0.1) seconds\n end", + "explanation": "This script makes the sprite move 10 steps Ten times, with a short pause after each movement on spacebar pressed." + }, + { + "script": "when [up arrow v] key pressed\n repeat (10)\n change y by (10)\n wait (0.1) seconds\n change y by (10)\n end", + "explanation": "This script makes the sprite jump, with a short pause after each movement on up arrow pressed." + } + ] + }, + { + "block_name": "forever", + "block_type": "Control", + "block_shape": "C-Block", + "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": null, + "example_standalone": "forever", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n forever\n move (5) steps\n if on edge, bounce\n end", + "explanation": "This script makes the sprite move endlessly and bounce off the edges of the stage, creating continuous motion." + } + ] + }, + { + "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": [ + { + "name": "condition", + "type": "boolean" + } + ], + "example_standalone": "if then", + "example_with_other_blocks": [ + { + "script": "forever\n if then\n stop [this script v]\n end", + "explanation": "This script continuously checks if the sprite is touching a red color, and if so, it stops the current script." + } + ] + }, + { + "block_name": "if <> then else", + "block_type": "Control", + "block_shape": "C-Block", + "op_code": "control_if_else", + "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]", + "inputs": [ + { + "name": "condition", + "type": "boolean" + } + ], + "example_standalone": "if (10)> then else", + "example_with_other_blocks": [ + { + "script": "if <(score) > (10)> then\n say [You win!] for (2) seconds\nelse\n say [Keep trying!] for (2) seconds\nend", + "explanation": "This script checks the 'score'. If the score is greater than 10, it says 'You win!'; otherwise, it says 'Keep trying!'." + } + ] + }, + { + "block_name": "repeat until <>", + "block_type": "Control", + "block_shape": "C-Block", + "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": [ + { + "name": "condition", + "type": "boolean" + } + ], + "example_standalone": "repeat until ", + "example_with_other_blocks": [ + { + "script": "repeat until \n move (5) steps\nend", + "explanation": "This script makes the sprite move 5 steps repeatedly until it touches the edge of the stage." + } + ] + } + ] +} diff --git a/blocks/cap_blocks.json b/blocks/cap_blocks.json new file mode 100644 index 0000000000000000000000000000000000000000..8a5de615f1c2da78d7cfd552f952e18127c5291d --- /dev/null +++ b/blocks/cap_blocks.json @@ -0,0 +1,53 @@ +{ + "block_category": "Cap Blocks", + "description": "Cap blocks have a notch at the top and a flat bottom. They signify the end of a script, preventing any further blocks from being placed below them, and are used to terminate scripts or specific actions.", + "blocks": [ + { + "block_name": "stop [v]", + "block_type": "Control", + "block_shape": "Cap Block (dynamic: can be Stack)", + "op_code": "control_stop", + "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.", + "inputs": [ + {"name": "option", "type": "dropdown", "options": ["all"]} + ], + "example_standalone": "stop [all v]", + "example_with_other_blocks": [ + { + "script": "if <(health) = (0)> then\n stop [all v]\nend", + "explanation": "This script stops all running scripts in the project if the 'health' variable reaches 0, typically signifying a game over condition. [9, 15]" + } + ] + }, + { + "block_name": "delete this clone", + "block_type": "Control", + "block_shape": "Cap Block", + "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":null, + "example_standalone": "delete this clone", + "example_with_other_blocks": [ + { + "script": "when I start as a clone\n wait until \n delete this clone\nend", + "explanation": "This script, run by a clone, causes the clone to disappear from the stage once it touches the edge. [1]" + } + ] + }, + { + "block_name": "forever", + "block_type": "Control", + "block_shape": "Cap Block", + "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": null, + "example_standalone": "forever", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n forever\n move (5) steps\n if on edge, bounce\n end", + "explanation": "This script makes the sprite move endlessly and bounce off the edges of the stage, creating continuous motion." + } + ] + } + ] +} \ No newline at end of file diff --git a/blocks/classwise_blocks/control_block.json b/blocks/classwise_blocks/control_block.json new file mode 100644 index 0000000000000000000000000000000000000000..73f99f63ca536ec7f9fdd617c057a5748cc9eab3 --- /dev/null +++ b/blocks/classwise_blocks/control_block.json @@ -0,0 +1,168 @@ +{ + "control_wait": { + "opcode": "control_wait", + "next": null, + "parent": null, + "inputs": { + "DURATION": [ + 1, + [ + 5, + "1" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 337, + "y": 129 + }, + "control_repeat": { + "opcode": "control_repeat", + "next": null, + "parent": null, + "inputs": { + "TIMES": [ + 1, + [ + 6, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 348, + "y": 265 + }, + "control_forever": { + "opcode": "control_forever", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 334, + "y": 439 + }, + "control_if": { + "opcode": "control_if", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 331, + "y": 597 + }, + "control_if_else": { + "opcode": "control_if_else", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 335, + "y": 779 + }, + "control_wait_until": { + "opcode": "control_wait_until", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 676, + "y": 285 + }, + "control_repeat_until": { + "opcode": "control_repeat_until", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 692, + "y": 381 + }, + "control_stop": { + "opcode": "control_stop", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "STOP_OPTION": [ + "all", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 708, + "y": 545, + "mutation": { + "tagName": "mutation", + "children": [], + "hasnext": "false" + } + }, + "control_start_as_clone": { + "opcode": "control_start_as_clone", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 665, + "y": 672 + }, + "control_create_clone_of": { + "opcode": "control_create_clone_of", + "next": null, + "parent": null, + "inputs": { + "CLONE_OPTION": [ + 1, + "control_create_clone_of_menu" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 648, + "y": 797 + }, + "control_create_clone_of_menu": { + "opcode": "control_create_clone_of_menu", + "next": null, + "parent": "control_create_clone_of", + "inputs": {}, + "fields": { + "CLONE_OPTION": [ + "_myself_", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "control_delete_this_clone": { + "opcode": "control_delete_this_clone", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 642, + "y": 914 + } +} \ No newline at end of file diff --git a/blocks/classwise_blocks/data_block.json b/blocks/classwise_blocks/data_block.json new file mode 100644 index 0000000000000000000000000000000000000000..c838cb007d7fab83f4a70cb5df39d71d2a4d6fd6 --- /dev/null +++ b/blocks/classwise_blocks/data_block.json @@ -0,0 +1,328 @@ +{ + "data_setvariableto": { + "opcode": "data_setvariableto", + "next": null, + "parent": null, + "inputs": { + "VALUE": [ + 1, + [ + 10, + "0" + ] + ] + }, + "fields": { + "VARIABLE": [ + "my variable", + "`jEk@4|i[#Fk?(8x)AV.-my variable" + ] + }, + "shadow": false, + "topLevel": true, + "x": 348, + "y": 241 + }, + "data_changevariableby": { + "opcode": "data_changevariableby", + "next": null, + "parent": null, + "inputs": { + "VALUE": [ + 1, + [ + 4, + "1" + ] + ] + }, + "fields": { + "VARIABLE": [ + "my variable", + "`jEk@4|i[#Fk?(8x)AV.-my variable" + ] + }, + "shadow": false, + "topLevel": true, + "x": 313, + "y": 363 + }, + "data_showvariable": { + "opcode": "data_showvariable", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "VARIABLE": [ + "my variable", + "`jEk@4|i[#Fk?(8x)AV.-my variable" + ] + }, + "shadow": false, + "topLevel": true, + "x": 415, + "y": 473 + }, + "data_hidevariable": { + "opcode": "data_hidevariable", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "VARIABLE": [ + "my variable", + "`jEk@4|i[#Fk?(8x)AV.-my variable" + ] + }, + "shadow": false, + "topLevel": true, + "x": 319, + "y": 587 + }, + "data_addtolist": { + "opcode": "data_addtolist", + "next": null, + "parent": null, + "inputs": { + "ITEM": [ + 1, + [ + 10, + "thing" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 385, + "y": 109 + }, + "data_deleteoflist": { + "opcode": "data_deleteoflist", + "next": null, + "parent": null, + "inputs": { + "INDEX": [ + 1, + [ + 7, + "1" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 384, + "y": 244 + }, + "data_deletealloflist": { + "opcode": "data_deletealloflist", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 387, + "y": 374 + }, + "data_insertatlist": { + "opcode": "data_insertatlist", + "next": null, + "parent": null, + "inputs": { + "ITEM": [ + 1, + [ + 10, + "thing" + ] + ], + "INDEX": [ + 1, + [ + 7, + "1" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 366, + "y": 527 + }, + "data_replaceitemoflist": { + "opcode": "data_replaceitemoflist", + "next": null, + "parent": null, + "inputs": { + "INDEX": [ + 1, + [ + 7, + "1" + ] + ], + "ITEM": [ + 1, + [ + 10, + "thing" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 365, + "y": 657 + }, + "data_itemoflist": { + "opcode": "data_itemoflist", + "next": null, + "parent": null, + "inputs": { + "INDEX": [ + 1, + [ + 7, + "1" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 862, + "y": 117 + }, + "data_itemnumoflist": { + "opcode": "data_itemnumoflist", + "next": null, + "parent": null, + "inputs": { + "ITEM": [ + 1, + [ + 10, + "thing" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 883, + "y": 238 + }, + "data_lengthoflist": { + "opcode": "data_lengthoflist", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 876, + "y": 342 + }, + "data_listcontainsitem": { + "opcode": "data_listcontainsitem", + "next": null, + "parent": null, + "inputs": { + "ITEM": [ + 1, + [ + 10, + "thing" + ] + ] + }, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 871, + "y": 463 + }, + "data_showlist": { + "opcode": "data_showlist", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 931, + "y": 563 + }, + "data_hidelist": { + "opcode": "data_hidelist", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "LIST": [ + "MY_LIST", + "o6`kIhtT{xWH+rX(5d,A" + ] + }, + "shadow": false, + "topLevel": true, + "x": 962, + "y": 716 + } +} \ No newline at end of file diff --git a/blocks/classwise_blocks/event_block.json b/blocks/classwise_blocks/event_block.json new file mode 100644 index 0000000000000000000000000000000000000000..521d65d9d5ce53107f80696bd33e30a2ad20faa9 --- /dev/null +++ b/blocks/classwise_blocks/event_block.json @@ -0,0 +1,136 @@ +{ + "event_whenflagclicked": { + "opcode": "event_whenflagclicked", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 166, + "y": -422 + }, + "event_whenkeypressed": { + "opcode": "event_whenkeypressed", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "KEY_OPTION": [ + "space", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 151, + "y": -329 + }, + "event_whenthisspriteclicked": { + "opcode": "event_whenthisspriteclicked", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 156, + "y": -223 + }, + "event_whenbackdropswitchesto": { + "opcode": "event_whenbackdropswitchesto", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "BACKDROP": [ + "backdrop1", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 148, + "y": -101 + }, + "event_whengreaterthan": { + "opcode": "event_whengreaterthan", + "next": null, + "parent": null, + "inputs": { + "VALUE": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": { + "WHENGREATERTHANMENU": [ + "LOUDNESS", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 150, + "y": 10 + }, + "event_whenbroadcastreceived": { + "opcode": "event_whenbroadcastreceived", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "BROADCAST_OPTION": [ + "message1", + "5O!nei;S$!c!=hCT}0:a" + ] + }, + "shadow": false, + "topLevel": true, + "x": 141, + "y": 118 + }, + "event_broadcast": { + "opcode": "event_broadcast", + "next": null, + "parent": null, + "inputs": { + "BROADCAST_INPUT": [ + 1, + [ + 11, + "message1", + "5O!nei;S$!c!=hCT}0:a" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 151, + "y": 229 + }, + "event_broadcastandwait": { + "opcode": "event_broadcastandwait", + "next": null, + "parent": null, + "inputs": { + "BROADCAST_INPUT": [ + 1, + [ + 11, + "message1", + "5O!nei;S$!c!=hCT}0:a" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 157, + "y": 340 + } +} \ No newline at end of file diff --git a/blocks/classwise_blocks/look_block.json b/blocks/classwise_blocks/look_block.json new file mode 100644 index 0000000000000000000000000000000000000000..e025de6a3af437de44707afe68fe391bb9b89bd8 --- /dev/null +++ b/blocks/classwise_blocks/look_block.json @@ -0,0 +1,365 @@ +{ + "looks_sayforsecs": { + "opcode": "looks_sayforsecs", + "next": null, + "parent": null, + "inputs": { + "MESSAGE": [ + 1, + [ + 10, + "Hello!" + ] + ], + "SECS": [ + 1, + [ + 4, + "2" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 408, + "y": 91 + }, + "looks_say": { + "opcode": "looks_say", + "next": null, + "parent": null, + "inputs": { + "MESSAGE": [ + 1, + [ + 10, + "Hello!" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 413, + "y": 213 + }, + "looks_thinkforsecs": { + "opcode": "looks_thinkforsecs", + "next": null, + "parent": null, + "inputs": { + "MESSAGE": [ + 1, + [ + 10, + "Hmm..." + ] + ], + "SECS": [ + 1, + [ + 4, + "2" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 413, + "y": 317 + }, + "looks_think": { + "opcode": "looks_think", + "next": null, + "parent": null, + "inputs": { + "MESSAGE": [ + 1, + [ + 10, + "Hmm..." + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 412, + "y": 432 + }, + "looks_switchcostumeto": { + "opcode": "looks_switchcostumeto", + "next": null, + "parent": null, + "inputs": { + "COSTUME": [ + 1, + "looks_costume" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 411, + "y": 555 + }, + "looks_costume": { + "opcode": "looks_costume", + "next": null, + "parent": "looks_switchcostumeto", + "inputs": {}, + "fields": { + "COSTUME": [ + "costume2", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "looks_nextcostume": { + "opcode": "looks_nextcostume", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 419, + "y": 687 + }, + "looks_switchbackdropto": { + "opcode": "looks_switchbackdropto", + "next": null, + "parent": null, + "inputs": { + "BACKDROP": [ + 1, + "looks_backdrops" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 901, + "y": 91 + }, + "looks_backdrops": { + "opcode": "looks_backdrops", + "next": null, + "parent": "looks_switchbackdropto", + "inputs": {}, + "fields": { + "BACKDROP": [ + "backdrop1", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "looks_changesizeby": { + "opcode": "looks_changesizeby", + "next": null, + "parent": null, + "inputs": { + "CHANGE": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 895, + "y": 192 + }, + "looks_setsizeto": { + "opcode": "looks_setsizeto", + "next": null, + "parent": null, + "inputs": { + "SIZE": [ + 1, + [ + 4, + "100" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 896, + "y": 303 + }, + "looks_changeeffectby": { + "opcode": "looks_changeeffectby", + "next": null, + "parent": null, + "inputs": { + "CHANGE": [ + 1, + [ + 4, + "25" + ] + ] + }, + "fields": { + "EFFECT": [ + "COLOR", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 892, + "y": 416 + }, + "looks_seteffectto": { + "opcode": "looks_seteffectto", + "next": null, + "parent": null, + "inputs": { + "VALUE": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": { + "EFFECT": [ + "COLOR", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 902, + "y": 527 + }, + "looks_cleargraphiceffects": { + "opcode": "looks_cleargraphiceffects", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 902, + "y": 638 + }, + "looks_show": { + "opcode": "looks_show", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 908, + "y": 758 + }, + "looks_hide": { + "opcode": "looks_hide", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 455, + "y": 861 + }, + "looks_gotofrontback": { + "opcode": "looks_gotofrontback", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "FRONT_BACK": [ + "front", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 853, + "y": 878 + }, + "looks_goforwardbackwardlayers": { + "opcode": "looks_goforwardbackwardlayers", + "next": null, + "parent": null, + "inputs": { + "NUM": [ + 1, + [ + 7, + "1" + ] + ] + }, + "fields": { + "FORWARD_BACKWARD": [ + "forward", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 851, + "y": 999 + }, + "looks_costumenumbername": { + "opcode": "looks_costumenumbername", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "NUMBER_NAME": [ + "number", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 458, + "y": 1007 + }, + "looks_backdropnumbername": { + "opcode": "looks_backdropnumbername", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "NUMBER_NAME": [ + "number", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 1242, + "y": 753 + }, + "looks_size": { + "opcode": "looks_size", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1249, + "y": 876 + } +} \ No newline at end of file diff --git a/blocks/classwise_blocks/motion_block.json b/blocks/classwise_blocks/motion_block.json new file mode 100644 index 0000000000000000000000000000000000000000..677651d11595cddd09a526a140e3ca93efa4f136 --- /dev/null +++ b/blocks/classwise_blocks/motion_block.json @@ -0,0 +1,370 @@ +{ + "motion_movesteps": { + "opcode": "motion_movesteps", + "next": null, + "parent": null, + "inputs": { + "STEPS": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 464, + "y": -416 + }, + "motion_turnright": { + "opcode": "motion_turnright", + "next": null, + "parent": null, + "inputs": { + "DEGREES": [ + 1, + [ + 4, + "15" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 467, + "y": -316 + }, + "motion_turnleft": { + "opcode": "motion_turnleft", + "next": null, + "parent": null, + "inputs": { + "DEGREES": [ + 1, + [ + 4, + "15" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 464, + "y": -210 + }, + "motion_goto": { + "opcode": "motion_goto", + "next": null, + "parent": null, + "inputs": { + "TO": [ + 1, + "motion_goto_menu" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 465, + "y": -95 + }, + "motion_goto_menu": { + "opcode": "motion_goto_menu", + "next": null, + "parent": "motion_goto", + "inputs": {}, + "fields": { + "TO": [ + "_random_", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "motion_gotoxy": { + "opcode": "motion_gotoxy", + "next": null, + "parent": null, + "inputs": { + "X": [ + 1, + [ + 4, + "0" + ] + ], + "Y": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 468, + "y": 12 + }, + "motion_glideto": { + "opcode": "motion_glideto", + "next": null, + "parent": null, + "inputs": { + "SECS": [ + 1, + [ + 4, + "1" + ] + ], + "TO": [ + 1, + "motion_glideto_menu" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 470, + "y": 129 + }, + "motion_glideto_menu": { + "opcode": "motion_glideto_menu", + "next": null, + "parent": "motion_glideto", + "inputs": {}, + "fields": { + "TO": [ + "_random_", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "motion_glidesecstoxy": { + "opcode": "motion_glidesecstoxy", + "next": null, + "parent": null, + "inputs": { + "SECS": [ + 1, + [ + 4, + "1" + ] + ], + "X": [ + 1, + [ + 4, + "0" + ] + ], + "Y": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 476, + "y": 239 + }, + "motion_pointindirection": { + "opcode": "motion_pointindirection", + "next": null, + "parent": null, + "inputs": { + "DIRECTION": [ + 1, + [ + 8, + "90" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 493, + "y": 361 + }, + "motion_pointtowards": { + "opcode": "motion_pointtowards", + "next": null, + "parent": null, + "inputs": { + "TOWARDS": [ + 1, + "6xQl1pPk%9E~Znhm*:ng" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 492, + "y": 463 + }, + "motion_pointtowards_menu": { + "opcode": "motion_pointtowards_menu", + "next": null, + "parent": "Ucm$YBs*^9GFTGXCbal@", + "inputs": {}, + "fields": { + "TOWARDS": [ + "_mouse_", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "motion_changexby": { + "opcode": "motion_changexby", + "next": null, + "parent": null, + "inputs": { + "DX": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 851, + "y": -409 + }, + "motion_setx": { + "opcode": "motion_setx", + "next": null, + "parent": null, + "inputs": { + "X": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 864, + "y": -194 + }, + "motion_changeyby": { + "opcode": "motion_changeyby", + "next": null, + "parent": null, + "inputs": { + "DY": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 861, + "y": -61 + }, + "motion_sety": { + "opcode": "motion_sety", + "next": null, + "parent": null, + "inputs": { + "Y": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 864, + "y": 66 + }, + "motion_ifonedgebounce": { + "opcode": "motion_ifonedgebounce", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1131, + "y": -397 + }, + "motion_setrotationstyle": { + "opcode": "motion_setrotationstyle", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "STYLE": [ + "left-right", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 1128, + "y": -287 + }, + "motion_xposition": { + "opcode": "motion_xposition", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1193, + "y": -136 + }, + "motion_yposition": { + "opcode": "motion_yposition", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1181, + "y": -64 + }, + "motion_direction": { + "opcode": "motion_direction", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1188, + "y": 21 + } +} \ No newline at end of file diff --git a/blocks/classwise_blocks/operator_block.json b/blocks/classwise_blocks/operator_block.json new file mode 100644 index 0000000000000000000000000000000000000000..c49fa68fdd17c6d7707fdea8a3eb73a26f2184e0 --- /dev/null +++ b/blocks/classwise_blocks/operator_block.json @@ -0,0 +1,409 @@ +{ + "operator_add": { + "opcode": "operator_add", + "next": null, + "parent": null, + "inputs": { + "NUM1": [ + 1, + [ + 4, + "" + ] + ], + "NUM2": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 128, + "y": 153 + }, + "operator_subtract": { + "opcode": "operator_subtract", + "next": null, + "parent": null, + "inputs": { + "NUM1": [ + 1, + [ + 4, + "" + ] + ], + "NUM2": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 134, + "y": 214 + }, + "operator_multiply": { + "opcode": "operator_multiply", + "next": null, + "parent": null, + "inputs": { + "NUM1": [ + 1, + [ + 4, + "" + ] + ], + "NUM2": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 134, + "y": 278 + }, + "operator_divide": { + "opcode": "operator_divide", + "next": null, + "parent": null, + "inputs": { + "NUM1": [ + 1, + [ + 4, + "" + ] + ], + "NUM2": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 138, + "y": 359 + }, + "operator_random": { + "opcode": "operator_random", + "next": null, + "parent": null, + "inputs": { + "FROM": [ + 1, + [ + 4, + "1" + ] + ], + "TO": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 311, + "y": 157 + }, + "operator_gt": { + "opcode": "operator_gt", + "next": null, + "parent": null, + "inputs": { + "OPERAND1": [ + 1, + [ + 10, + "" + ] + ], + "OPERAND2": [ + 1, + [ + 10, + "50" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 348, + "y": 217 + }, + "operator_lt": { + "opcode": "operator_lt", + "next": null, + "parent": null, + "inputs": { + "OPERAND1": [ + 1, + [ + 10, + "" + ] + ], + "OPERAND2": [ + 1, + [ + 10, + "50" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 345, + "y": 286 + }, + "operator_equals": { + "opcode": "operator_equals", + "next": null, + "parent": null, + "inputs": { + "OPERAND1": [ + 1, + [ + 10, + "" + ] + ], + "OPERAND2": [ + 1, + [ + 10, + "50" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 345, + "y": 372 + }, + "operator_and": { + "opcode": "operator_and", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 701, + "y": 158 + }, + "operator_or": { + "opcode": "operator_or", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 705, + "y": 222 + }, + "operator_not": { + "opcode": "operator_not", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 734, + "y": 283 + }, + "operator_join": { + "opcode": "operator_join", + "next": null, + "parent": null, + "inputs": { + "STRING1": [ + 1, + [ + 10, + "apple " + ] + ], + "STRING2": [ + 1, + [ + 10, + "banana" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 663, + "y": 378 + }, + "operator_letter_of": { + "opcode": "operator_letter_of", + "next": null, + "parent": null, + "inputs": { + "LETTER": [ + 1, + [ + 6, + "1" + ] + ], + "STRING": [ + 1, + [ + 10, + "apple" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 664, + "y": 445 + }, + "operator_length": { + "opcode": "operator_length", + "next": null, + "parent": null, + "inputs": { + "STRING": [ + 1, + [ + 10, + "apple" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 664, + "y": 521 + }, + "operator_contains": { + "opcode": "operator_contains", + "next": null, + "parent": null, + "inputs": { + "STRING1": [ + 1, + [ + 10, + "apple" + ] + ], + "STRING2": [ + 1, + [ + 10, + "a" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 634, + "y": 599 + }, + "operator_mod": { + "opcode": "operator_mod", + "next": null, + "parent": null, + "inputs": { + "NUM1": [ + 1, + [ + 4, + "" + ] + ], + "NUM2": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 295, + "y": 594 + }, + "operator_round": { + "opcode": "operator_round", + "next": null, + "parent": null, + "inputs": { + "NUM": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 307, + "y": 674 + }, + "operator_mathop": { + "opcode": "operator_mathop", + "next": null, + "parent": null, + "inputs": { + "NUM": [ + 1, + [ + 4, + "" + ] + ] + }, + "fields": { + "OPERATOR": [ + "abs", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 280, + "y": 754 + } +} \ No newline at end of file diff --git a/blocks/classwise_blocks/sensing_block.json b/blocks/classwise_blocks/sensing_block.json new file mode 100644 index 0000000000000000000000000000000000000000..30fe1872a91aeb7d75cd3552c51571c07bd43a4c --- /dev/null +++ b/blocks/classwise_blocks/sensing_block.json @@ -0,0 +1,292 @@ +{ + "sensing_touchingobject": { + "opcode": "sensing_touchingobject", + "next": null, + "parent": null, + "inputs": { + "TOUCHINGOBJECTMENU": [ + 1, + "sensing_touchingobjectmenu" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 359, + "y": 116 + }, + "sensing_touchingobjectmenu": { + "opcode": "sensing_touchingobjectmenu", + "next": null, + "parent": "sensing_touchingobject", + "inputs": {}, + "fields": { + "TOUCHINGOBJECTMENU": [ + "_mouse_", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "sensing_touchingcolor": { + "opcode": "sensing_touchingcolor", + "next": null, + "parent": null, + "inputs": { + "COLOR": [ + 1, + [ + 9, + "#55b888" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 360, + "y": 188 + }, + "sensing_coloristouchingcolor": { + "opcode": "sensing_coloristouchingcolor", + "next": null, + "parent": null, + "inputs": { + "COLOR": [ + 1, + [ + 9, + "#d019f2" + ] + ], + "COLOR2": [ + 1, + [ + 9, + "#2b0de3" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 348, + "y": 277 + }, + "sensing_askandwait": { + "opcode": "sensing_askandwait", + "next": null, + "parent": null, + "inputs": { + "QUESTION": [ + 1, + [ + 10, + "What's your name?" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 338, + "y": 354 + }, + "sensing_answer": { + "opcode": "sensing_answer", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 782, + "y": 111 + }, + "sensing_keypressed": { + "opcode": "sensing_keypressed", + "next": null, + "parent": null, + "inputs": { + "KEY_OPTION": [ + 1, + "sensing_keyoptions" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 762, + "y": 207 + }, + "sensing_keyoptions": { + "opcode": "sensing_keyoptions", + "next": null, + "parent": "sensing_keypressed", + "inputs": {}, + "fields": { + "KEY_OPTION": [ + "space", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "sensing_mousedown": { + "opcode": "sensing_mousedown", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 822, + "y": 422 + }, + "sensing_mousex": { + "opcode": "sensing_mousex", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 302, + "y": 528 + }, + "sensing_mousey": { + "opcode": "sensing_mousey", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 668, + "y": 547 + }, + "sensing_setdragmode": { + "opcode": "sensing_setdragmode", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "DRAG_MODE": [ + "draggable", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 950, + "y": 574 + }, + "sensing_loudness": { + "opcode": "sensing_loudness", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 658, + "y": 703 + }, + "sensing_timer": { + "opcode": "sensing_timer", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 459, + "y": 671 + }, + "sensing_resettimer": { + "opcode": "sensing_resettimer", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 462, + "y": 781 + }, + "sensing_of": { + "opcode": "sensing_of", + "next": null, + "parent": null, + "inputs": { + "OBJECT": [ + 1, + "sensing_of_object_menu" + ] + }, + "fields": { + "PROPERTY": [ + "backdrop #", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 997, + "y": 754 + }, + "sensing_of_object_menu": { + "opcode": "sensing_of_object_menu", + "next": null, + "parent": "sensing_of", + "inputs": {}, + "fields": { + "OBJECT": [ + "_stage_", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "sensing_current": { + "opcode": "sensing_current", + "next": null, + "parent": null, + "inputs": {}, + "fields": { + "CURRENTMENU": [ + "YEAR", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 627, + "y": 884 + }, + "sensing_dayssince2000": { + "opcode": "sensing_dayssince2000", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 959, + "y": 903 + }, + "sensing_username": { + "opcode": "sensing_username", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 833, + "y": 757 + } +} \ No newline at end of file diff --git a/blocks/classwise_blocks/sound_block.json b/blocks/classwise_blocks/sound_block.json new file mode 100644 index 0000000000000000000000000000000000000000..8729465e17fd76b972305a90f48fdaf9378b86b1 --- /dev/null +++ b/blocks/classwise_blocks/sound_block.json @@ -0,0 +1,167 @@ +{ + "sound_playuntildone": { + "opcode": "sound_playuntildone", + "next": null, + "parent": null, + "inputs": { + "SOUND_MENU": [ + 1, + "sound_sounds_menu" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 253, + "y": 17 + }, + "sound_sounds_menu": { + "opcode": "sound_sounds_menu", + "next": null, + "parent": "sound_playuntildone and sound_play", + "inputs": {}, + "fields": { + "SOUND_MENU": [ + "Meow", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "sound_play": { + "opcode": "sound_play", + "next": null, + "parent": null, + "inputs": { + "SOUND_MENU": [ + 1, + "sound_sounds_menu" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 245, + "y": 122 + }, + "sound_stopallsounds": { + "opcode": "sound_stopallsounds", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 253, + "y": 245 + }, + "sound_changeeffectby": { + "opcode": "sound_changeeffectby", + "next": null, + "parent": null, + "inputs": { + "VALUE": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": { + "EFFECT": [ + "PITCH", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 653, + "y": 14 + }, + "sound_seteffectto": { + "opcode": "sound_seteffectto", + "next": null, + "parent": null, + "inputs": { + "VALUE": [ + 1, + [ + 4, + "100" + ] + ] + }, + "fields": { + "EFFECT": [ + "PITCH", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 653, + "y": 139 + }, + "sound_cleareffects": { + "opcode": "sound_cleareffects", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 651, + "y": 242 + }, + "sound_changevolumeby": { + "opcode": "sound_changevolumeby", + "next": null, + "parent": null, + "inputs": { + "VOLUME": [ + 1, + [ + 4, + "-10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 645, + "y": 353 + }, + "sound_setvolumeto": { + "opcode": "sound_setvolumeto", + "next": null, + "parent": null, + "inputs": { + "VOLUME": [ + 1, + [ + 4, + "100" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1108, + "y": 5 + }, + "sound_volume": { + "opcode": "sound_volume", + "next": null, + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 1136, + "y": 123 + } +} \ No newline at end of file diff --git a/blocks/embeddings.json b/blocks/embeddings.json new file mode 100644 index 0000000000000000000000000000000000000000..eb494005a9a1ea594a1946f8bfb4fc0e791ba1f2 --- /dev/null +++ b/blocks/embeddings.json @@ -0,0 +1,13392 @@ +[ + { + "name": "8cc0b88d53345b3e337e8f028a32a4e7.png", + "file-path": "E:\\Pratham\\2025\\Harsh Sir\\Scratch Vision\\images\\Backdrops\\badroom3.sb3\\8cc0b88d53345b3e337e8f028a32a4e7.png", + "embeddings": [ + 0.007388541009277105, + -0.002432113280519843, + -0.018262000754475594, + 0.018314851447939873, + -0.014241515658795834, + -0.008655917830765247, + -0.05070235952734947, + 0.00520742405205965, + -0.03931010514497757, + 0.007388440426439047, + -0.00046590823330916464, + -0.00877783726900816, + -0.02261359803378582, + 0.05903354287147522, + -0.004829192999750376, + 0.03454623371362686, + 0.010911782272160053, + -0.03372596949338913, + -0.03272081911563873, + 0.03814464807510376, + 0.0005864477134309709, + -0.02214811183512211, + -0.021732982248067856, + 0.03707899525761604, + 0.018765412271022797, + -0.03370746597647667, + -0.01540293823927641, + 0.0027518721763044596, + -0.00919872336089611, + 0.011088537983596325, + 0.028753435239195824, + -0.005378135945647955, + 0.026549914851784706, + 0.023476645350456238, + 0.015362244099378586, + 0.021737288683652878, + 0.009532863274216652, + -0.009891863912343979, + -0.010189780034124851, + -0.0027929660864174366, + 0.016152461990714073, + 0.02635396271944046, + 0.062934510409832, + 0.03484809771180153, + -0.04996981471776962, + -0.017022671177983284, + 0.04420061409473419, + -0.006293182261288166, + -0.050501272082328796, + 0.0190791804343462, + 0.050034087151288986, + -0.002256764331832528, + 0.02466716431081295, + 0.017113419249653816, + 0.008760465309023857, + 0.031249281018972397, + -0.01870080828666687, + -0.031340815126895905, + -0.039028529077768326, + -0.020178597420454025, + 0.04769154265522957, + -0.047957029193639755, + -0.03689299523830414, + 0.0536482073366642, + 0.0007447211537510157, + -0.020027456805109978, + -0.026418600231409073, + 0.02551780641078949, + 0.014615601859986782, + -0.0432688444852829, + -0.005017177201807499, + 0.03437288478016853, + -0.03332178667187691, + 0.016641419380903244, + -0.059802036732435226, + -0.017785876989364624, + 0.04214446246623993, + 0.0087079843506217, + 0.0015332660404965281, + -0.030220629647374153, + -0.025780560448765755, + -0.036619238555431366, + -0.03411710634827614, + -0.024013858288526535, + 0.004263806156814098, + 0.011622665449976921, + -0.022070232778787613, + 0.0021832026541233063, + 0.010277090594172478, + 0.01528512965887785, + 0.019854873418807983, + -0.0059302253648638725, + -0.02999649941921234, + 0.0005986402975395322, + 0.003795033786445856, + 0.02858702465891838, + -0.035396866500377655, + -0.015232822857797146, + -0.043741632252931595, + -0.010484202764928341, + 0.027292758226394653, + -0.002465130528435111, + 0.04611499607563019, + 0.03428356721997261, + 0.022325290367007256, + 0.008401640690863132, + -0.015616578981280327, + -0.03437208756804466, + 0.016948582604527473, + -0.04181999713182449, + -0.004045654088258743, + 0.023228658363223076, + -0.016802117228507996, + 0.00012689875438809395, + -0.018079956993460655, + -0.0004586362629197538, + 0.007734871469438076, + 0.022401073947548866, + 0.018440665677189827, + 0.06529904156923294, + -0.007433151826262474, + 0.0025693110655993223, + 0.017683906480669975, + 0.032362744212150574, + 0.049913883209228516, + 0.04286918789148331, + 0.0066986423917114735, + 0.01649393141269684, + 0.025250118225812912, + 0.002985243918374181, + -0.0034537510946393013, + -0.002585838083177805, + -0.0122941629961133, + 0.0020243534818291664, + -0.02382548153400421, + 0.001889732200652361, + -0.006667083129286766, + 0.018944820389151573, + 0.04202789068222046, + 0.024395186454057693, + 0.02108028158545494, + -0.021474270150065422, + -0.02890605479478836, + 0.020820684731006622, + -0.02743387036025524, + 0.00871451385319233, + 0.02798018790781498, + -0.016673248261213303, + -0.040372494608163834, + -0.005188914481550455, + 0.035444121807813644, + 0.0030255725141614676, + 0.015314366668462753, + 0.03237350657582283, + 0.022345133125782013, + 0.020149344578385353, + 0.013749559409916401, + -0.005901559721678495, + 0.014151763170957565, + 0.03225656598806381, + 0.02313424088060856, + 0.0216642115265131, + -0.025140514597296715, + -0.013366840779781342, + 0.03253620117902756, + 0.03160512447357178, + -0.01481413934379816, + -0.015300702303647995, + -0.048752520233392715, + 0.017133532091975212, + -0.007630441337823868, + 0.015600454062223434, + -0.006504442077130079, + -0.021568648517131805, + 0.029328208416700363, + 0.03120841272175312, + -0.01580164209008217, + 0.03810209408402443, + -0.012952571734786034, + 0.015173466876149178, + 0.015599967911839485, + 0.0008499331888742745, + 0.021193072199821472, + 0.0030440506525337696, + -0.010503486730158329, + 0.04348098859190941, + 0.025708500295877457, + 0.03384491056203842, + -0.021643683314323425, + 0.018344663083553314, + 0.027282483875751495, + 0.023731330409646034, + 0.023189036175608635, + 0.0009390759514644742, + -0.007141424808651209, + 0.04018590971827507, + -0.039033401757478714, + -0.0034437025897204876, + -0.0051041548140347, + -0.0024253525771200657, + -0.040636882185935974, + 0.03495556116104126, + 0.0509674996137619, + 0.004657163750380278, + -0.056876786053180695, + -0.017442861571907997, + 0.05081117898225784, + -0.0048654270358383656, + 0.03873450681567192, + 0.028330925852060318, + 0.007703171111643314, + 0.03966454789042473, + -0.004428878426551819, + -0.003196421544998884, + 0.010630996897816658, + 0.027605514973402023, + 0.013424727134406567, + -0.02233259379863739, + 0.027913179248571396, + 0.0605810172855854, + -0.1463070660829544, + 0.003506039036437869, + 0.003694328013807535, + -0.05497030168771744, + 0.048687245696783066, + 0.0010174381313845515, + -0.014127468690276146, + 0.003226344706490636, + -0.01798134297132492, + -0.0007521426887251437, + 0.030147423967719078, + 0.02268952503800392, + -0.006514368578791618, + 0.0357959046959877, + 0.028058581054210663, + 0.07070378959178925, + -0.016999077051877975, + -0.024576321244239807, + -0.004061397630721331, + 0.026333674788475037, + -0.012437207624316216, + 0.056660283356904984, + 0.004370560869574547, + -0.0019551750738173723, + -0.0033376109786331654, + 0.00885004736483097, + -0.013133194297552109, + -0.004049898125231266, + -0.05314921960234642, + 0.0028670078609138727, + 0.03717454522848129, + -0.04572910815477371, + 0.002783362288028002, + -0.03268321976065636, + -0.034795016050338745, + -0.006929675117135048, + -0.06364203244447708, + 0.0034370257053524256, + -0.011517790146172047, + -0.061745379120111465, + -0.007711441721767187, + 0.021304354071617126, + -0.041747573763132095, + 0.009104914963245392, + 0.04474478214979172, + 0.013855335302650928, + -0.029296519234776497, + 0.0011284899665042758, + 0.03358781710267067, + -0.030611546710133553, + -0.05347912758588791, + 0.039480872452259064, + 0.042993009090423584, + -0.0029620800632983446, + -0.033388420939445496, + -0.00023047141439747065, + 0.003758922452107072, + 0.018680337816476822, + -0.02801366150379181, + 0.01986638829112053, + 0.035938527435064316, + 0.029141588136553764, + -0.0011534154182299972, + 0.026902105659246445, + 0.008526964113116264, + 0.05549972876906395, + 0.05332263559103012, + 0.0024625235237181187, + 0.03113343007862568, + 0.010126322507858276, + 0.0033309031277894974, + 0.020296864211559296, + -0.05544567108154297, + 0.013150196522474289, + 0.006991229951381683, + -4.331076343078166e-05, + 0.0457397922873497, + 0.0629289448261261, + -0.0005346322432160378, + -0.008329859934747219, + -0.0073872581124305725, + -0.02597980573773384, + 0.008846307173371315, + 0.01926048845052719, + -0.054568659514188766, + 0.07530928403139114, + -0.028014088049530983, + -0.008132644928991795, + 0.005909551866352558, + -0.012481664307415485, + 0.04863356053829193, + 0.04234445095062256, + -0.036007724702358246, + 0.004129552282392979, + 0.042138971388339996, + 0.010277845896780491, + -0.005743447225540876, + 0.0018805640283972025, + 0.025669008493423462, + 0.044377651065588, + -0.010196030139923096, + -0.011702814139425755, + 0.004385293461382389, + -0.05814163759350777, + 0.015890084207057953, + -0.004888380877673626, + -0.03429817408323288, + -0.005255511496216059, + -0.033594727516174316, + -0.02039336785674095, + -0.014051511883735657, + -0.01706155575811863, + 0.03603300824761391, + 0.05483494699001312, + -0.0071287075988948345, + -0.004563222639262676, + -0.028948811814188957, + -0.009792628698050976, + 0.008953436277806759, + 0.006737087853252888, + 0.058281343430280685, + 0.005540140904486179, + 0.022297823801636696, + 0.031277984380722046, + -0.016280828043818474, + 0.006875600200146437, + 0.042734771966934204, + -0.044580038636922836, + -0.010194176807999611, + 0.02334941178560257, + -0.03149360045790672, + 0.01979236491024494, + -0.009999854490160942, + 0.011195274069905281, + 0.015376521274447441, + -0.02385328710079193, + -0.018348615616559982, + 0.026158466935157776, + -0.003332048188894987, + 0.012919967994093895, + -0.025772297754883766, + -0.12262434512376785, + 0.005425973329693079, + -0.026725763455033302, + 0.050293222069740295, + 0.03113563172519207, + -0.007862845435738564, + 0.01798270456492901, + -0.021434247493743896, + -0.01683305762708187, + 0.0007929705898277462, + -0.009610442444682121, + 0.02017207443714142, + 0.00624814722687006, + 0.022473275661468506, + 0.017394164577126503, + -0.04001498222351074, + -0.04115486145019531, + 0.004615727346390486, + -0.03880703076720238, + 0.04073873534798622, + 0.046226270496845245, + -0.06392012536525726, + 0.02324852906167507, + 0.020879531279206276, + 0.018863072618842125, + -0.020692940801382065, + 0.027004368603229523, + -0.031133878976106644, + 0.016743192449212074, + 0.04781945049762726, + 0.019248181954026222, + -0.018585538491606712, + -0.02267843671143055, + -0.0061914655379951, + 0.042253341525793076, + -0.02088421955704689, + -0.008004045113921165, + 0.06568654626607895, + 0.027881264686584473, + 0.04141107201576233, + -0.025637423619627953, + -0.03325702250003815, + -0.055227965116500854, + -0.04151436313986778, + 0.027435991913080215, + -0.02676714025437832, + 0.08136159926652908, + -0.009612590074539185, + -0.035916537046432495, + 0.017236847430467606, + 0.029609382152557373, + 0.03407468646764755, + -0.04367892071604729, + 0.01993528939783573, + -0.013954496011137962, + -0.01898079551756382, + -0.0007723680464550853, + -0.007968206889927387, + -0.019065113738179207, + -0.0033272975124418736, + 0.001159839564934373, + 0.027865493670105934, + 0.021043049171566963, + -0.027947504073381424, + -0.028931081295013428, + 0.03406541794538498, + -0.025251997634768486, + -0.022626055404543877, + -0.05480170249938965, + 0.01719987578690052, + 0.04518219828605652, + 0.02095942199230194, + 0.021669194102287292, + 0.012609275989234447, + -0.056780073791742325, + -0.024058550596237183, + -0.004922788590192795, + 0.011296208947896957, + -0.016050588339567184, + -0.01444441918283701, + -0.028159014880657196, + -0.015256751328706741, + 0.03272555395960808, + 0.03484953194856644, + -0.01366571057587862, + -0.06427065283060074, + -0.006890357006341219, + -0.02783387340605259, + 0.014095832593739033, + 0.017727859318256378, + 0.021182788535952568, + 0.022473333403468132, + -0.05749524012207985, + 0.01063565444201231, + -0.07466845214366913, + 0.016868339851498604, + 0.18724896013736725, + 0.018859175965189934, + 0.015140269882977009, + 0.010398438200354576, + -0.04415823519229889, + 0.054827991873025894, + -0.0027966550551354885, + 0.05219243839383125, + -0.02783328667283058, + -0.042647216469049454, + -0.004565225448459387, + 0.035043444484472275, + -0.021320195868611336, + 0.026305723935365677, + -0.0140383280813694, + 0.09041514247655869, + 0.033366959542036057, + -0.0016736198449507356, + -0.0016241230769082904, + 0.016735859215259552, + -0.028188109397888184, + 0.009271548129618168, + 0.0057258401066064835, + 0.01340339332818985, + 0.030153656378388405, + 0.031018216162919998, + 0.027731655165553093, + 0.032357487827539444, + 0.006386386696249247, + -0.033633142709732056, + 0.036933887749910355, + 0.040110986679792404, + 0.026968615129590034, + 0.020563969388604164, + -0.005229279864579439, + -0.01584744080901146, + 0.02980222925543785, + 0.0008245370117947459, + -0.038224685937166214, + -0.017561377957463264, + -0.002425978658720851, + 0.016926998272538185, + 0.019738472998142242, + 0.01930125057697296, + 0.02825159952044487, + 0.0721684992313385, + -0.011994095519185066, + -0.024875346571207047, + 0.04127094894647598, + -0.023036625236272812, + -0.03845673054456711, + 0.015703421086072922, + 0.0432002879679203, + 0.0864180251955986, + -0.012548976577818394, + -0.037710655480623245, + 0.017457937821745872, + -0.028650054708123207, + 0.030031967908143997, + 0.009309292770922184, + 0.027823863551020622, + 0.03707723319530487, + -0.03337941691279411, + 0.011749261990189552, + -0.026216870173811913, + 0.00845775380730629, + 0.020542152225971222, + 0.01394155714660883, + -0.04947460815310478, + -0.030398504808545113, + 0.012675384059548378, + 0.04551458731293678, + 0.024235054850578308, + -0.012436640448868275, + -0.025392521172761917, + 0.0019174538319930434, + -0.015019918791949749, + -0.019214631989598274, + -0.054642099887132645, + 0.01749381050467491, + -0.008864457719027996, + -0.009033290669322014, + -0.02864772267639637, + -0.029091235250234604, + -0.0018076231935992837, + -0.12030182033777237, + -0.008875814266502857, + -0.012756402604281902, + 0.010816813446581364, + 0.004621803294867277, + 0.002291914774104953, + -0.0066718412563204765, + 0.010358292609453201, + -0.019510561600327492, + 0.05633874237537384, + 0.01732783205807209, + -0.05088362470269203, + -0.015530327335000038, + 0.014258780516684055, + 0.06481587886810303, + -0.016122376546263695, + -0.020665356889367104, + 0.03958164155483246, + -0.01482467446476221, + -0.03193671256303787, + -0.04202530160546303, + -0.02067154087126255, + -0.02257068082690239, + 0.02640766091644764, + 0.006579952780157328, + -0.00034656657953746617, + 0.014028443023562431, + 0.03196113184094429, + 0.006274815183132887, + -0.009096723981201649, + 0.016007758677005768, + 0.012376186437904835, + -0.006253180094063282, + -0.019749542698264122, + 0.019436616450548172, + 0.013328087516129017, + -0.00415181927382946, + -0.028215331956744194, + -0.04721774160861969, + -0.047384586185216904, + -0.05118990316987038, + 0.05362527817487717, + -0.03439867123961449, + -0.004855682607740164, + 0.004437355790287256, + -0.021179499104619026, + -0.06652089953422546, + -0.013398930430412292, + -0.05225693807005882, + 0.027289511635899544, + 0.011779514141380787, + 0.04742240533232689, + 0.004553484730422497, + 0.014250644482672215, + -0.013698230497539043, + 0.04523259401321411, + 0.016883332282304764, + -0.06618540734052658, + 0.017195628955960274, + 0.010188980959355831, + 0.006641887594014406, + 0.029577506706118584, + -0.03186742216348648, + 0.01901751011610031, + -0.0037286446895450354, + -0.04861173406243324, + 0.016436506062746048, + 0.02678714506328106, + 0.016786003485322, + -0.008248032070696354, + -0.029767725616693497, + 0.007806097157299519, + 0.009078901261091232, + -0.012638438493013382, + 0.0007754892576485872, + -0.0012019394198432565, + 0.03435845300555229, + -0.010524732060730457, + -0.02435356378555298, + -0.014754971489310265, + -0.005730078089982271, + 0.012912211939692497, + 0.022541077807545662, + 0.004904234316200018, + -0.0922498106956482, + 0.0017269403906539083, + 0.015512831509113312, + 0.009832444600760937, + 0.0003029931103810668, + -0.10805560648441315, + -0.028361255303025246, + -0.0013769223587587476, + -0.015805751085281372, + -0.006201367825269699, + -0.016846349462866783, + 0.004565571900457144, + 0.015113873407244682, + 0.01981973461806774, + -0.0033971681259572506, + -0.07385232299566269, + 0.0020717615261673927, + 0.09382731467485428, + 0.0005624617333523929, + 0.030566954985260963, + -0.002226582495495677, + -0.023838136345148087, + -0.12846410274505615, + -0.005957075860351324, + -0.002031264826655388, + -0.08940962702035904, + 0.03737229108810425, + 0.10179851204156876, + 0.004776171874254942, + -0.04708043485879898, + -0.019175570458173752, + -0.03959598392248154, + -0.014474969357252121, + -0.021473949775099754, + -0.03368239849805832, + 0.02481233887374401, + -0.036135513335466385, + -0.0008754536393098533, + -0.009967106394469738, + 0.010329989716410637, + 0.01698560267686844, + 0.03003249689936638, + -0.007348306942731142, + -0.004141852725297213, + 0.058512862771749496, + 0.011075508780777454, + -0.0027885849121958017, + 0.044011108577251434, + 0.017632953822612762, + 0.046599868685007095, + -0.052232567220926285, + 0.01591498591005802, + -0.01308209914714098, + -0.008777214214205742, + 0.004510295111685991, + 0.07319235801696777, + 0.011999042704701424, + -0.019189169630408287, + -0.004832123406231403, + -0.012594370171427727, + 0.010241282172501087, + -0.03365425392985344, + -0.0036789223086088896, + -0.017254820093512535, + 0.04545308277010918, + -0.046557653695344925, + -0.02896164543926716, + 0.09708438813686371, + 0.00410539610311389, + -0.07812206447124481, + 0.037459537386894226, + 0.024198653176426888, + 0.004187518265098333, + -0.05911486968398094, + 0.04819956794381142, + -0.021862978115677834, + -0.03903098404407501, + -0.016113346442580223, + -0.004332039505243301, + 0.029068296775221825, + 0.0069262622855603695, + 0.012893876060843468, + 0.009998240508139133, + -0.01712396927177906, + 0.02103053405880928, + 0.007403647992759943, + -0.03626503422856331, + -0.012246830388903618, + -0.018018025904893875, + -0.00722519401460886, + -0.014934361912310123, + 0.006318210624158382, + -0.0073738666251301765, + 0.004842646420001984, + -0.0018219356425106525, + 0.011158837005496025, + -0.020879119634628296, + -0.0009790518088266253, + -0.04546676576137543, + 0.0032668341882526875, + -0.03355897217988968, + -0.020416025072336197, + -0.029820669442415237, + 0.029132777824997902, + -0.0040029012598097324, + 0.030202534049749374, + 0.03312332555651665, + -0.01333322748541832, + 0.009666664525866508, + 0.07880251854658127, + 0.015068558976054192, + 0.03321102634072304, + -0.015623810701072216, + -0.024978039786219597, + -0.00686959084123373, + 0.026587197557091713, + -0.014474369585514069, + -0.05110203102231026, + 0.013628915883600712, + 0.03543096408247948, + 0.022935297340154648, + 0.010921129025518894, + -0.004187013953924179, + 0.01800990104675293, + 0.0252201147377491, + -0.011255324818193913, + 0.0058847833424806595, + -0.06569631397724152, + 0.015362448990345001, + -0.16992640495300293, + -0.031763073056936264, + 0.032420381903648376, + -0.01280074194073677, + -0.023082895204424858, + -0.006933304015547037, + 0.048602987080812454, + 0.046395592391490936, + 0.02191818319261074, + -0.002257897984236479, + 0.015045654028654099, + -0.033644307404756546, + 0.05623554810881615, + 0.03087870217859745, + 0.011422230862081051, + -0.05026531592011452, + 0.025947220623493195, + 0.03595378249883652, + 0.056761354207992554, + -0.02094561792910099, + 0.07505766302347183, + 0.018292373046278954, + -0.058755625039339066, + 0.0065858932211995125, + 0.037021614611148834, + 0.006287820637226105, + -0.0029484799597412348, + 9.723947732709348e-05, + -0.015444635413587093, + -0.01181673351675272, + -0.007309931796044111, + -0.014741851016879082, + -0.005039694719016552, + -0.0018409871263429523, + -0.0004640010301955044, + 0.09365638345479965, + 0.04797295480966568, + 0.01213198620826006, + -0.06579457968473434, + -0.036293432116508484, + 0.005236395169049501, + -0.010924022644758224, + 0.046289581805467606, + 0.02987007424235344, + 0.00685732951387763, + -0.02591264434158802, + 0.01056625321507454, + -0.01572599448263645, + 0.03210019692778587, + 0.019047901034355164, + -0.02738930471241474, + 0.005347046535462141, + -0.0464765727519989, + -0.05332859978079796, + 0.008335798978805542, + 0.04524742066860199, + -0.021407518535852432, + 0.002755864290520549, + 0.009953542612493038, + 0.04261631518602371, + -0.06728505343198776, + -0.029096240177750587, + -0.048889387398958206, + -0.002373133786022663, + -0.005584873724728823, + -0.023973722010850906, + 0.020633993670344353, + -0.021408764645457268, + -0.02736700139939785, + 0.0210132896900177, + 0.012478196062147617, + 0.007381657138466835, + -0.01569151133298874, + 0.007762829773128033, + 0.021264612674713135, + -0.01639731228351593, + -0.0067575061693787575, + -0.034191641956567764, + 0.04134446382522583, + -0.017706746235489845, + 0.009674884378910065, + 0.028125984594225883, + -0.019767193123698235, + 0.005227801389992237, + -0.0008677990408614278, + -0.049649421125650406, + -0.0038925104308873415, + -0.02156895585358143, + 0.0013921549543738365, + -0.004628687631338835, + 0.022242268547415733, + 0.009635246358811855, + 0.006699681747704744, + 0.012997928075492382, + 0.03824338689446449, + -0.026453262194991112, + -0.008391324430704117, + 0.06000092253088951, + -0.06840293109416962, + 0.03952508792281151, + -0.014332780614495277, + 0.025161931291222572, + 0.014644738286733627, + -0.02598893642425537, + -0.0038814505096524954, + 0.008916658349335194, + 0.03754161298274994, + -0.014970030635595322, + -0.033667583018541336, + 0.02172589860856533, + 0.0024626976810395718, + -0.03452729061245918, + -0.06168972700834274, + -0.0016940481727942824, + -0.06048371642827988, + -0.005924480967223644, + 0.004384916741400957, + -0.008128098212182522, + -0.03701617941260338, + -0.030303670093417168, + 0.013657459057867527, + -0.016343116760253906, + 0.015390578657388687, + 0.023101238533854485, + -0.04311436787247658, + -0.02318604476749897, + -0.039138950407505035, + -0.018649213016033173, + 0.02603953331708908, + -0.003948085010051727, + -0.004286197014153004, + -0.012338793836534023, + -0.007880635559558868, + 0.009319736622273922, + 0.012436138466000557, + -0.006100854370743036, + 0.014329856261610985, + -0.0024139825254678726, + 0.02036679908633232, + -0.012076443992555141, + -0.006835149601101875, + 0.007628175895661116, + 0.06136320158839226, + 0.03353876248002052, + -0.00242019584402442, + -0.001767092733643949, + -0.010919881984591484, + 0.011894234456121922, + -0.011672665365040302, + 0.018929988145828247, + -0.02438575215637684, + 0.04422333091497421, + -0.02359822951257229, + 0.02845284342765808, + -0.023007012903690338, + -0.014472468756139278, + -0.01998523809015751, + 0.03789285197854042, + 0.01138534490019083, + -0.008378801867365837, + -0.03282729908823967, + 0.016505960375070572, + 0.037558574229478836, + 0.02056802622973919, + -0.022750677540898323, + -0.008339711464941502, + 0.03147060051560402, + 0.021029651165008545, + -0.02873203158378601, + 0.0011072400957345963, + 0.008926772512495518, + 0.0002754168235696852, + 0.020341718569397926, + 0.039763182401657104, + -0.014687256887555122, + 0.003706481773406267, + 0.027052009478211403, + -0.020960699766874313, + 0.02296583540737629, + -0.023268405348062515, + -0.0209114421159029, + 0.021586962044239044, + 0.012872870080173016, + 0.0046834275126457214, + -0.004303022287786007, + 0.0026905303820967674, + 0.03202376514673233, + 0.08940836787223816, + 0.027041113004088402, + 0.019368883222341537, + 0.018561705946922302, + 0.008416617289185524, + -0.04576999321579933, + 0.004752469714730978, + 0.00012034566316287965, + -0.020158324390649796, + -0.023259568959474564, + 0.052016500383615494, + -0.01267529558390379, + -0.021250706166028976, + 0.0018661573994904757, + -0.023392051458358765, + 0.07952087372541428, + 0.025573991239070892, + 0.03026130422949791, + 0.03263990208506584, + 0.0001702569570625201, + 0.01792987994849682, + 6.495741399703547e-05, + 0.03424385190010071, + 0.03551917523145676, + 0.004282842390239239, + 0.03577355295419693, + 0.014330203644931316, + -0.0265219546854496, + 0.002709422493353486, + 0.021439209580421448, + -0.003945700358599424, + -0.031523093581199646, + 0.015954887494444847, + -0.007460663560777903, + 0.008589833043515682, + -0.018103167414665222, + 0.01240358967334032, + 0.013369454070925713, + 0.031167233362793922, + -0.03005516529083252, + 0.01889786683022976, + 0.01886548101902008, + 0.0008011213503777981, + -0.0630202516913414, + 0.029564321041107178, + 0.035517219454050064, + -0.01938832364976406, + -0.006216829642653465, + -0.02278832532465458, + 0.0008671037503518164, + 0.034555964171886444, + -0.036528103053569794, + -0.013350353576242924, + 0.0010797021677717566, + 0.019242599606513977, + -0.006748594809323549, + 0.023117929697036743, + -0.03637746348977089, + -0.0039992439560592175, + -0.007458226289600134, + 0.012168493121862411, + -0.0054365345276892185, + 0.008374894969165325, + 0.04165509343147278, + -0.01835646852850914, + 0.003489171387627721, + -0.007567259017378092, + -0.012375809252262115, + 0.00286936410702765, + 0.02904091589152813, + 0.04181958734989166, + 0.039468564093112946, + 0.029064031317830086, + -0.039346322417259216, + 0.004456887487322092, + -0.014241005294024944, + 0.041562434285879135, + -0.002803075360134244, + -0.04017805680632591, + -0.006691767368465662, + -0.05976055935025215, + -0.03799454867839813 + ] + }, + { + "name": "7be1f5b3e682813dac1f297e52ff7dca.png", + "file-path": "E:\\Pratham\\2025\\Harsh Sir\\Scratch Vision\\images\\Backdrops\\baseball2.sb3\\7be1f5b3e682813dac1f297e52ff7dca.png", + "embeddings": [ + 0.010486210696399212, + 0.021611923351883888, + -0.030085274949669838, + 0.059395931661129, + -0.01050077099353075, + 0.036698102951049805, + -0.04251910001039505, + -0.021452557295560837, + 0.023046238347887993, + 0.009626209735870361, + -0.0009053472895175219, + -0.025530410930514336, + 0.003773217322304845, + 0.0471394881606102, + 0.07534180581569672, + 0.02608339674770832, + 0.0030681148637086153, + -0.02631881833076477, + -0.00296188285574317, + 0.008260204456746578, + 0.09446211904287338, + 0.01685985177755356, + -0.008006270043551922, + -0.04334443062543869, + 0.019919637590646744, + 0.02033155970275402, + -0.010506105609238148, + 0.01728118397295475, + -0.031142473220825195, + 0.030159877613186836, + -0.010733340866863728, + -0.006965949200093746, + -0.017991961911320686, + 0.012241978198289871, + 0.0009805767331272364, + 0.006605407688766718, + 0.010441567748785019, + 0.012045839801430702, + -0.024239683523774147, + 0.0369296632707119, + -0.007618173025548458, + -0.022979391738772392, + -0.0013674177462235093, + 0.030210983008146286, + -0.08278559148311615, + -0.01628199592232704, + 0.024018213152885437, + 0.08471119403839111, + -0.028717895969748497, + 0.021243959665298462, + 0.020611966028809547, + 0.007328397128731012, + 0.025658700615167618, + 0.0020350308623164892, + -0.004773302935063839, + 0.02650916390120983, + -0.023543119430541992, + -0.013034839183092117, + -0.011324531398713589, + 0.02112264186143875, + 0.016164662316441536, + -0.04939118027687073, + -0.024768875911831856, + -0.009400523267686367, + 0.0054322355426847935, + 0.044506050646305084, + 0.00498443515971303, + 0.01559565868228674, + -0.03560661897063255, + 0.02046278491616249, + 0.0022618763614445925, + -0.0034667954314500093, + -0.012245992198586464, + 0.02904331684112549, + -0.03443422168493271, + 0.008356953039765358, + 0.0725182443857193, + -0.002726004458963871, + -0.004808763042092323, + -0.013824469409883022, + 0.012678799219429493, + -0.02899709902703762, + -0.004220389761030674, + -0.00638034800067544, + 0.046317510306835175, + 0.0012276261113584042, + 0.000452201726147905, + 0.020056916400790215, + -0.008903895504772663, + -0.014137919060885906, + 0.014719648286700249, + -0.0177853312343359, + 0.013055690564215183, + 0.013102042488753796, + 0.020399603992700577, + 0.01431029662489891, + -0.023974526673555374, + -0.0319475494325161, + -0.0038200467824935913, + 0.02225746028125286, + 0.01305592805147171, + 0.003401146037504077, + -0.02396506816148758, + 0.024509519338607788, + -0.012371556833386421, + -0.004949962720274925, + 0.018640050664544106, + -0.022011633962392807, + 0.024495406076312065, + 0.018966393545269966, + -0.02883126400411129, + 0.014332308433949947, + 0.07561653852462769, + -0.003983777016401291, + 0.01303886342793703, + -0.003916457295417786, + 0.012504341080784798, + -0.004865025170147419, + -0.03644884005188942, + -0.010742549784481525, + -0.01395855750888586, + 0.07181224226951599, + 0.006411182694137096, + 0.06283369660377502, + 0.006068091839551926, + -0.017116544768214226, + 0.02842845767736435, + 0.035504695028066635, + 0.0026781391352415085, + 0.018820933997631073, + -0.023054905235767365, + 0.01402322854846716, + -0.012292609550058842, + -0.01878592185676098, + -0.0661611557006836, + 0.02459707483649254, + 0.02179255150258541, + -0.024180691689252853, + 0.013031682930886745, + 0.001594441244378686, + -0.013025752268731594, + 0.03349106013774872, + 0.02589336410164833, + 0.05323624983429909, + 0.028485823422670364, + -0.010856887325644493, + 0.0027054306119680405, + -0.037031516432762146, + -0.00018041774455923587, + 0.015040322206914425, + 0.019775111228227615, + -0.0048591746017336845, + 0.017728015780448914, + 0.025849513709545135, + -0.021412022411823273, + 0.03527575358748436, + -0.05595022067427635, + -0.0194229893386364, + -0.02324688248336315, + -0.008589666336774826, + -0.02285703644156456, + 0.007883693091571331, + 0.014055063016712666, + -0.0027439119294285774, + -0.005400561261922121, + 0.01529822126030922, + -0.03642560914158821, + 0.022711660712957382, + 0.023904811590909958, + -0.01644972153007984, + 0.007680385839194059, + 0.000877725484315306, + -0.020926350727677345, + 0.008376553654670715, + -0.004716628231108189, + -0.01822049356997013, + -0.01460682600736618, + 0.011556294746696949, + 0.014440363273024559, + 0.033252764493227005, + -0.03950478136539459, + -0.0002152573288185522, + 0.008476118557155132, + -0.03672749921679497, + -0.02467534877359867, + 0.013558710925281048, + 0.007831462658941746, + -0.0048836576752364635, + -0.04395309090614319, + -0.0249282568693161, + -0.022599605843424797, + 0.02440740168094635, + 0.013682757504284382, + 0.022247713059186935, + -0.05052562803030014, + -0.02673119306564331, + 0.006845800671726465, + 0.014831272885203362, + 0.02024860866367817, + 0.020985960960388184, + 0.012575343251228333, + 0.02600429579615593, + 0.0067587681114673615, + 0.021610498428344727, + -0.02639755606651306, + 0.0019678622484207153, + 0.013460480608046055, + 0.03299008682370186, + 0.043117739260196686, + 0.01139261294156313, + -0.01274032797664404, + 0.008792165666818619, + -0.004137005191296339, + 0.005567480809986591, + 0.032268304377794266, + 0.0056382338516414165, + 0.04923730343580246, + -0.006612051278352737, + -0.0032515302300453186, + 0.0383349247276783, + -0.13259443640708923, + -0.03265083581209183, + 0.0027796917129307985, + -0.028065618127584457, + 0.009359591640532017, + -0.0024649675469845533, + -0.01706397347152233, + 0.008928980678319931, + 0.010859224945306778, + -0.010284172371029854, + 0.06158749386668205, + 0.03238018974661827, + -0.0011062868870794773, + 0.017589759081602097, + 0.0048676542937755585, + 0.04248925670981407, + 0.007822242565453053, + -0.03046577237546444, + -0.030070440843701363, + -0.027807267382740974, + 0.0033634279388934374, + 0.02951980009675026, + -0.004036244470626116, + -0.00722935376688838, + -0.010509065352380276, + -0.01979505643248558, + -0.013306730426847935, + -0.0182667076587677, + 0.010596293024718761, + -0.003088241908699274, + 0.08118517696857452, + -0.036838725209236145, + 0.0001838868047343567, + -0.04629507288336754, + -0.05864536389708519, + -0.01372317224740982, + -0.006419289391487837, + 0.00036468342295847833, + 0.021834824234247208, + -0.037503115832805634, + 0.04346788302063942, + -0.008186346851289272, + -0.019940055906772614, + -0.027779318392276764, + 0.02309677004814148, + 0.03623083233833313, + 0.0075997840613126755, + -0.027164898812770844, + 0.0071486118249595165, + 0.013467236422002316, + -0.03641057759523392, + 0.02169397659599781, + -0.004737867973744869, + -0.034306906163692474, + -0.005992366466671228, + -0.005164547357708216, + 0.01235934253782034, + -0.016389260068535805, + 0.01804860681295395, + -0.004956608638167381, + 0.006230994127690792, + -0.009607922285795212, + -0.00990560557693243, + 0.004784476011991501, + -0.01787060685455799, + 0.008776838891208172, + -0.020585544407367706, + -0.03352975472807884, + -0.024440325796604156, + 0.044015780091285706, + -0.015362760983407497, + 0.016220616176724434, + 0.028238004073500633, + -0.04117768630385399, + 0.005191251635551453, + -0.03742098808288574, + -0.005100585520267487, + 0.042720239609479904, + -0.015685945749282837, + 0.014468037523329258, + -0.004525847267359495, + 0.026788178831338882, + 0.0018184336367994547, + -0.062379106879234314, + -0.033358875662088394, + -0.008693614043295383, + -0.026876838877797127, + -0.001261449884623289, + 0.023167911916971207, + 0.013724170625209808, + 0.031144458800554276, + 0.017559673637151718, + 0.004652594216167927, + 0.026185547932982445, + 0.04319395124912262, + -0.017521092668175697, + -0.009750288911163807, + -0.011133677326142788, + 0.03642053157091141, + 0.005496159195899963, + -0.019994797185063362, + 0.03368450701236725, + 0.004037973936647177, + 0.018617473542690277, + -0.014846536330878735, + 0.029539460316300392, + -0.11859133839607239, + -0.031278662383556366, + 0.010400783270597458, + 0.02290993183851242, + -0.00901945773512125, + 0.01599138230085373, + 0.03191005811095238, + -0.009340503253042698, + -0.01770646497607231, + 0.014797661453485489, + -0.0287458598613739, + -0.05387270450592041, + -0.03186573460698128, + -0.030520690605044365, + 0.027917880564928055, + -0.021663347259163857, + 0.03183745592832565, + -0.03741815686225891, + -0.0018411340424790978, + 0.011408036574721336, + 0.014403357170522213, + -0.03055405803024769, + 0.04156197980046272, + -0.012174665927886963, + -0.007925418205559254, + -0.00801943987607956, + 0.025109004229307175, + -0.01625150442123413, + -0.0028359165880829096, + -0.01564931496977806, + 0.05895188823342323, + 0.0381900854408741, + -0.010245554149150848, + 0.026962172240018845, + -0.016941482201218605, + -0.010428821668028831, + -0.019525326788425446, + -0.008256466127932072, + -0.005207936745136976, + -0.021833844482898712, + -0.019188404083251953, + 0.009416508488357067, + 0.024906495586037636, + -0.018629061058163643, + -0.008938518352806568, + -0.015462559647858143, + -0.010289211757481098, + 0.005794847849756479, + 0.014155625365674496, + 0.03629007562994957, + -0.03623064607381821, + -0.01590552181005478, + 0.026375891640782356, + -0.05541900917887688, + 0.019276881590485573, + 0.13379880785942078, + -0.09701324999332428, + 0.00021108963119331747, + -0.019962016493082047, + 0.02644808404147625, + 0.048864707350730896, + 0.03847884759306908, + -0.09301217645406723, + 0.018388181924819946, + -0.012546064332127571, + -0.007474065758287907, + -0.03880305215716362, + -0.026369759812951088, + -0.008697827346622944, + 0.03259287402033806, + -0.025683438405394554, + -0.003151406068354845, + 0.026483207941055298, + -0.023485271260142326, + 0.02347281202673912, + 0.006131946109235287, + 0.017855489626526833, + -0.0012559014139696956, + 0.009391417726874352, + -0.030123906210064888, + -0.020413430407643318, + 0.05309244617819786, + -0.02488074079155922, + -0.0253620482981205, + -0.02941676788032055, + 0.02979646995663643, + -0.0012963408371433616, + 0.016674760729074478, + -0.006108936388045549, + -0.021192744374275208, + 0.05995721369981766, + -0.04917282611131668, + -0.010433667339384556, + 0.015084675513207912, + 0.03206532076001167, + -0.011344658210873604, + 0.013480493798851967, + 0.020725462585687637, + -0.03571099787950516, + -0.012086538597941399, + -0.01617533527314663, + 0.006639431230723858, + -0.03565462306141853, + -0.02580251917243004, + -0.036844898015260696, + 0.005030975677073002, + 0.03491251543164253, + 0.0009851112263277173, + -0.008525479584932327, + -0.046196065843105316, + -0.02085476741194725, + -0.012239070609211922, + 0.010300273075699806, + 0.03335189074277878, + 0.006528082303702831, + -0.05957746505737305, + -0.004457662347704172, + -0.001363571034744382, + 0.011301900260150433, + 0.013940883800387383, + -0.025145400315523148, + -0.018083304166793823, + -0.03604428842663765, + -0.07627573609352112, + 0.05212917923927307, + -0.025896450504660606, + -0.050933536142110825, + -0.005113003309816122, + 0.03865918144583702, + -0.00012743554543703794, + -0.04616813734173775, + 0.3264164328575134, + -0.0030991933308541775, + -0.00955990795046091, + 0.01447561290115118, + -0.011923862621188164, + -0.04750983789563179, + -0.00847054272890091, + 0.027498411014676094, + -0.009911838918924332, + -0.02962067537009716, + -0.02844419702887535, + -0.017943065613508224, + -0.010082951746881008, + 0.016420086845755577, + 0.0045626526698470116, + 0.05309230089187622, + 0.04256332293152809, + -0.0018474137177690864, + -0.07098856568336487, + -0.0019794038962572813, + -0.008582754991948605, + -0.025297796353697777, + 0.0023486039135605097, + 0.0411306731402874, + 0.014186190441250801, + -0.027216359972953796, + -0.011815264821052551, + -0.014089928939938545, + 0.010415468364953995, + -0.019689856097102165, + -0.011238939128816128, + 0.025604993104934692, + -0.019044600427150726, + -0.04990880563855171, + 0.00946030393242836, + -0.02498011849820614, + -0.013076363131403923, + 0.01728982664644718, + 0.04849497973918915, + 0.014247288927435875, + -0.009688403457403183, + 0.019691545516252518, + -0.019010158255696297, + -0.008497589267790318, + -0.016617722809314728, + 0.012914467602968216, + 0.00809599831700325, + 0.0075035239569842815, + 0.07307519018650055, + -0.02141706459224224, + -0.032781366258859634, + 0.00835574883967638, + 0.01939173974096775, + -0.024505656212568283, + -0.031716328114271164, + -0.04268934205174446, + 0.03459591045975685, + 0.0009790932526811957, + -0.03320164605975151, + -0.027221359312534332, + -0.012870692647993565, + -0.013676555827260017, + 0.0015522725880146027, + -0.007868405431509018, + -0.0036138505674898624, + -0.0010183623526245356, + -0.004984026774764061, + -0.008697318844497204, + -0.006317608058452606, + -0.010483276098966599, + 0.024111414328217506, + 0.0347333662211895, + 0.023475948721170425, + 0.02027660608291626, + -0.03498991206288338, + 0.026751412078738213, + -0.015602534636855125, + -0.03473483398556709, + 0.01137040089815855, + 0.029001303017139435, + -0.027881119400262833, + -0.033668145537376404, + -0.017290540039539337, + 0.04869122430682182, + 0.015754232183098793, + -0.018335340544581413, + -0.02000625804066658, + -0.025843476876616478, + -0.024256551638245583, + 0.03913091495633125, + -0.027115240693092346, + 0.012139804661273956, + -0.042427558451890945, + -0.03329859673976898, + 0.0402953214943409, + 0.02539951354265213, + 0.015350372530519962, + 0.04654766619205475, + -0.0011823030654340982, + 0.0013454270083457232, + -0.010216317139565945, + -0.03553961589932442, + 0.06170719116926193, + 0.004426367115229368, + 0.003234757576137781, + -0.03499135747551918, + -0.015399429015815258, + 0.028327282518148422, + 0.04630134627223015, + -0.02925995923578739, + -0.0072952862828969955, + 0.03800898417830467, + 0.019364746287465096, + -0.011406755074858665, + 0.027614623308181763, + 0.03471197560429573, + -0.00479379016906023, + 0.0010741426376625896, + -0.03486309573054314, + -0.0018771091708913445, + -0.008457123301923275, + -0.010315228253602982, + -0.045672208070755005, + -0.017014287412166595, + -0.031204767525196075, + -0.03544241935014725, + 0.07503098249435425, + 0.033298373222351074, + -0.039096567779779434, + -0.031858183443546295, + 0.025167640298604965, + -0.030314283445477486, + -0.03302077576518059, + 0.005447611212730408, + 0.004025828558951616, + -0.001498651341535151, + -0.005558285862207413, + 0.020923007279634476, + 0.005945476237684488, + 0.0005868730368092656, + -0.011836851947009563, + 0.009828931652009487, + -0.016151877120137215, + -0.0018064879113808274, + 0.004517611116170883, + -0.0012625347590073943, + 0.006374101620167494, + -0.003571124514564872, + 0.010118926875293255, + 0.03081604838371277, + -0.034047920256853104, + 0.02402156963944435, + -0.010596141219139099, + -0.031394414603710175, + -0.030780136585235596, + 0.0033885736484080553, + 0.052736762911081314, + 0.02575208991765976, + 0.01103428564965725, + 0.008382834494113922, + 0.022232336923480034, + -0.025307413190603256, + -0.0028881460893899202, + -0.0137791121378541, + -0.002904303604736924, + 0.0041963220573961735, + -0.02906855009496212, + -0.02567007578909397, + 0.021513739600777626, + 0.011991718783974648, + -0.005595938302576542, + -0.006923256907612085, + 0.009152522310614586, + -0.014838790521025658, + -0.08548229932785034, + -0.00902607012540102, + 0.0020298566669225693, + -0.008858314715325832, + -0.011438023298978806, + -0.01552719995379448, + 0.01296145748347044, + -0.014678450301289558, + -0.01904388703405857, + -0.002864364068955183, + -0.03948020562529564, + 0.022229060530662537, + 0.00375388259999454, + 0.0337996669113636, + 0.035983506590127945, + 0.06740888208150864, + -0.010355443693697453, + -0.0313955582678318, + 0.036208394914865494, + -0.008999682031571865, + -0.0019975099712610245, + 0.044263534247875214, + -0.013477277010679245, + -0.009869946166872978, + -0.010396051220595837, + -0.013180496171116829, + -0.0032446386758238077, + -0.008246366865932941, + 0.03271927312016487, + 0.018092665821313858, + -0.01643424667418003, + 0.0015257643535733223, + -0.03802526369690895, + -0.00013426571968011558, + 0.018721293658018112, + -0.007915088906884193, + -0.023472338914871216, + -0.024362025782465935, + 0.031139621511101723, + 0.015164162963628769, + 0.010975743643939495, + -0.023246971890330315, + -0.020476868376135826, + -0.029316112399101257, + 0.06805050373077393, + 0.005803925916552544, + -0.014913956634700298, + -0.024012986570596695, + -0.01686079613864422, + 0.006966863758862019, + 0.03568089008331299, + 0.002640125108882785, + 0.014352664351463318, + 0.004998938180506229, + -0.011439812369644642, + 0.023913512006402016, + 0.03141772001981735, + -0.031244801357388496, + -0.11356791853904724, + -0.016528397798538208, + -0.041854094713926315, + 0.04466991126537323, + 0.08207962661981583, + 0.005279100500047207, + -0.01589193567633629, + 0.004774948116391897, + 0.019119074568152428, + 0.0189347006380558, + 0.03382601961493492, + -0.014475218020379543, + -0.11983301490545273, + -0.009690334089100361, + 0.039119694381952286, + 0.04342803731560707, + 0.03784060850739479, + 0.03788343071937561, + -0.034168895334005356, + -0.005867829080671072, + 0.02219143696129322, + 0.043859489262104034, + 0.02225954644382, + 0.034890275448560715, + -0.010705615393817425, + -0.0033966333139687777, + -0.028665268793702126, + -0.00787448137998581, + 0.013517193496227264, + 0.003625506069511175, + 0.01575625315308571, + 0.010211250744760036, + 0.009221479296684265, + -0.014290454797446728, + -0.014606288634240627, + -0.006866500247269869, + -0.006320445332676172, + 0.0676754042506218, + -0.008923238143324852, + 0.01948774792253971, + 0.010221901349723339, + 0.020129704847931862, + 0.003695472376421094, + -0.012121685780584812, + 0.0194671880453825, + 0.015323483385145664, + 0.030485782772302628, + -0.01691996306180954, + -0.004554263781756163, + -0.0026289320085197687, + 0.030320731922984123, + 0.009655401110649109, + 0.07343024760484695, + 0.005282429046928883, + 0.035014454275369644, + 0.03587789461016655, + 0.021046534180641174, + 0.005316684953868389, + 0.022220751270651817, + -0.022862475365400314, + -0.014346806332468987, + 0.008926913142204285, + 0.01762055605649948, + 0.006801550276577473, + 0.009681827388703823, + 0.018096590414643288, + -0.2926333546638489, + 0.0011539304396137595, + -0.037083759903907776, + 0.012781599536538124, + -0.01277882233262062, + 0.04694042727351189, + 0.04817314073443413, + -0.016605371609330177, + 0.04848314821720123, + -0.011681945994496346, + -0.01732136309146881, + 0.00701499730348587, + 0.05778314918279648, + 0.04410801827907562, + 0.015463395044207573, + -0.01132775004953146, + 0.025403963401913643, + 0.0177358016371727, + 0.021943051367998123, + -0.022551201283931732, + 0.03790492191910744, + 0.05542759969830513, + -0.009198070503771305, + 0.018666787073016167, + 0.021303357556462288, + -0.003979917149990797, + -0.008290935307741165, + -0.02091357484459877, + -0.004123951308429241, + 0.01969367265701294, + 0.0063406238332390785, + 0.018761631101369858, + -0.018301697447896004, + 0.012477956712245941, + -0.017947621643543243, + -0.02554340846836567, + 0.0011267181253060699, + 0.011204792186617851, + 0.011263146065175533, + -0.040736500173807144, + -0.008007545955479145, + 0.021571239456534386, + -0.015610914677381516, + -0.001762807834893465, + -0.0008508024620823562, + -0.011124483309686184, + 0.0004331650270614773, + 0.03223150968551636, + -0.02052922733128071, + 0.002791912294924259, + -0.021522460505366325, + -0.019295591861009598, + -0.03263603895902634, + -0.036752209067344666, + -0.006752911955118179, + 0.01983606070280075, + -0.06321506202220917, + 0.06610800325870514, + 0.007728168740868568, + 0.002140922239050269, + 0.06337437033653259, + -0.025946704670786858, + -0.034533895552158356, + -0.024734141305088997, + -0.011028803884983063, + 0.02610662765800953, + 0.0024402758572250605, + 0.006502795033156872, + 0.017969952896237373, + -0.012537260539829731, + 0.008546251803636551, + -0.037386927753686905, + -0.023893367499113083, + -0.010062312707304955, + -0.0380890890955925, + -0.03811858966946602, + 0.03583401069045067, + -0.0053894794546067715, + -0.006445040460675955, + -0.022279005497694016, + -0.008416271768510342, + 0.02263202890753746, + -0.015932928770780563, + -0.03629075735807419, + 0.0054925959557294846, + -0.01788375899195671, + 0.02218654938042164, + -0.029118865728378296, + 0.013892167247831821, + -0.018030522391200066, + -0.016013676300644875, + 0.02607131376862526, + 0.02436257153749466, + -0.0031814947724342346, + -0.016405975446105003, + -0.05132673308253288, + -0.0069497316144406796, + -0.011653370223939419, + 0.02367514930665493, + -0.024273285642266273, + 0.01824251003563404, + 0.01578054204583168, + 0.045799620449543, + 0.032413680106401443, + -0.008810244500637054, + -0.012245557270944118, + 0.054692648351192474, + 0.006450043059885502, + -0.021578030660748482, + 0.004452606663107872, + -0.019484665244817734, + 0.02732430212199688, + -0.06528113037347794, + -0.021619068458676338, + -0.052397221326828, + 0.0037755693774670362, + -0.0026109274476766586, + 0.032542336732149124, + 0.0025500087067484856, + 0.015390527434647083, + -0.0029434841126203537, + -0.022263439372181892, + 0.0005678391898982227, + -0.02936520427465439, + -0.018697557970881462, + 0.045628659427165985, + -0.02356475591659546, + 0.05723956227302551, + 0.0039049808401614428, + 0.03154719993472099, + 0.012250825762748718, + 0.013306085020303726, + 0.005841527134180069, + -0.008950569666922092, + -0.045765943825244904, + -0.030494414269924164, + -0.006090905051678419, + 0.01766926795244217, + 0.019943051040172577, + -0.02864181622862816, + 0.04647773504257202, + -0.010894195176661015, + -0.030601369217038155, + 0.011029018089175224, + -0.0053455643355846405, + 0.03790684789419174, + 0.006488209590315819, + -0.004123530816286802, + -0.02325456589460373, + 0.03146061673760414, + 0.020736610516905785, + -0.031539466232061386, + 0.01494520902633667, + 0.014751319773495197, + 0.014983637258410454, + 0.001968116732314229, + -0.06391628086566925, + 0.004594296216964722, + 0.0025860897731035948, + -0.0015108413062989712, + 0.04350382834672928, + -0.015041574835777283, + 0.060172103345394135, + 0.015593013726174831, + -0.05258578062057495, + -0.04428400099277496, + -0.022440817207098007, + 0.02541368454694748, + -0.010860532522201538, + -0.042829860001802444, + -0.04599897190928459, + 0.0005477960803546011, + -0.00015764322597533464, + -0.05231189355254173, + 0.043780263513326645, + -0.0018106687348335981, + 0.016511017456650734, + -0.007165230810642242, + 0.054631542414426804, + -0.008831975981593132, + -0.06056878715753555, + -0.0105542978271842, + 0.017546651884913445, + 0.002325013279914856, + 0.020457707345485687, + 0.02186591736972332, + -0.024133455008268356, + 0.03980358690023422, + 0.027049927040934563, + 0.0056030526757240295, + 0.037066519260406494, + -0.028363065794110298, + -0.020626870915293694, + -0.011233006604015827, + 0.04784679040312767, + -0.002496100030839443, + -0.010760116390883923, + 0.028990844264626503, + 0.04344082996249199, + -0.015059240162372589, + 0.015599106438457966, + 0.032906174659729004, + -0.01410516444593668, + 0.03337869420647621, + -0.0054810442961752415, + -0.08319605141878128, + 0.016787368804216385, + 0.022678274661302567, + 0.03533221781253815, + 0.0269264355301857, + -0.015420029871165752, + 0.0045530544593930244, + 0.017574133351445198, + 0.004379510413855314, + -0.02652738243341446, + 0.015335272066295147, + -0.005382945295423269, + 0.0020220670849084854, + 0.011462178081274033, + 0.039436254650354385, + 0.024272644892334938, + 0.01965649053454399, + -0.010372564196586609, + 0.026132022961974144, + -0.00047800043830648065, + 0.0025823053438216448, + -0.035571616142988205, + -0.011064890772104263, + -0.01510661095380783, + 0.01929357461631298, + 0.05575929582118988, + 0.018209589645266533, + 0.010318535380065441, + 0.033173710107803345, + -0.043427009135484695, + -0.019570739939808846, + -0.019365526735782623, + 0.03165639936923981, + -0.018161216750741005, + 0.023189833387732506, + -0.030538910999894142, + 5.4258918680716306e-05, + 0.027887606993317604, + 0.003167682560160756, + -0.008584647439420223, + 0.025758549571037292, + -0.03697461262345314, + -0.014619600027799606, + -0.04140063375234604, + 0.024468477815389633, + 0.0011960475239902735, + -0.009922835044562817, + -0.0018137878505513072, + -0.017172200605273247, + -0.04741816595196724, + 0.023048697039484978, + -0.011781249195337296, + -0.010392111726105213, + -0.0014989667106419802, + -0.015591775067150593, + 0.011761688627302647, + 0.012319122441112995, + -0.025603141635656357, + 0.06783616542816162, + -0.02054246887564659, + -0.035440582782030106, + -0.013653451576828957, + -0.016277235001325607, + 0.03325791656970978 + ] + }, + { + "name": "050615fe992a00d6af0e664e497ebf53.png", + "file-path": "E:\\Pratham\\2025\\Harsh Sir\\Scratch Vision\\images\\Backdrops\\beach_malibu.sb3\\050615fe992a00d6af0e664e497ebf53.png", + "embeddings": [ + 0.013951914384961128, + 0.01682569831609726, + -0.024244774132966995, + 0.013797750696539879, + -0.004513754975050688, + 0.03797956928610802, + -0.04193492606282234, + 0.003913453780114651, + -0.02884082868695259, + -0.060497380793094635, + -0.012591978535056114, + -0.0008161275763995945, + 0.002948807319626212, + -0.03531849384307861, + -0.02138642966747284, + 0.03693532198667526, + 0.0039763241074979305, + -0.034976255148649216, + 0.014370119199156761, + 0.00580771965906024, + 0.07362315058708191, + -0.04041800647974014, + 0.002056003315374255, + 0.008477572351694107, + -0.005257549230009317, + -0.03838050737977028, + 0.03983984887599945, + -0.02825012244284153, + -0.009884083643555641, + 0.03401455655694008, + -0.005113806109875441, + -0.01924600452184677, + 0.036058295518159866, + -0.03346128761768341, + -0.04916193708777428, + -0.03411581367254257, + -0.04678740352392197, + 0.010963315144181252, + -0.013573585078120232, + 0.03758554905653, + 0.01109994761645794, + -0.001343357260338962, + 0.03151967376470566, + 0.03550875559449196, + -0.0016253722133114934, + 0.020538337528705597, + -4.665804954129271e-05, + 0.011809054762125015, + -0.04693666473031044, + -0.018283629789948463, + 0.050978951156139374, + 0.02512706257402897, + 0.06512723863124847, + 0.022610338404774666, + -0.05282783508300781, + 0.015209892764687538, + -0.003167071845382452, + 0.04843337833881378, + -0.017413519322872162, + -0.03482111543416977, + -0.03216155245900154, + 0.0209085363894701, + -0.023579096421599388, + 0.011480662040412426, + 0.06230756267905235, + -0.004445222206413746, + 0.02327125146985054, + 0.014995869249105453, + -0.03938877955079079, + 0.002225196221843362, + 0.00565576134249568, + 0.01730930618941784, + 0.003829512046650052, + 0.024175992235541344, + -0.07980324327945709, + -0.03900585323572159, + -0.0009068030631169677, + 0.017660120502114296, + 0.004260865040123463, + 0.0346272848546505, + -0.009055937640368938, + -0.04590520262718201, + -0.038662735372781754, + 0.02096697688102722, + -0.0344083346426487, + -0.04031777381896973, + 0.04873574152588844, + 0.024203242734074593, + -0.003280159318819642, + 0.003721986897289753, + -0.03912092372775078, + -0.02680743858218193, + 0.005631869193166494, + 0.025628497824072838, + 0.007946941070258617, + 0.08816490322351456, + 0.034286532551050186, + 0.004338313825428486, + -0.02736189030110836, + 0.005031546112149954, + 0.003736509708687663, + -0.027604486793279648, + -0.02825787663459778, + 0.01655009388923645, + 0.003206996712833643, + 0.02710656262934208, + -0.006935528013855219, + -0.015731288120150566, + -0.06010253727436066, + -0.00991861242800951, + 0.028834406286478043, + 0.015437133610248566, + 0.009430690668523312, + -0.03623174875974655, + 0.01878713071346283, + -0.013544667512178421, + -0.01657760702073574, + -0.016846975311636925, + -0.06354962289333344, + 0.0594039186835289, + -7.421434565912932e-05, + 0.010812615975737572, + 0.03406815230846405, + -0.07720651477575302, + -0.018197979778051376, + 0.02257571555674076, + 0.005896188318729401, + 0.023576699197292328, + -0.019959308207035065, + -0.015425868332386017, + 0.025273041799664497, + 0.011367637664079666, + -0.005583287216722965, + -0.0024099296424537897, + -0.017762813717126846, + -0.019764352589845657, + -0.05268533527851105, + 0.017969021573662758, + 0.02269272319972515, + 0.0023965188302099705, + -0.024631211534142494, + -0.007189132738858461, + -0.008634192869067192, + -0.03392413258552551, + 0.0158468559384346, + 0.019793111830949783, + 0.0233265683054924, + 0.015998797491192818, + 0.022204991430044174, + 0.011196348816156387, + 0.0014626114862039685, + 0.024852612987160683, + -0.006807718891650438, + 0.015277868136763573, + -0.01108753215521574, + 0.02430455945432186, + 0.010704523883759975, + 0.007909124717116356, + 0.0029517626389861107, + 0.047109272330999374, + 0.0052982233464717865, + 0.005956805776804686, + -0.0124936169013381, + -0.005743127316236496, + -0.04136700928211212, + 0.02607732079923153, + 0.030485305935144424, + 0.01015727873891592, + -0.0066061620600521564, + -0.00020672612299676985, + -0.008968456648290157, + 0.07071632146835327, + -0.026642683893442154, + 0.0007658575777895749, + -0.010171158239245415, + 0.033497583121061325, + -0.03541775792837143, + 0.004454045556485653, + -0.020719014108181, + -0.023928102105855942, + 0.01918727345764637, + -0.04918751120567322, + 0.009062795899808407, + -0.019274748861789703, + 0.05464162304997444, + 0.006527669262140989, + 0.007614457048475742, + -0.03172421455383301, + -0.02152842842042446, + -0.02489362470805645, + 0.0007438587490469217, + -0.011690715327858925, + 0.02469048835337162, + -0.015463425777852535, + 0.005561127793043852, + -0.012477829121053219, + -0.0006114608841016889, + 0.059466514736413956, + 0.023330170661211014, + 0.08031196892261505, + 0.0042588235810399055, + 0.010858715511858463, + 0.03504032641649246, + -0.0011397618800401688, + -0.012754076160490513, + 0.050537124276161194, + 0.023910023272037506, + 0.05306723341345787, + 0.0232024397701025, + 0.01219965610653162, + -0.010463857091963291, + -0.0309817623347044, + -0.038723431527614594, + -0.03156568855047226, + 0.003039952367544174, + -0.0031742220744490623, + -0.005857265088707209, + 0.013244601897895336, + -0.002617852296680212, + 0.0037694855127483606, + -0.044504813849925995, + 0.05015746131539345, + -0.01167363952845335, + -0.01835201308131218, + -0.010639922693371773, + -0.015255497768521309, + 0.012437410652637482, + 0.01779826357960701, + 0.031325388699769974, + 0.0055800811387598515, + 0.01707908883690834, + 0.022827573120594025, + -0.002615682315081358, + -0.03423455357551575, + -0.032291918992996216, + -0.012545386329293251, + 0.009260198101401329, + -0.055705953389406204, + -0.007282944396138191, + 0.019373802468180656, + -0.02882866933941841, + -0.0056353420950472355, + -0.02725970558822155, + 0.020130712538957596, + -0.025063531473279, + 0.006809717509895563, + -0.014640016481280327, + -0.020335696637630463, + 0.009811804629862309, + -0.016113540157675743, + 0.06309667974710464, + -0.01707240380346775, + 0.016122611239552498, + 0.023344777524471283, + 0.0024192698765546083, + -0.0294821634888649, + -0.014083895832300186, + -0.024467771872878075, + 0.03842904046177864, + 0.016932090744376183, + -0.038726937025785446, + -0.009856706485152245, + -0.01699533686041832, + 0.03285627067089081, + -0.0469861701130867, + 0.05322238802909851, + -0.022070646286010742, + -0.0007260065758600831, + -0.017824899405241013, + 0.013523411005735397, + -0.014721868559718132, + -0.034735798835754395, + 0.05188795551657677, + -0.02001180686056614, + 0.006628190632909536, + 0.022184234112501144, + -0.013708377256989479, + 0.022783758118748665, + -0.01626795157790184, + 0.0036504315212368965, + -0.01781274937093258, + -0.019955487921833992, + -0.007243946194648743, + -0.014106431044638157, + -0.0026318239979445934, + 0.057353366166353226, + 0.011480077169835567, + -0.010147172957658768, + 0.014421009458601475, + 0.0029098642989993095, + 0.01612929068505764, + 0.00034486036747694016, + -0.018192095682024956, + -0.06533320248126984, + 0.028300946578383446, + -0.005341471638530493, + -0.05685646831989288, + 0.008639195933938026, + 0.00535423681139946, + -0.010934821330010891, + 0.011096697300672531, + -0.011150898411870003, + -0.005082853138446808, + 0.004043049179017544, + -0.004585625603795052, + 0.030853677541017532, + 0.03608063980937004, + 0.02397051639854908, + -0.05603604391217232, + 0.011417447589337826, + -0.005845774430781603, + -0.0007065910031087697, + -0.020397154614329338, + -0.004942494444549084, + -0.05322674289345741, + -0.0031488975510001183, + 0.0004906495450995862, + -0.012641534209251404, + -0.009168182499706745, + -0.0026908786967396736, + 0.0038626280147582293, + -0.06166158616542816, + 0.02090349607169628, + 0.017588090151548386, + -0.027083639055490494, + 0.05541267246007919, + -0.08006934076547623, + 0.011282423511147499, + 0.03946680203080177, + 0.04100131615996361, + 0.014869766309857368, + -0.02344088815152645, + 0.016689898446202278, + -0.029051732271909714, + -0.02167614735662937, + -0.05909650772809982, + 0.028086107224225998, + -0.0688655823469162, + 0.008958854712545872, + 0.014658733271062374, + 0.003987465985119343, + -0.01305432803928852, + 0.023159461095929146, + 0.02178129553794861, + 0.031133219599723816, + 0.06435579061508179, + 0.08378873020410538, + 0.01700882613658905, + 0.005363014992326498, + 0.005672512110322714, + -0.012475185096263885, + -0.007986961863934994, + -0.018396873027086258, + 0.0072584375739097595, + 0.011244318448007107, + -0.020634271204471588, + -0.004224712494760752, + -0.01084230188280344, + 0.0017823799280449748, + 0.02881777472794056, + 0.0067526004277169704, + -0.009822962805628777, + 0.030624995008111, + 0.018122974783182144, + -0.020231489092111588, + 0.053787101060152054, + 0.01823902688920498, + 0.010060138069093227, + 0.013621125370264053, + 0.005813376512378454, + 0.0060797142796218395, + 0.005389963276684284, + -0.04457997530698776, + 0.031442709267139435, + 0.021494533866643906, + 0.01588141731917858, + -0.036853108555078506, + 0.009441963396966457, + -0.024087175726890564, + 0.01877729967236519, + -0.017545582726597786, + 0.05290433391928673, + 0.06041982024908066, + 0.023581691086292267, + -0.003336450783535838, + 0.03781795874238014, + 0.003622234333306551, + -0.01094468217343092, + 0.016605861485004425, + 0.023823995143175125, + 0.02403947338461876, + -0.0358031764626503, + -0.013779804110527039, + 0.006300439592450857, + -0.019332202151417732, + 0.0011292226845398545, + -0.0076657552272081375, + -0.015920160338282585, + 0.03950704634189606, + -0.06297826766967773, + -0.002899407409131527, + 0.07440672814846039, + 0.007863792590796947, + 0.005190317984670401, + 0.0032421548385173082, + 0.0027938224375247955, + -0.03799409791827202, + -0.0058671277947723866, + 0.032503288239240646, + -0.024359628558158875, + 0.05477079376578331, + 0.007498597726225853, + -0.027984358370304108, + -0.0008197637507691979, + -0.0010462154168635607, + 0.027456756681203842, + -0.08218269050121307, + -0.04396510869264603, + -0.007696394342929125, + 0.05638967454433441, + 0.011781332083046436, + -0.04493678733706474, + -0.00012696940393652767, + -0.004504231736063957, + 0.04678170382976532, + -0.003651092294603586, + 0.007354611996561289, + 0.00017218268476426601, + 0.020650766789913177, + 0.01906588114798069, + -0.006963520310819149, + 0.04082769900560379, + -0.049139462411403656, + 0.012698582373559475, + -0.12644538283348083, + 0.05110342800617218, + 0.013562106527388096, + -0.0028729280456900597, + -0.028051679953932762, + 0.006202524993568659, + -0.00760060315951705, + 0.04049832001328468, + -0.04331732541322708, + 0.0051845284178853035, + 0.033856429159641266, + -0.03248346969485283, + -0.0382162407040596, + -0.005551076028496027, + -0.04193177446722984, + -0.024754729121923447, + -0.010396012105047703, + -0.04708132520318031, + 0.013068041764199734, + 0.006710096262395382, + -0.024419661611318588, + -0.018082212656736374, + -0.009380375035107136, + 0.2655470371246338, + 0.050586454570293427, + -0.018779350444674492, + 0.000434921239502728, + 0.006275656633079052, + 0.014541937038302422, + 0.008530917577445507, + 0.02154296450316906, + -0.017815001308918, + -0.0014353090664371848, + 0.008242538198828697, + 0.04601724073290825, + 0.040205180644989014, + -0.045281048864126205, + -0.017252713441848755, + 0.03999152407050133, + -0.007459883578121662, + -0.040946025401353836, + -0.06891844421625137, + 0.0010227578459307551, + 0.011184150353074074, + -0.027906319126486778, + -0.008460561744868755, + -0.008166910149157047, + 0.018754513934254646, + -0.01787007413804531, + -0.022118637338280678, + 0.025031041353940964, + -0.005926104262471199, + 0.01745203323662281, + 0.0221162810921669, + 0.018192172050476074, + 0.006470498163253069, + -0.022146426141262054, + -0.02244274877011776, + 0.006280755158513784, + 0.015347056090831757, + -0.002972121350467205, + 0.010626586154103279, + 0.04974300414323807, + -0.01913749985396862, + 0.00349687528796494, + 0.02798365242779255, + -0.00970772560685873, + -0.0012835355009883642, + -0.03149439021945, + 0.008344827219843864, + -0.04773540422320366, + -0.003301692195236683, + -0.005811499431729317, + 0.030302030965685844, + 0.005751915276050568, + 0.014654536731541157, + -0.04788456857204437, + 0.02133682742714882, + -0.020213158801198006, + -0.002281694673001766, + -0.0024485986214131117, + -0.02154700830578804, + 0.02596230059862137, + -0.02371511235833168, + -0.034695010632276535, + 0.022563694044947624, + 0.0008066094014793634, + 0.016649654135107994, + -0.00933775119483471, + -0.046787552535533905, + -0.003476683283224702, + 0.01596738211810589, + -0.010305075906217098, + 0.002447716426104307, + 0.020191755145788193, + 0.024959418922662735, + 0.016870643943548203, + 0.014407157897949219, + -0.0051354533061385155, + -0.008406720124185085, + 0.004352964926511049, + -0.04875926300883293, + 0.041873086243867874, + -0.022675631567835808, + 0.032555658370256424, + -0.009971770457923412, + 0.0026294151321053505, + 0.05331417918205261, + -0.03348890691995621, + 0.02765762247145176, + 0.015731599181890488, + 0.004129370674490929, + -0.02234741672873497, + -0.005283149424940348, + 0.027588173747062683, + 0.02741646207869053, + -0.006049841642379761, + 0.014706842601299286, + -0.0019473324064165354, + -0.05639735981822014, + 0.015157918445765972, + -0.013128980062901974, + 0.02209669165313244, + -0.019582189619541168, + -0.005599845666438341, + 0.035127509385347366, + -0.0016235976945608854, + -0.01798691228032112, + 0.030372219160199165, + -0.019285758957266808, + 0.034083474427461624, + -0.0358043871819973, + 0.032850321382284164, + -0.015505471266806126, + 0.00523511553183198, + -0.005120088811963797, + 0.0029241121374070644, + -0.012345635332167149, + -0.030127329751849174, + -0.0013231018092483282, + -0.02114269509911537, + -0.029774455353617668, + -0.03886893391609192, + -0.019701238721609116, + 0.008834279142320156, + -0.024698257446289062, + 0.04237721860408783, + -0.030121447518467903, + 0.019427506253123283, + 0.06925257295370102, + -0.01678447052836418, + -0.07017301768064499, + -0.028986509889364243, + -0.02123023010790348, + 0.053266264498233795, + 0.008152436465024948, + -0.004021485801786184, + -0.030501220375299454, + -0.000980798271484673, + 0.02237781696021557, + 0.02289457991719246, + -0.018908066675066948, + 0.03247050568461418, + 0.06097106635570526, + -0.02358749508857727, + -0.007925072684884071, + 0.013575820252299309, + 0.03799351677298546, + 0.014581731520593166, + 0.015286033973097801, + -0.0766788199543953, + 0.017082495614886284, + -0.019768964499235153, + 0.029373763129115105, + -0.026917951181530952, + -0.03767038881778717, + 0.009715433232486248, + 0.0023848179262131453, + 0.00014221925812307745, + 0.009836527518928051, + -0.01364557072520256, + -0.018841300159692764, + -0.052869200706481934, + -0.027879998087882996, + 0.003922739997506142, + -0.004205757286399603, + -0.06595920026302338, + -0.0017991031054407358, + -0.028071189299225807, + -0.0003319443203508854, + 0.13081514835357666, + 0.04957215115427971, + 0.09123360365629196, + -0.03198381885886192, + 0.02875925973057747, + 0.0027421663980931044, + 0.01392082404345274, + -0.17598655819892883, + 0.011385095305740833, + 0.03389891982078552, + 0.004396362230181694, + -0.02340851165354252, + -0.08412676304578781, + 0.01095307245850563, + 0.009168283082544804, + 0.04487576708197594, + -0.024968689307570457, + 0.015423796139657497, + 0.012206253595650196, + 0.06640102714300156, + 0.006596737541258335, + 0.060540102422237396, + 0.06781265139579773, + 0.01540268026292324, + -0.04400622099637985, + 0.029331298545002937, + 0.011498319916427135, + 0.01574275642633438, + 0.011474864557385445, + -0.002569410717114806, + 0.004954432137310505, + -0.031652797013521194, + 0.015712125226855278, + 0.01619299128651619, + 0.0028600278310477734, + 0.01161534059792757, + -0.001993563026189804, + 0.03197414427995682, + -0.006169831845909357, + -0.0033145551569759846, + -0.01370551809668541, + -0.05368635803461075, + -0.0004651157942134887, + -0.03521309047937393, + 0.01351956371217966, + 0.01929338648915291, + 0.003951390273869038, + -0.01799945719540119, + -0.00048473465722054243, + -0.017409564927220345, + 0.016987817361950874, + 0.04272674396634102, + 0.03411044552922249, + 0.03529539331793785, + 0.0017048702575266361, + 0.025709960609674454, + -0.034132637083530426, + 0.041581835597753525, + -0.01899491809308529, + 0.002913716481998563, + 0.0025558071210980415, + -0.006294329650700092, + -0.022015487775206566, + -0.01181506272405386, + -0.009819298051297665, + -0.09948548674583435, + -0.023823663592338562, + 0.004951119422912598, + -0.0028695722576230764, + 0.02711423859000206, + -0.021227024495601654, + 0.021447835490107536, + 0.0009205515962094069, + 0.024577729403972626, + 0.05857324227690697, + -0.00036726376856677234, + 0.053523872047662735, + -0.03629777580499649, + -0.025452429428696632, + -0.0127389682456851, + -0.010422609746456146, + 0.023922592401504517, + 0.0029013240709900856, + 0.07274724543094635, + 0.018908042460680008, + 0.006972773466259241, + 0.018069952726364136, + -0.022675858810544014, + -0.026354994624853134, + -0.05692369118332863, + -0.010014107450842857, + -0.006474588997662067, + 0.03350811079144478, + 0.009527355432510376, + -0.0029497426003217697, + -0.010545825585722923, + -0.0192964356392622, + 0.00564633309841156, + -0.021756216883659363, + 0.06945081800222397, + -0.016069388017058372, + -0.005792854353785515, + -0.013003692962229252, + 0.017536809667944908, + 0.0038650056812912226, + 0.04010099545121193, + -0.04889781028032303, + 0.022257011383771896, + -0.06532278656959534, + -0.0032704053446650505, + -0.03988562151789665, + 0.04684767127037048, + -0.025593778118491173, + 0.018291614949703217, + 0.015551161020994186, + 0.0014703478664159775, + 0.02650528773665428, + 0.015998806804418564, + -0.028622567653656006, + 0.0023363351356238127, + 0.02792137861251831, + -0.023458871990442276, + 0.019733725115656853, + 0.03620905056595802, + 0.014070866629481316, + -0.03625703603029251, + 0.01120020542293787, + 0.042335547506809235, + 0.02301884815096855, + -0.028118444606661797, + 0.019230471923947334, + -0.24275685846805573, + 0.02551705203950405, + 0.0015612364513799548, + -0.007203651592135429, + -0.017915058881044388, + 0.026784515008330345, + 0.028911128640174866, + 0.019504781812429428, + -0.03301616385579109, + 0.020974865183234215, + 0.005072788801044226, + -0.038733407855033875, + 0.07825835794210434, + 0.012584642507135868, + 0.02082550711929798, + 0.023768339306116104, + 0.014023357070982456, + 0.003122120862826705, + -0.006466183345764875, + 0.042089756578207016, + 0.01282722968608141, + 0.016674119979143143, + -0.00558252539485693, + -0.03226037323474884, + 0.08039730042219162, + 0.028348447754979134, + -0.005082741845399141, + -0.015207636170089245, + -0.02263379842042923, + -0.0009085934143513441, + 0.02081412449479103, + 0.03129736706614494, + 0.004752248991280794, + 0.036018528044223785, + 0.01135118305683136, + -0.04666711017489433, + 0.0021307351998984814, + 0.009314058348536491, + 0.00208838889375329, + -0.013459901325404644, + -0.03726601600646973, + -0.005602910183370113, + -0.03222976624965668, + 0.004852843936532736, + -0.009006975218653679, + 0.003974009770900011, + -0.00821754615753889, + 0.025947710499167442, + 0.0038030564319342375, + 0.03497745469212532, + -0.0011222140165045857, + 0.01885407418012619, + -0.02884439378976822, + 0.005725910887122154, + -0.03862322121858597, + -0.04392200708389282, + -0.02364741638302803, + 0.006843567360192537, + -0.03626514598727226, + 0.00869324803352356, + 0.0023180858697742224, + -0.027461688965559006, + 0.006511772982776165, + -0.03282763436436653, + -0.014335887506604195, + 0.003278398420661688, + 0.014175653457641602, + -0.002751903375610709, + 0.006835591979324818, + -0.033850278705358505, + 0.01287408173084259, + 0.025782188400626183, + -0.022174805402755737, + -0.000484459480503574, + 0.003548095468431711, + 0.02750379405915737, + -0.001169278984889388, + -0.019336029887199402, + -0.008937438949942589, + 0.011688725091516972, + 0.009158452041447163, + 0.018765710294246674, + -0.03370701149106026, + -0.05239715427160263, + 0.007816103287041187, + -0.045237164944410324, + -0.006437581963837147, + -0.03302469104528427, + 0.011755872517824173, + -0.03464179113507271, + -0.0058245789259672165, + -0.009085331112146378, + 0.029173320159316063, + -0.002954101888462901, + 0.01702694036066532, + -0.03353581205010414, + 0.01869007758796215, + 0.024078605696558952, + -0.036743324249982834, + -0.016938788816332817, + -0.004926006775349379, + 0.0577755868434906, + -0.0032649568747729063, + 0.0866401195526123, + 0.030668998137116432, + -0.005452318117022514, + 0.004862627480179071, + -0.005426056683063507, + 0.01039587240666151, + 0.024670656770467758, + 0.058697592467069626, + -0.026464438065886497, + -0.030414635315537453, + 0.056570690125226974, + -0.053254738450050354, + -0.02711675316095352, + 0.017301948741078377, + 0.007229527924209833, + -0.002165893092751503, + -0.014218484051525593, + 0.0014374242164194584, + -0.008198537863790989, + -0.019607845693826675, + 0.03858133777976036, + -0.02012389898300171, + 0.01650535687804222, + -0.038427501916885376, + -0.012584337964653969, + 0.0070708333514630795, + -0.0003288040461484343, + -0.030869169160723686, + -0.01617264933884144, + 0.024098167195916176, + -0.007466730661690235, + 0.022981319576501846, + -0.06839022785425186, + 0.01566731184720993, + -0.013827047310769558, + -0.011236573569476604, + -0.03152476251125336, + -0.005997174419462681, + -0.04424517974257469, + 0.01341889426112175, + -0.03225625306367874, + 0.001666330499574542, + 0.00922403484582901, + -0.009154397062957287, + 0.019523626193404198, + 0.006286887917667627, + 0.006641362328082323, + -0.024340683594346046, + 0.0024654946755617857, + -0.004959607031196356, + -0.034152526408433914, + 0.023708466440439224, + 0.01160721480846405, + -0.01568485237658024, + -0.0031508568208664656, + -0.028751853853464127, + -0.014586664736270905, + 0.032342683523893356, + 0.01961834914982319, + 0.02301952987909317, + 0.022680042311549187, + 0.03306863084435463, + -0.013635355979204178, + -0.0020832412410527468, + -0.01819681189954281, + -0.03453584760427475, + 0.03296429291367531, + 0.0036569898948073387, + 0.0023999670520424843, + -0.008963003754615784, + -0.0032944942358881235, + 0.007664334028959274, + -0.013429702259600163, + -0.0020946995355188847, + -0.03839687630534172, + -0.034186843782663345, + -0.04712926596403122, + 0.023084022104740143, + 0.006358745973557234, + 0.017433593049645424, + -0.020161351189017296, + -0.03933681175112724, + 0.00913520809262991, + 0.007818604819476604, + 0.019750315696001053, + 0.018542004749178886, + -0.032666705548763275, + -0.04385244846343994, + -0.0013889611000195146, + 0.01802593469619751, + 0.020114324986934662, + 0.031042808666825294, + -0.041724178940057755, + -0.030896427109837532, + -0.008278743363916874, + 0.007895287126302719, + 0.0031784388702362776, + 0.003980468958616257, + -0.0028407082427293062, + 0.07972566783428192, + 0.005324921105057001, + -0.044542208313941956, + -0.06093365699052811, + -0.002058974700048566, + -0.027608076110482216, + 0.0396646149456501, + -0.00774197606369853, + -0.00018958641157951206, + 0.042271919548511505, + 0.009273791685700417, + 0.017533479258418083, + -0.023572875186800957, + -0.03488055616617203, + 0.024448629468679428, + -0.032823797315359116, + 0.032340068370103836, + -0.027559254318475723, + 0.005919784307479858, + 0.02591201290488243, + -0.008504319004714489, + 0.007594464346766472, + -0.002376906108111143, + 0.04731407016515732, + 0.017300793901085854, + -0.024082086980342865, + -0.042261753231287, + -0.010201923549175262, + 0.0067755673080682755, + -0.06239499896764755, + -0.025707703083753586, + -0.009064574725925922, + 0.027637377381324768, + -0.006966742221266031, + -0.047750845551490784, + 0.04156321659684181, + 0.02366579882800579, + 0.006989030167460442, + 0.0001363615010632202, + -0.007558770477771759, + 0.05754871293902397, + -0.01599225029349327, + -0.009366670623421669, + 0.007455794140696526, + -0.01849699392914772, + 0.02440028451383114, + 0.01847958192229271, + 0.008733455091714859, + -0.026525845751166344, + 0.006587599869817495, + -0.00742509588599205, + 0.014315100386738777, + 0.01987297274172306, + 0.006123802624642849, + 0.010271572507917881, + 0.04295336455106735, + 0.012307102791965008, + -0.004981381352990866, + -0.052686262875795364, + -0.04304014518857002, + -0.019772253930568695, + 0.08917892724275589, + 0.01677967980504036, + 0.01251740287989378, + -0.0029130098409950733, + -0.01892206072807312, + 0.01761847920715809 + ] + }, + { + "name": "951765ee7f7370f120c9df20b577c22f.png", + "file-path": "E:\\Pratham\\2025\\Harsh Sir\\Scratch Vision\\images\\Backdrops\\castle2.sb3\\951765ee7f7370f120c9df20b577c22f.png", + "embeddings": [ + 0.026378154754638672, + 0.015847641974687576, + 0.011125997640192509, + -0.002282689558342099, + -0.015127483755350113, + 0.04235579073429108, + -0.028845271095633507, + -0.003980499226599932, + -0.04015539959073067, + -0.0239808801561594, + 0.02712036855518818, + -0.03638893738389015, + -0.02055714651942253, + -0.007397321984171867, + 0.012156147509813309, + -0.000145837664604187, + -0.0037985574454069138, + 0.036638177931308746, + -0.015876000747084618, + 0.02833145298063755, + -0.039700668305158615, + 0.019705915823578835, + -0.011172202415764332, + 0.05434431508183479, + -0.03258654102683067, + 0.0012603964423760772, + -0.004317051265388727, + 0.021575242280960083, + 0.014551347121596336, + 0.001023060642182827, + 0.04725151136517525, + 0.04973311349749565, + 0.021460164338350296, + 0.004516697488725185, + 0.00872490368783474, + 0.03941630572080612, + 0.06757286936044693, + 0.0244371946901083, + -0.01687067560851574, + -0.0027720441576093435, + -0.006578547414392233, + 0.03726130351424217, + -0.006657974794507027, + 0.000858097686432302, + -0.017116433009505272, + -0.002960973884910345, + 0.018908800557255745, + -0.03785547986626625, + -0.018451031297445297, + -0.009057305753231049, + -0.005833067465573549, + -0.033602215349674225, + 0.02094271034002304, + 0.00116315099876374, + 0.006363545078784227, + -0.03896353393793106, + 0.028885923326015472, + 0.002525241579860449, + -0.06905414909124374, + -0.03821201249957085, + -0.0034907341469079256, + 0.008813283406198025, + -0.014453697018325329, + 0.02996712550520897, + 0.0526464506983757, + 0.017652489244937897, + 0.014965854585170746, + 0.006081904284656048, + -0.011460544541478157, + 0.026926545426249504, + 0.047960057854652405, + -0.0256967656314373, + -0.0197343360632658, + -0.012587523087859154, + -0.012832509353756905, + 0.01943347603082657, + -0.019845381379127502, + 0.013065529987215996, + 0.0031873255502432585, + -0.05231514945626259, + -0.03958090394735336, + -0.020389679819345474, + 0.03629695251584053, + 0.026876218616962433, + -0.014437124133110046, + -0.008219442330300808, + 0.004774470813572407, + 0.02298182062804699, + 0.015273318625986576, + -0.04364725202322006, + -0.042521748691797256, + 0.013983918353915215, + -0.012252796441316605, + -0.007986261509358883, + 0.01732468605041504, + 0.022356100380420685, + 0.014745748601853848, + -0.001828228123486042, + -0.07736334204673767, + 0.007414749823510647, + -0.01455183606594801, + -0.022628217935562134, + 0.019896935671567917, + 0.005918489303439856, + -0.011684841476380825, + -0.0005005636485293508, + -0.004478808492422104, + 0.011776824481785297, + -0.0006704433471895754, + 0.024202048778533936, + -0.024562906473875046, + 0.04215111956000328, + -0.0038593157660216093, + -0.011803923174738884, + 0.026218678802251816, + -0.035237472504377365, + 0.0035040834918618202, + 0.04390157014131546, + 0.014327065087854862, + 0.03273715451359749, + -0.005705403164029121, + 0.005766529124230146, + 0.012715476565063, + -0.1236284077167511, + 0.016019556671380997, + 0.033305201679468155, + 0.04477821663022041, + 0.04203421622514725, + 0.003843811107799411, + -0.021308058872818947, + -0.0338444858789444, + -0.007242965511977673, + -0.02585798315703869, + 0.02084280364215374, + -0.041049499064683914, + 0.0358518585562706, + -0.02377832680940628, + -0.015620318241417408, + -0.04576735198497772, + 0.019496452063322067, + 0.002794734900817275, + -0.027141880244016647, + -0.011483397334814072, + -0.007328404579311609, + 0.02488098479807377, + -0.008612342178821564, + 0.012242908589541912, + -0.02578321285545826, + 0.054625608026981354, + 0.014556081034243107, + 0.018245603889226913, + -0.016223838552832603, + -0.006702236831188202, + -0.003461864311248064, + -0.05409374088048935, + -0.013755658641457558, + -0.01926640048623085, + -0.005064814351499081, + 0.016546130180358887, + -0.017906486988067627, + 0.006361105013638735, + -0.012922692112624645, + -0.02993451990187168, + 0.03297644108533859, + -0.01908981427550316, + 0.011525060050189495, + -0.008341181091964245, + -0.018148299306631088, + -0.005170180927962065, + -0.028225334361195564, + -0.005759098567068577, + -0.012930227443575859, + -0.038904305547475815, + -0.005261601414531469, + -0.011364941485226154, + 0.048167068511247635, + -0.013634297996759415, + -0.011703637428581715, + -0.04071079194545746, + -0.014396711252629757, + -0.02301638200879097, + -0.06290357559919357, + 0.009435715153813362, + -0.03192160278558731, + -0.035478994250297546, + 0.023936603218317032, + 0.01590888574719429, + 0.0023082923144102097, + -0.005962006282061338, + 0.0023711665999144316, + 0.0015813714126124978, + 0.02217729762196541, + 0.012674088589847088, + 0.050107840448617935, + -0.008429457433521748, + -0.017783932387828827, + -0.031064722687005997, + 0.02616557665169239, + 0.03113395906984806, + 0.037446122616529465, + -0.0017126910388469696, + 0.012903526425361633, + 0.034044187515974045, + 0.011160584166646004, + -0.043025825172662735, + -0.013281570747494698, + 0.018371907994151115, + 0.0011586599284783006, + 0.03102555312216282, + 0.006796712521463633, + -0.002304798923432827, + -0.02888670563697815, + -0.006364178378134966, + 0.019535180181264877, + -0.01849023997783661, + -0.01348648127168417, + 0.030951134860515594, + 0.020918693393468857, + -0.008695859462022781, + 0.031143713742494583, + -0.07759401947259903, + 0.004797555971890688, + -0.041376516222953796, + -0.015465176664292812, + 0.04408504068851471, + 0.01237241830676794, + -0.001314751454629004, + 0.007341915741562843, + -0.048453085124492645, + -0.015066137537360191, + 0.0356559231877327, + 0.01633281260728836, + -0.005272608250379562, + -0.005688384175300598, + 0.004587365314364433, + 0.006708362139761448, + -0.008560605347156525, + 0.0006259714718908072, + -0.018221788108348846, + -0.014859207905828953, + -0.01629985310137272, + 0.00824007298797369, + -0.03146442025899887, + 0.005422351881861687, + 0.0143674835562706, + 0.019444450736045837, + 0.017136799171566963, + -0.056858692318201065, + 0.014826798811554909, + 0.007972562685608864, + 0.05685998499393463, + -0.011310789734125137, + -0.022868668660521507, + -0.025884339585900307, + 0.005378369241952896, + 0.015811456367373466, + 0.011171828024089336, + -0.02815665863454342, + -0.01307571679353714, + -0.028703372925519943, + -0.007115255109965801, + 0.0301078949123621, + -0.01192935649305582, + 0.06333417445421219, + -0.019967835396528244, + 0.04876632243394852, + -0.025175929069519043, + -0.02020110748708248, + -0.011821332387626171, + 0.014523796737194061, + -0.0037585159298032522, + 0.01914123259484768, + -0.03708731755614281, + 0.033562544733285904, + -0.011399144306778908, + 0.02005826123058796, + 0.01617334969341755, + -0.020389525219798088, + 0.0022239089012145996, + 0.009178928099572659, + 0.024524644017219543, + -0.04173111543059349, + -0.018681848421692848, + 0.012871353887021542, + 0.00881462823599577, + 0.048626646399497986, + 0.0030728185083717108, + 0.012346155941486359, + 0.02826548181474209, + 0.04011820629239082, + 0.014694946818053722, + 0.011294662952423096, + 0.006303024012595415, + -0.012262308038771152, + 0.048181772232055664, + -0.02887250855565071, + 0.009098690003156662, + 0.047251150012016296, + -0.0035308217629790306, + 0.004454194102436304, + 0.03042035736143589, + -0.012995733879506588, + -0.00737740145996213, + 0.023245694115757942, + -0.03642046079039574, + -0.024574019014835358, + -0.04807588830590248, + 0.012456048280000687, + -0.018762132152915, + -0.010792865417897701, + 0.001484883250668645, + -0.0011164979077875614, + 0.03611552342772484, + 0.021060800179839134, + 0.008755793794989586, + 0.03793724253773689, + -0.017644545063376427, + -0.01576140709221363, + 0.029865821823477745, + -0.009929166175425053, + 0.016870131716132164, + 0.02616770751774311, + -0.02220749296247959, + -0.004812058061361313, + 0.025103647261857986, + -0.00838998332619667, + 0.04135173186659813, + 0.004814141429960728, + 0.015099923126399517, + -0.0006994034629315138, + 0.02718389965593815, + -0.05558360368013382, + 0.02955326996743679, + 0.0038195843808352947, + 0.03499017283320427, + 0.019586937502026558, + -0.02214285545051098, + -0.006731558591127396, + -0.028908496722579002, + 0.043643172830343246, + -0.03747791796922684, + 0.017862655222415924, + 0.05938008427619934, + -0.013341791927814484, + -0.039971668273210526, + -0.02020523138344288, + -0.006764151155948639, + -0.019779445603489876, + -0.04545285180211067, + 0.05438382551074028, + -0.04678433761000633, + 0.03684389963746071, + -9.077700815396383e-05, + 0.026319924741983414, + -0.01304169837385416, + 0.02236766740679741, + 0.040449775755405426, + -0.00038494475302286446, + 0.02891152910888195, + 0.026549266651272774, + -0.006396479904651642, + 0.00853236485272646, + 0.04300742596387863, + -0.009140802547335625, + -0.0011178480926901102, + 0.01462595071643591, + 0.017056338489055634, + 0.009501023218035698, + 0.0030533713288605213, + 0.01112035196274519, + 0.012824972160160542, + -0.0037524327635765076, + 0.013632772490382195, + -0.01455234456807375, + -0.02297876961529255, + -0.0003816680400632322, + -0.04286552593111992, + -0.001495283329859376, + 0.001032632659189403, + -0.0025039485190063715, + 0.035405483096838, + 0.022536715492606163, + 0.020521122962236404, + -0.018743548542261124, + 0.006440264172852039, + 0.043989147990942, + -0.008645196445286274, + -0.01708645559847355, + -0.004591269418597221, + 0.02258365973830223, + 9.309872257290408e-05, + 0.01692030020058155, + -0.0173510629683733, + -0.027272889390587807, + -0.03729720041155815, + 0.0027984913904219866, + 0.021226130425930023, + -0.005971319042146206, + 0.03602922335267067, + -0.014363500289618969, + 0.03790959343314171, + 0.019991151988506317, + -0.026180656626820564, + 0.036414165049791336, + -0.01316072978079319, + 0.018245866522192955, + -0.03941069543361664, + 0.08117137849330902, + -0.01672777533531189, + 0.011604965664446354, + 0.04850604012608528, + 0.029708346351981163, + -0.012184511870145798, + 0.00691052945330739, + -0.003146028844639659, + -0.010375689715147018, + -0.03912202641367912, + -0.037747371941804886, + 0.02717292681336403, + 0.01577407866716385, + 0.0399494394659996, + -0.021285295486450195, + 0.011433955281972885, + 0.005085580516606569, + -0.01884562149643898, + 0.007327629253268242, + 0.020726745948195457, + 0.00954753439873457, + 0.052509207278490067, + 0.017245130613446236, + 0.03632960841059685, + 0.01902257278561592, + -0.021453801542520523, + -0.05716405436396599, + 0.014857574366033077, + -0.06564930081367493, + 0.0018294461769983172, + -0.03551369160413742, + 0.0019535089377313852, + -0.00014701746113132685, + 0.004140942357480526, + 0.031109539791941643, + -0.006346630863845348, + -0.011013209819793701, + 0.03514854609966278, + -0.030848421156406403, + -0.035942092537879944, + -0.00930361170321703, + -0.0068639894016087055, + 0.005480208434164524, + 0.0004995060153305531, + -0.015505040064454079, + -0.020242182537913322, + -0.03133612871170044, + -0.004781111143529415, + -0.030094033107161522, + 0.013344327919185162, + 0.32475194334983826, + -0.06958742439746857, + 0.03753884136676788, + -0.031769391149282455, + -0.013513601385056973, + -0.02668910287320614, + 0.07424389570951462, + 0.011780106462538242, + 0.01660735346376896, + 0.005310917738825083, + -0.014584461227059364, + 0.029685042798519135, + -0.004884897731244564, + 0.0368477888405323, + -0.00016014184802770615, + 0.009080477990210056, + -0.0018869563937187195, + -0.007670045830309391, + -0.05131123214960098, + -0.010577810928225517, + 0.02495170384645462, + 0.02602449059486389, + 0.010131493210792542, + -0.005605147685855627, + -0.011079715564846992, + -0.01892906241118908, + 0.026451630517840385, + -0.018821703270077705, + -0.00777859752997756, + -0.04698039963841438, + 0.01409924402832985, + 0.07844281196594238, + -0.06741826981306076, + 0.02192114107310772, + 0.014353527687489986, + -0.02290293201804161, + 0.007702843751758337, + -0.005961514078080654, + -0.009072556160390377, + 0.01759834960103035, + -0.07305744290351868, + 0.009813819080591202, + -0.0054367901757359505, + -0.021026993170380592, + -0.014950424432754517, + -0.004862137604504824, + 0.01150705199688673, + -0.009989770129323006, + 0.05701877176761627, + -0.035320352762937546, + -0.02080143243074417, + 0.01510948408395052, + -0.03634849190711975, + -0.0016099062049761415, + 0.02914208173751831, + -0.13209302723407745, + -2.672583650564775e-05, + -0.04427105933427811, + 0.016992712393403053, + -0.004861252848058939, + 0.005783486180007458, + -0.03330102562904358, + 0.0105277756229043, + -0.00557948462665081, + -0.023262368515133858, + 0.009907380677759647, + -0.012673381716012955, + 0.020337890833616257, + 0.0026226555928587914, + -0.012922944501042366, + -0.01034557819366455, + 0.02925957553088665, + 0.03086075372993946, + -0.010503451339900494, + -0.010083735920488834, + -0.02743344008922577, + -0.043663140386343, + 0.005118625704199076, + -0.06860420852899551, + 0.0013315173564478755, + -0.0015009174821898341, + -0.020727116614580154, + 0.005627341568470001, + 0.010105814784765244, + -0.031182166188955307, + 0.0312103983014822, + 0.03958325460553169, + -0.020433390513062477, + 0.018250983208417892, + -0.0027858088724315166, + 0.008058113977313042, + 0.029568709433078766, + -0.025961581617593765, + -0.017692651599645615, + 0.02142471820116043, + 0.0038888067938387394, + 0.020903829485177994, + 0.05379735305905342, + 0.017889536917209625, + 0.036379411816596985, + -0.02521454729139805, + -0.04533204808831215, + -0.004042030777782202, + 0.01035345159471035, + -0.007775322999805212, + 0.02018476463854313, + -3.371599086676724e-05, + -0.02103378437459469, + 0.0105480020865798, + 0.00886007770895958, + 0.0013533030869439244, + 0.020566001534461975, + 0.020033499225974083, + -0.005746206268668175, + -0.015700049698352814, + 0.03726721554994583, + -0.027829209342598915, + 0.008189870044589043, + -0.017538391053676605, + 0.018586626276373863, + -0.03802782669663429, + 0.004859105218201876, + -0.005865784361958504, + -0.015039139427244663, + -0.013464425690472126, + -0.015253962017595768, + 0.0682622492313385, + -0.039533138275146484, + -0.09115038067102432, + 0.038695644587278366, + 0.026839841157197952, + -0.04009610414505005, + 0.010093946941196918, + -0.05761738121509552, + 0.045265957713127136, + -0.006985533982515335, + -0.004451907705515623, + -0.03905605524778366, + 0.023089982569217682, + -0.030598122626543045, + 0.020000912249088287, + 0.0013680788688361645, + -0.0024794714991003275, + -0.013932582922279835, + 0.0438564196228981, + 0.01383215468376875, + -0.012081182561814785, + 0.02062971517443657, + -0.007211109157651663, + 0.028903068974614143, + 0.033005837351083755, + -0.027542777359485626, + -0.012600542977452278, + 0.0351024754345417, + -0.040100350975990295, + 0.020409341901540756, + 0.017572518438100815, + -0.008493520319461823, + 0.04824360832571983, + -0.01554966252297163, + -0.01896580681204796, + -0.01977735571563244, + -0.012926415540277958, + -0.032010965049266815, + -0.04533889889717102, + -0.018802523612976074, + -0.030251268297433853, + -0.09667984396219254, + 0.01658632792532444, + -0.13103501498699188, + 0.025513507425785065, + 0.01875150017440319, + -0.021666891872882843, + -0.005271135363727808, + -0.06486763805150986, + 0.019437192007899284, + -0.016214173287153244, + -0.022431660443544388, + 0.01096654124557972, + -0.014731446281075478, + 0.025144372135400772, + 0.013309674337506294, + 0.04044188931584358, + 0.013911898247897625, + -0.025746610015630722, + -0.02106257900595665, + -0.021889904513955116, + -0.021825766190886497, + 0.005973095074295998, + 0.0502234622836113, + -0.04302624613046646, + -0.045582693070173264, + 0.009578384459018707, + -0.03550545126199722, + -0.002098388271406293, + -0.013642232865095139, + 0.012743987143039703, + -0.015648437663912773, + 0.009871698915958405, + -0.025764023885130882, + 0.03066958673298359, + -0.025689352303743362, + 0.006690533831715584, + -0.0017962473211809993, + -0.017000477761030197, + -0.01480826921761036, + -0.002763735596090555, + -0.016594648361206055, + 0.012822565622627735, + 0.015145898796617985, + 0.026539670303463936, + 0.025578705593943596, + -0.017212342470884323, + 0.055205971002578735, + 0.014201231300830841, + -0.005974567029625177, + 0.015204210765659809, + 0.012353937141597271, + -0.004220457281917334, + -0.02378523163497448, + 0.012976976111531258, + -0.020361261442303658, + -0.011514701880514622, + 0.016861626878380775, + 0.022682201117277145, + -0.0009491418022662401, + 0.032794270664453506, + 0.006119191646575928, + -0.0020636776462197304, + 0.025991166010499, + -0.04192587733268738, + 0.008359107188880444, + 0.006672253832221031, + -0.016124965623021126, + 0.012279867194592953, + -0.005093001294881105, + 0.01359865814447403, + -0.015565218403935432, + -0.02186563052237034, + -0.03619864583015442, + 0.03856077045202255, + 0.019228173419833183, + -0.011687328107655048, + 0.024228354915976524, + -0.07897584140300751, + -0.032361481338739395, + -0.030455730855464935, + 0.00017301199841313064, + 0.0338718518614769, + 0.019410425797104836, + -0.034781038761138916, + -0.012547675520181656, + 0.018370212987065315, + 0.04182573780417442, + -0.000790441466961056, + -0.009596052579581738, + -0.025040550157427788, + 0.01286687795072794, + 0.020074591040611267, + -0.01286032423377037, + -0.032856736332178116, + 0.003239352023229003, + 0.010728140361607075, + 0.033584114164114, + 0.012277199886739254, + -0.012359078973531723, + 0.03614939749240875, + -0.006195769179612398, + 0.004444224759936333, + 0.014321080408990383, + 0.004338945262134075, + 0.002403527731075883, + 0.022683659568428993, + -0.018167929723858833, + 0.0139817139133811, + -0.12097100168466568, + -0.02045736275613308, + -0.017137527465820312, + 0.018200932070612907, + 0.011092481203377247, + 0.012648449279367924, + -0.034793782979249954, + 0.04664189741015434, + -0.03307272493839264, + -0.01692374050617218, + 0.027230890467762947, + -0.02348896861076355, + 0.009879854507744312, + 0.010750719346106052, + -0.013809691183269024, + 0.046936679631471634, + 0.0011544657172635198, + 0.00786908995360136, + -0.010044638067483902, + 0.009282024577260017, + -0.010493047535419464, + 0.008565165102481842, + -0.025157568976283073, + -0.29173728823661804, + 0.013949385844171047, + 0.0025355268735438585, + -0.0028417964931577444, + -0.014534158632159233, + 0.019054260104894638, + 0.03324208781123161, + -0.005634466651827097, + 0.011742379516363144, + -0.0010122595122084022, + 0.008319241926074028, + 0.004957471042871475, + 0.05546383187174797, + 0.0038378245662897825, + 0.03972291573882103, + 0.008058883249759674, + -0.023597659543156624, + -0.03188700973987579, + 0.006023040972650051, + 0.006977546494454145, + 0.021555021405220032, + -0.019637245684862137, + -0.059364646673202515, + -0.007125036790966988, + 0.04711782932281494, + 0.020369533449411392, + -0.016837554052472115, + 0.013841911219060421, + -0.01558474637567997, + -0.003170103533193469, + 0.029336726292967796, + 0.037967249751091, + 0.02759557217359543, + -0.0417131632566452, + 0.016852427273988724, + -0.03945450857281685, + -0.006667551584541798, + -0.032106220722198486, + 0.015851087868213654, + 0.032978326082229614, + 0.02110578492283821, + 0.0033853796776384115, + -0.04418572410941124, + -0.0696202740073204, + 0.005492370575666428, + -0.03827675059437752, + 0.006642703432589769, + 0.0028332415968179703, + -0.016967277973890305, + -0.013572437688708305, + 0.023930272087454796, + -0.01795697957277298, + -0.0020174579694867134, + -0.009210770018398762, + 0.013568999245762825, + 0.0421738438308239, + -0.004998510703444481, + 0.07460629194974899, + 0.0042349244467914104, + 0.015117245726287365, + 0.001528570312075317, + 0.007907961495220661, + -0.03842604160308838, + 0.057787876576185226, + 0.016613181680440903, + -0.011943853460252285, + -0.013587740249931812, + -0.020569628104567528, + -0.02105863206088543, + -0.012139711529016495, + 0.005354769993573427, + -0.012719197198748589, + -0.036632802337408066, + -0.01394461840391159, + -0.011006815358996391, + -0.016503311693668365, + 0.009349741041660309, + 0.02100297063589096, + 0.05526133254170418, + -0.004661911632865667, + 0.037705592811107635, + -0.03485500067472458, + 0.027028698474168777, + 0.01940314471721649, + 0.01832515001296997, + 0.011845597997307777, + -0.006262816954404116, + -0.06510013341903687, + 0.019574562087655067, + -0.007936508394777775, + 0.016640352085232735, + 0.0127026978880167, + 0.032653819769620895, + 0.037401992827653885, + 0.025971774011850357, + -0.016669506207108498, + 0.0007034823065623641, + -0.011938261799514294, + -0.03381223976612091, + 0.01628376916050911, + -0.03630426898598671, + 0.024630095809698105, + -0.005219586193561554, + -0.022636333480477333, + -0.052746716886758804, + 0.003010333515703678, + 0.03640617057681084, + -0.017377691343426704, + 0.015866827219724655, + 0.07708118110895157, + 0.005253201816231012, + 0.024780046194791794, + -0.008430768735706806, + 0.009078362956643105, + -0.10029767453670502, + 0.016435107216238976, + 0.04496238753199577, + 0.01763910986483097, + -0.08092337846755981, + -0.01116535346955061, + 0.014742461033165455, + -0.0033168469090014696, + 0.010251753963530064, + -0.07602939754724503, + 0.02751883491873741, + -0.03276529908180237, + 0.029737861827015877, + 0.0053010969422757626, + -0.038456372916698456, + 0.03537299856543541, + -0.01625823602080345, + 0.02014942467212677, + 0.10032647848129272, + 0.004421289078891277, + 0.003202186431735754, + -0.0326516292989254, + 0.013873237185180187, + -0.0037363944575190544, + -0.017871882766485214, + -0.03911822289228439, + 0.03695815056562424, + 0.006338862702250481, + 0.014280135743319988, + -0.006673009600490332, + 0.010603968054056168, + -1.1013704352080822e-05, + 0.005263235419988632, + -0.00858763325959444, + -0.056535158306360245, + 0.029054107144474983, + -0.002437105169519782, + 0.015111315064132214, + 0.008714341558516026, + -0.00596291059628129, + -0.03671013563871384, + 0.035285744816064835, + -0.017748534679412842, + 0.04087936133146286, + -0.006747189909219742, + 0.021935313940048218, + -0.02657698467373848, + -0.009591273032128811, + 0.02753169648349285, + 0.020021677017211914, + -0.034542787820100784, + -0.02012575790286064, + 0.01744294911623001, + -0.01033166702836752, + 0.03164916858077049, + 0.02696276269853115, + -0.03547533601522446, + 0.02639179863035679, + -0.016664104536175728, + -0.021315889433026314, + -0.024272745475172997, + 0.009747182950377464, + -0.02794734202325344, + 0.018592389300465584, + 0.00319254444912076, + -0.04054354503750801, + -0.023697586730122566, + 0.010227290913462639, + -0.01767677813768387, + 0.018415575847029686, + 0.0013129286235198379, + 0.037213284522295, + 0.02123964950442314, + 0.013665451668202877, + -0.017313571646809578, + 0.023441685363650322, + 0.013160991482436657, + -0.000715607195161283, + -0.025192180648446083, + -0.03170442953705788, + -0.03373144567012787, + -0.008036592975258827, + -0.02809218131005764, + -0.003330197650939226, + -0.00027988391229882836, + -0.023723632097244263, + 0.02380118891596794, + -0.0399668887257576, + 0.08898408710956573, + -0.01298504788428545, + -0.002958949189633131, + -0.08931790292263031, + 0.01794985495507717, + 0.026356235146522522, + 0.0037359052803367376, + -0.011422591283917427, + -0.011442299000918865, + 0.04301318898797035, + -0.04707515612244606, + 0.027757341042160988, + -0.036772359162569046, + 0.022463833913207054, + 0.0013122071977704763, + -0.008051300421357155, + -0.040015943348407745, + 0.006165364757180214, + 0.029261460527777672, + -0.0030607921071350574, + -0.005030003376305103, + -0.009542854502797127, + 0.02008034847676754, + -0.00919965747743845, + 0.0373181514441967, + -0.009840010665357113, + 0.0020545152947306633, + 0.017362356185913086, + -0.006654149852693081, + -0.04468706622719765, + 0.009167804382741451, + 0.0013872403651475906, + 0.019085252657532692, + -0.03390924632549286, + -0.055864524096250534, + -0.007083230186253786, + -0.03852374479174614, + 0.021109934896230698, + -0.03182285651564598, + 0.000604458968155086, + -0.035531558096408844, + 0.005026982631534338, + -0.017174655571579933, + -0.019234981387853622, + 0.05665444955229759, + 0.02842988260090351, + -0.05267392471432686, + 0.02009234018623829, + 0.03208636865019798, + -0.015663623809814453, + -0.018596559762954712, + -0.040188368409872055, + 0.021310491487383842, + 0.005694539286196232, + -0.03644813969731331, + 0.03875624015927315, + -0.02142355591058731, + -0.01352705992758274, + -0.03299194946885109, + 0.011877035722136497, + -0.046910569071769714, + 0.03146253526210785, + -0.02362813428044319, + 0.012542937882244587, + -0.026769118383526802, + -0.02783053182065487, + 0.04991021007299423 + ] + }, + { + "name": "ea86ca30b346f27ca5faf1254f6a31e3.png", + "file-path": "E:\\Pratham\\2025\\Harsh Sir\\Scratch Vision\\images\\Backdrops\\hall.sb3\\ea86ca30b346f27ca5faf1254f6a31e3.png", + "embeddings": [ + 0.0018283040262758732, + -0.027339842170476913, + 0.025590980425477028, + 0.009768425486981869, + 0.024387869983911514, + 0.010549269616603851, + -0.0683671236038208, + 0.014046577736735344, + 0.0036398570518940687, + 0.03635953366756439, + -0.0039847902953624725, + 0.026268621906638145, + -0.01725403219461441, + 0.04289010167121887, + -0.002529166406020522, + 0.04049426689743996, + -0.008778069168329239, + -0.012155083939433098, + 0.00563764339312911, + -0.022470925003290176, + 0.040031641721725464, + -0.02728983387351036, + -0.005234991200268269, + 0.03866909444332123, + 0.008672116324305534, + 0.00023783283540979028, + 0.000620405888184905, + -0.008733676746487617, + -0.010943874716758728, + 0.01665843464434147, + 0.017589006572961807, + -0.002285740338265896, + 0.01673152670264244, + 0.014074173755943775, + -0.010158889926970005, + -0.03513895347714424, + 0.01849582977592945, + -0.014487245120108128, + -0.017767349258065224, + -0.01215033046901226, + -0.024341490119695663, + 0.015448235906660557, + 0.013083417899906635, + -0.027534276247024536, + -0.05745258554816246, + -0.007079729810357094, + -0.012259448878467083, + 0.07432650029659271, + 0.015212972648441792, + 0.005710296798497438, + 0.017864922061562538, + 0.0022633234038949013, + 0.028569433838129044, + 0.004390838090330362, + 0.006287536583840847, + 0.03410887345671654, + 0.014773858711123466, + 0.016787055879831314, + 0.007520708255469799, + 0.008434447459876537, + -0.03192787244915962, + -0.023467186838388443, + -0.02369925007224083, + -0.028755387291312218, + 0.031684067100286484, + -0.018851924687623978, + 0.031497515738010406, + 0.009062481112778187, + -0.039704468101263046, + -0.00937595497816801, + 0.023844582960009575, + -0.03329392150044441, + -0.016398724168539047, + 0.018052753061056137, + 0.005756615661084652, + -0.02499651536345482, + 0.046843353658914566, + 0.004803828429430723, + 0.036038532853126526, + -0.03692243993282318, + 0.009043442085385323, + -0.011417464353144169, + 0.008471696637570858, + 0.011218264698982239, + 0.020381076261401176, + -0.04143550992012024, + -0.020491424947977066, + 0.022254247218370438, + 0.002040393650531769, + 0.0014020497910678387, + -0.037592798471450806, + 0.028729664161801338, + -0.03637653961777687, + 0.02396826259791851, + -0.0036280944477766752, + 0.005489063914865255, + -0.11699336022138596, + -0.009401933290064335, + -0.05909718945622444, + 0.03749336302280426, + 0.02985125407576561, + 0.07041062414646149, + -0.02140243537724018, + 0.022901706397533417, + -0.06693108379840851, + -0.03243130072951317, + 0.01886197365820408, + -0.05697386711835861, + -0.05120033398270607, + -0.030682463198900223, + -0.028637874871492386, + -5.1982406148454174e-05, + -0.00793104525655508, + 0.0356237031519413, + 0.002628771821036935, + 0.005192610435187817, + -0.02185320481657982, + 0.021761342883110046, + -0.027112295851111412, + 0.004629370756447315, + 0.02787201479077339, + 0.0008840402006171644, + -0.008701090700924397, + 0.000428241299232468, + -0.020747004076838493, + 0.004684613086283207, + -0.022311050444841385, + 0.02528402954339981, + 0.0068841539323329926, + -0.03542231023311615, + 0.025392062962055206, + 0.06251755356788635, + -0.014591364189982414, + 0.013783769682049751, + -0.020164713263511658, + 0.045141831040382385, + -0.013403099961578846, + 0.007133543025702238, + -0.0032511246390640736, + 0.0010077734477818012, + -0.022449318319559097, + 0.029487501829862595, + 0.02250690571963787, + -0.015637269243597984, + 0.02134176902472973, + 0.009715716354548931, + 0.03410571441054344, + 0.024588774889707565, + -0.05585926026105881, + 0.0316653847694397, + 0.004737613257020712, + 0.0024373282212764025, + -0.004114197567105293, + 0.012259046547114849, + 0.01122880820184946, + -0.0034499182365834713, + -0.015095233917236328, + 0.018306611105799675, + 0.002723818179219961, + 0.013358737342059612, + -0.0007771229138597846, + -0.033852655440568924, + -0.044063590466976166, + 0.001812279922887683, + 0.017225494608283043, + 0.004011877812445164, + -0.001905984478071332, + -0.003010083455592394, + 0.004195428919047117, + -0.009617325849831104, + -0.006328278686851263, + -0.0009456397383473814, + -0.009168514981865883, + -0.023580746725201607, + 0.009230188094079494, + 0.017977185547351837, + 0.014292784035205841, + 0.0026560407131910324, + -0.00610363157466054, + -0.015964144840836525, + -0.012509682215750217, + -0.03618476539850235, + 0.005757315084338188, + -0.0018343408592045307, + 0.031005680561065674, + 0.023562243208289146, + -0.021882111206650734, + 0.025053048506379128, + -0.003640754148364067, + 0.01997769996523857, + 0.022582586854696274, + 0.03588040545582771, + -0.032090362161397934, + 0.006996571086347103, + 0.02804337814450264, + -0.004611958283931017, + -0.008985141292214394, + 0.03422209247946739, + -0.020751895383000374, + -0.013629194349050522, + -0.005501963663846254, + -0.0009773597121238708, + -0.0009391799103468657, + -0.0003794570220634341, + -0.009409267455339432, + 0.0016572346212342381, + 0.0037215801421552896, + 0.02088538557291031, + -0.0223012063652277, + 0.0005967335309833288, + 0.004749330226331949, + 0.019684812054038048, + 0.008149711415171623, + -0.09594143182039261, + 0.011719273403286934, + -0.008625653572380543, + 0.05073227360844612, + 0.01670360006392002, + -0.02731851302087307, + 0.014186456799507141, + -0.15340320765972137, + 0.011327570304274559, + 0.012042468413710594, + -0.0177232027053833, + 0.012427014298737049, + 0.004440257791429758, + 0.04897835850715637, + 0.03547917678952217, + -0.08602035790681839, + -0.0511552132666111, + -0.012666100636124611, + 0.016397753730416298, + 0.015227412804961205, + 0.011432306841015816, + -0.005169196520000696, + 0.11208724975585938, + 0.01135840080678463, + 0.027032386511564255, + -0.008549186401069164, + 0.01697089709341526, + -0.03857201337814331, + 0.0026142443530261517, + -0.00798946525901556, + -0.022798113524913788, + -0.02688494883477688, + -0.04367641359567642, + -0.013064450584352016, + 0.024080907925963402, + 6.249057332752272e-05, + -0.026049835607409477, + 0.055727291852235794, + -0.015922216698527336, + -0.03956320136785507, + 0.03712119534611702, + -0.04215265065431595, + 0.012768277898430824, + -0.015887629240751266, + 0.018376341089606285, + -0.05264689028263092, + 0.024120351299643517, + 0.0036491109058260918, + -0.0015852958895266056, + -0.005596648436039686, + 0.010300135239958763, + -0.04810263589024544, + 0.0005294244620017707, + 0.028082098811864853, + 0.02139510214328766, + -0.04133074730634689, + -0.038527000695466995, + 0.03984745219349861, + 0.036208875477313995, + -0.02415701188147068, + -0.010751762427389622, + 0.003168914932757616, + 0.0261528380215168, + 0.015992755070328712, + -0.0002001240209210664, + -0.015455509535968304, + -0.010430147871375084, + -0.03159927949309349, + -0.016475984826683998, + -0.05640750005841255, + 0.01952834613621235, + 0.04422283172607422, + -0.0015921861631795764, + -0.00971223134547472, + -0.032377056777477264, + 0.06067056953907013, + -0.011491243727505207, + 0.026559144258499146, + -0.0032155148219317198, + 0.03021940588951111, + 0.027570335194468498, + -0.03241194039583206, + -0.0014351379359140992, + -0.050950128585100174, + 0.006179295480251312, + 0.005721712484955788, + 0.01788528822362423, + 0.005450934171676636, + 0.006792753469198942, + 0.01906612329185009, + -0.0010990474838763475, + -0.013343626633286476, + -0.034784890711307526, + 0.004611956421285868, + 0.0005985503667034209, + -0.029452862218022346, + -0.0229131318628788, + 0.004225625656545162, + -0.008773315697908401, + -0.018672136589884758, + 0.03587678074836731, + 0.029583081603050232, + 0.005249695852398872, + -0.040806643664836884, + -0.023693600669503212, + -0.01699809916317463, + 0.0008044522837735713, + -0.0024978909641504288, + -0.02122880145907402, + 0.009969484992325306, + -0.0185712780803442, + -0.008284141309559345, + -0.005650905892252922, + 0.025090640410780907, + -0.009074082598090172, + 0.02570640668272972, + -0.027925686910748482, + 0.002941067796200514, + 0.032476406544446945, + 0.029284892603754997, + -0.011669420637190342, + 0.029555564746260643, + 0.011112322099506855, + 0.01513612549751997, + -0.008648686110973358, + -0.014906036667525768, + 0.00889721978455782, + -0.010230359621345997, + 0.006558829452842474, + -0.016247592866420746, + 0.008248467929661274, + 0.028750089928507805, + -0.03299733251333237, + -0.0057274457067251205, + -0.009484588168561459, + 0.004950687754899263, + 0.01889149285852909, + 0.010380065068602562, + -0.006620329804718494, + -0.002607142785564065, + 0.004903991706669331, + -0.004689604975283146, + -0.029991844668984413, + 0.01768292300403118, + -0.007507100701332092, + 0.01975156180560589, + 0.04661000519990921, + -0.015585585497319698, + -0.08497966825962067, + 0.000318038190016523, + -0.0027031786739826202, + 0.028147734701633453, + 0.017902718856930733, + 0.002554277889430523, + 0.006267320830374956, + 0.029182856902480125, + 0.002502507995814085, + -0.007874990813434124, + -0.0011073291534557939, + -0.013981829397380352, + -0.055649228394031525, + -0.005992651451379061, + 0.020016590133309364, + 0.0029765137005597353, + 0.007713140919804573, + 0.02631092444062233, + -0.0225762240588665, + -0.002779295900836587, + 0.053352970629930496, + -0.02464217133820057, + 0.07302174717187881, + 0.011090463027358055, + -0.03503163158893585, + -0.006080994848161936, + 0.011884029023349285, + -0.0015060807345435023, + -0.05443693324923515, + 0.015268315561115742, + 0.007646800950169563, + 0.01741192676126957, + 0.0030642130877822638, + 0.00532731506973505, + -0.009802929125726223, + 0.006725892424583435, + -0.02902350388467312, + 0.03203997015953064, + -0.020880836993455887, + 0.046358734369277954, + -0.004927527159452438, + 0.0010932181030511856, + 0.026746924966573715, + -0.002433305373415351, + -0.006177188362926245, + 0.019004184752702713, + 0.007907460443675518, + 0.00496045732870698, + 0.038418062031269073, + 0.04503031075000763, + 0.03272898122668266, + -0.01954597979784012, + 0.009631927125155926, + 0.01367550902068615, + 0.03680064529180527, + 0.014829523861408234, + -0.005620679818093777, + -0.023870263248682022, + 0.036217208951711655, + 0.02716698683798313, + -0.02643571048974991, + 0.02540137618780136, + 0.00887372437864542, + -0.08187936246395111, + 0.0015542882028967142, + -0.007497632410377264, + 0.01476262230426073, + 0.01667111925780773, + 0.012260397896170616, + 0.04749022051692009, + -0.02079460211098194, + -0.03223331272602081, + -0.006676402408629656, + -0.03471097722649574, + -0.058983203023672104, + -0.016057631000876427, + 0.004939579870551825, + -0.00391428591683507, + -0.014703149907290936, + 0.009795498102903366, + -0.025273675099015236, + -0.028435181826353073, + 0.012726184912025928, + 0.008016770705580711, + -0.027641484513878822, + -0.032639503479003906, + 0.03366653248667717, + 0.016605710610747337, + -0.033138059079647064, + 0.010694315657019615, + 0.047580257058143616, + -0.02038080245256424, + -0.033344052731990814, + -0.019299253821372986, + -0.04437815025448799, + 0.021328400820493698, + 0.3610589802265167, + -0.012391587719321251, + -0.00022409613302443177, + -0.01387573778629303, + 0.024699224159121513, + -0.06410364806652069, + -0.009027358144521713, + -0.02262232080101967, + -0.005281554069370031, + 0.0012049704091623425, + -0.010333791375160217, + -0.019233375787734985, + 0.0014489615568891168, + -0.000357885000994429, + -0.004435808397829533, + 0.007268794812262058, + 0.010147016495466232, + 0.03371834382414818, + -0.06604574620723724, + -0.024852603673934937, + -0.06702136993408203, + 0.02582612633705139, + -0.024516791105270386, + 0.01576266810297966, + 0.01161093171685934, + -0.0038752732798457146, + 0.011276477947831154, + -0.016353411599993706, + -0.013685706071555614, + 0.025138860568404198, + 0.024872584268450737, + -0.0012777269585058093, + 0.019508961588144302, + 0.0132612818852067, + -0.008513770997524261, + 0.021612824872136116, + -0.02565639838576317, + -0.01060846634209156, + -0.019441109150648117, + 0.04237227514386177, + 0.023047422990202904, + 0.0009652925655245781, + -0.009409909136593342, + 0.008072241209447384, + -0.012083109468221664, + -0.023748790845274925, + 0.007525838445872068, + -0.04071469232439995, + -0.006630127318203449, + -0.04283542186021805, + 0.02389867976307869, + 0.012412921525537968, + -0.04418599233031273, + -0.07516643404960632, + -0.012188954278826714, + -0.0007036899332888424, + 0.007216928992420435, + -0.046090267598629, + -0.009791163727641106, + -0.005825526546686888, + 0.0205112062394619, + 0.006964400876313448, + -4.805308344657533e-05, + -0.028337955474853516, + 0.03354751691222191, + -0.0044574677012860775, + -0.010952147655189037, + 0.013737611472606659, + 0.0317000150680542, + 0.030870383605360985, + -0.0051226564683020115, + -0.0010931056458503008, + 0.06540580838918686, + 0.031584639102220535, + -0.0009176937164738774, + -0.0663849338889122, + -0.012996016070246696, + 0.0038568226154893637, + -0.06439412385225296, + 0.018284432590007782, + -0.03919007629156113, + 0.015185849741101265, + -0.011988179758191109, + -0.023724475875496864, + 0.0018586443038657308, + 0.0194836612790823, + 0.01563280075788498, + 0.03784811124205589, + 0.002659310819581151, + -4.387916487758048e-05, + -0.01874850131571293, + 0.014849992468953133, + 0.002431786386296153, + -0.009893802925944328, + -0.05080978572368622, + 0.05417894572019577, + -0.006346860434859991, + -0.0012404018780216575, + 0.013944783248007298, + -0.002621650928631425, + 0.02189847268164158, + -0.006245495285838842, + -0.015222534537315369, + -0.01242382824420929, + -0.005523804575204849, + -0.02561783976852894, + 0.012878441251814365, + 0.00016562480595894158, + 0.017739593982696533, + 0.026145633310079575, + 0.001930971979163587, + 0.002799403853714466, + -0.002497965469956398, + 0.02940988540649414, + -0.014502120204269886, + 0.0006734231719747186, + -0.01630759984254837, + -0.022209202870726585, + -0.022455504164099693, + -0.008585606701672077, + 0.007266589440405369, + -0.02195863053202629, + -0.007934552617371082, + 0.00665790680795908, + 0.02631917968392372, + 0.018656253814697266, + 0.14115598797798157, + 0.01883753202855587, + 0.06675892323255539, + -0.014565263874828815, + 0.05045481026172638, + -0.04861630126833916, + 0.014318804256618023, + 0.007841979153454304, + 0.026869570836424828, + -0.02374439500272274, + -0.027687717229127884, + 0.0029199481941759586, + -0.044563211500644684, + 0.016813375055789948, + 0.00816156342625618, + -0.009875823743641376, + 0.036528658121824265, + 0.0034165435936301947, + -0.006265893578529358, + 0.020322587341070175, + 0.003517923876643181, + 0.007510736584663391, + 0.006118054036051035, + 0.026397980749607086, + 0.004737108945846558, + -0.02972208708524704, + 0.001172165502794087, + 0.027063511312007904, + 0.010157615877687931, + -0.0017009966541081667, + -0.04639546573162079, + -0.015577442944049835, + 0.06258200854063034, + 0.018993452191352844, + -0.0019588321447372437, + 0.04512394964694977, + -0.03286061808466911, + -0.03923659399151802, + -0.001437497092410922, + -0.009007208980619907, + -0.02997652254998684, + 0.07956263422966003, + 0.013204875402152538, + 0.048389557749032974, + 0.011810758151113987, + -0.01303349994122982, + 0.02147495374083519, + -0.011358334682881832, + -0.17229866981506348, + -0.015598207712173462, + 0.008798829279839993, + -0.05577941983938217, + -0.028049658983945847, + -0.022157413884997368, + 0.024355608969926834, + 0.003945139702409506, + 0.04352649673819542, + -1.4275232388172299e-05, + -0.07111766189336777, + -0.019901785999536514, + 0.07044698297977448, + -0.030963920056819916, + 0.008893022313714027, + 0.06261442601680756, + -0.025439171120524406, + -0.022516561672091484, + -0.014040120877325535, + 0.007688768208026886, + 0.009647296741604805, + -0.010780418291687965, + 0.0012978025479242206, + 0.005420411471277475, + 0.003981962334364653, + 0.023770354688167572, + 0.0037408724892884493, + 0.016817165538668633, + -0.0019811540842056274, + 0.0009480637963861227, + 0.016737183555960655, + 0.02755780890583992, + -0.026872685179114342, + 0.019893359392881393, + -0.004662444349378347, + 0.032772246748209, + -0.016008591279387474, + 0.030097486451268196, + 0.018170595169067383, + 0.018910573795437813, + -0.015792541205883026, + -0.0004348409711383283, + -0.04427994042634964, + -0.026262199506163597, + 0.020910948514938354, + -0.0017399812350049615, + -0.0008366582333110273, + -0.023298047482967377, + -0.01197825837880373, + 0.006862990092486143, + -0.028782933950424194, + 0.013347472064197063, + 0.019406680017709732, + 0.005931025370955467, + 0.01580452173948288, + 0.027643166482448578, + -0.000594684446696192, + -0.00048170555965043604, + -0.07104430347681046, + 0.026898162439465523, + 0.005154372192919254, + 0.006794585380703211, + 0.04039337486028671, + -0.031652212142944336, + -0.0018366234144195914, + -0.02436143532395363, + 0.007723602931946516, + 0.0012858322588726878, + -0.018216166645288467, + 0.02368474192917347, + 0.025255119428038597, + -0.005942363291978836, + 0.008261197246611118, + -0.0010414208518341184, + 0.046644020825624466, + 0.002219299552962184, + -0.02689496800303459, + -0.01161104254424572, + -0.00366608751937747, + 0.012508726678788662, + -0.003600846277549863, + -0.0037438480649143457, + -0.0014310305705294013, + -0.0022646458819508553, + 0.01699909009039402, + -0.009007764048874378, + 0.03860777989029884, + 0.01222810335457325, + -0.03414086997509003, + -0.032005250453948975, + 0.03301055356860161, + -0.03354060649871826, + 0.025963185355067253, + 0.027994373813271523, + -0.022174889221787453, + 0.0034008610527962446, + 0.0013721151044592261, + 0.041138239204883575, + 0.024469876661896706, + 0.09860667586326599, + -0.006286724470555782, + 0.0157585721462965, + 0.004108059220016003, + -0.033391620963811874, + 0.00711823208257556, + 0.012566180899739265, + 0.022258486598730087, + -0.0005283909849822521, + 0.024815542623400688, + -0.051200807094573975, + 0.023403046652674675, + -0.026492290198802948, + 0.016394436359405518, + 0.02521608956158161, + 0.009371945634484291, + 0.037625283002853394, + 0.02597210928797722, + -0.0009334227070212364, + -0.0323149636387825, + -0.02339734137058258, + -0.027646154165267944, + -0.0701485276222229, + -0.023906400427222252, + -0.010577525943517685, + -0.32236653566360474, + 0.00703719537705183, + 0.005643519572913647, + -0.047247033566236496, + -0.012894058600068092, + 0.02317545935511589, + 0.0147833451628685, + -0.011406656354665756, + -0.003406585194170475, + 0.004609163384884596, + -0.0045120068825781345, + -0.012263374403119087, + 0.05988433212041855, + -0.013017424382269382, + 0.031898707151412964, + 0.021155912429094315, + 0.019830157980322838, + -0.02663656510412693, + 0.007883296348154545, + 0.025251569226384163, + 0.02427295409142971, + 0.014165962114930153, + -0.005231764167547226, + -0.03370574489235878, + 0.0710265040397644, + -0.0048750038258731365, + -0.055800557136535645, + -0.014684490859508514, + -0.043156348168849945, + -0.01224976684898138, + -0.024003442376852036, + -0.018047932535409927, + 0.004530755802989006, + 0.007211668882519007, + 0.008822374977171421, + -0.02326040156185627, + -0.022683357819914818, + 0.012928701937198639, + -0.02667861059308052, + 0.0038096948992460966, + 0.016490036621689796, + -0.006815070752054453, + -0.017823699861764908, + -0.018041269853711128, + -0.006557623855769634, + 0.004400656558573246, + -0.006966083310544491, + -0.030825093388557434, + -0.037737660109996796, + -0.010890855453908443, + -0.017785806208848953, + 0.006355959922075272, + 0.061114437878131866, + -0.034295305609703064, + -0.004522199742496014, + 0.018229763954877853, + -0.004005917813628912, + 0.057518038898706436, + -0.02462613582611084, + 0.03432483226060867, + 0.0037117484025657177, + -0.02944958582520485, + 0.022221414372324944, + -0.019110720604658127, + 0.0091020492836833, + -0.012951716780662537, + 0.013488996773958206, + -0.043339721858501434, + 0.002630739938467741, + 0.012856214307248592, + -0.008410525508224964, + -0.011489640921354294, + 0.02433893084526062, + -0.005484713241457939, + -0.024379950016736984, + 0.01948021724820137, + -0.0010595383355394006, + -0.030474655330181122, + 0.013998500071465969, + -0.014548074454069138, + -0.025295989587903023, + 0.03207910433411598, + -0.03159229829907417, + -0.01791965216398239, + 0.01760558784008026, + 0.005553355440497398, + -0.0010814189445227385, + 0.03846399486064911, + -0.005491361953318119, + -0.01806769147515297, + 0.009573052637279034, + 0.011945506557822227, + -0.028573965653777122, + 0.01174613181501627, + 0.018586058169603348, + -0.013782924041152, + 0.005243195686489344, + -0.03238625451922417, + -0.010498839430510998, + -0.02703295648097992, + -0.04744585230946541, + 0.008703072555363178, + 0.006379573605954647, + 0.008543448522686958, + -0.0287920031696558, + -0.012555857188999653, + -0.00818537175655365, + -0.02121724933385849, + 0.004051907919347286, + 0.008314031176269054, + 0.018088756129145622, + 0.03553271293640137, + 0.0067905583418905735, + -0.003140132175758481, + -0.057567447423934937, + -0.010332655161619186, + 0.06318271905183792, + -0.003696615109220147, + 0.00840845424681902, + 0.01916727051138878, + -0.0163936335593462, + -0.023558514192700386, + -0.034993726760149, + 0.001618542242795229, + -0.057067058980464935, + -0.031286854296922684, + 0.020082876086235046, + 0.037458330392837524, + 0.030802931636571884, + -0.03385234996676445, + -0.03720847889780998, + -0.00579184852540493, + -0.030033309012651443, + 0.010473715141415596, + -0.004317393992096186, + 0.0213870070874691, + 0.01702873595058918, + 0.018028615042567253, + 0.00348240090534091, + -0.0037541608326137066, + 0.01624651439487934, + -0.03032367117702961, + 0.0007437827298417687, + 0.0006491385865956545, + 0.005956200882792473, + 0.00865867454558611, + -0.021258026361465454, + -0.0070960065349936485, + 0.011957677081227303, + -0.016298536211252213, + 0.008514929562807083, + 0.0023186039179563522, + -0.02938898466527462, + -0.024804135784506798, + -0.010780442506074905, + -0.00665642274543643, + 0.05007678642868996, + 0.006068341434001923, + -0.01043156161904335, + 0.042279090732336044, + -0.00041147173033095896, + 0.029946181923151016, + -0.011594077572226524, + -0.028150571510195732, + -0.03771317005157471, + 0.03391464054584503, + 0.006704780273139477, + -0.0008863252587616444, + -0.010801407508552074, + 0.01290382631123066, + 0.0048012263141572475, + 0.019123181700706482, + -0.010329821147024632, + 0.0003811984497588128, + -0.032516684383153915, + 0.02487035281956196, + 0.016688808798789978, + 0.014971322380006313, + 0.028716005384922028, + -0.020757442340254784, + -0.04120093584060669, + 0.0004330268711782992, + 0.028502846136689186, + -0.014562264084815979, + -0.03479398041963577, + 0.04292706027626991, + 0.0014638928696513176, + 0.038927480578422546, + 0.02108013816177845, + 0.014212409034371376, + 0.041148293763399124, + 0.00021885239402763546, + -0.025302665308117867, + 0.011622452177107334, + -0.012458698824048042, + -0.028344767168164253, + 0.003992671612650156, + 0.0050855521112680435, + -0.029758041724562645, + 0.0021962898317724466, + 0.005779812578111887, + -0.02804120071232319, + 0.07183194160461426, + -0.008404646068811417, + 0.0037010367959737778, + -0.006347185000777245, + -0.008413375355303288, + -0.011778062209486961, + 0.02059667557477951, + -0.03138197213411331, + -0.016190005466341972, + -0.020114831626415253, + -0.021713102236390114, + -0.0330817811191082, + 0.009896292351186275, + -0.01770779676735401, + 0.004321535583585501, + -0.008795942179858685, + -0.052061062306165695, + -0.031242715194821358, + -0.026838261634111404, + -0.028532635420560837, + -0.012699116952717304, + -0.03220829367637634, + 0.025972990319132805, + -0.030484085902571678, + 0.049427516758441925, + -0.0017353043658658862, + -0.027877062559127808, + -0.003760670078918338, + 0.01935907080769539, + -0.033290743827819824, + 0.0006133613060228527, + -0.020243151113390923, + 0.04469480738043785, + -0.019717024639248848, + -0.06132536754012108, + -0.003182285465300083, + -0.009087306447327137, + -0.03369243070483208, + -0.01827675849199295, + -0.02109399437904358, + -0.013599151745438576, + 0.03388619050383568, + -0.012017968110740185, + -0.062287818640470505, + 0.0036708444822579622, + 0.031764306128025055, + 0.014895318076014519, + 0.009885996580123901, + -0.05471392720937729, + -0.0033837787341326475, + 0.01346228364855051, + 0.015297306701540947, + 0.0020544352009892464, + -0.005412154830992222, + -0.01265763957053423, + 0.021832389757037163, + -0.03422674909234047, + 0.0028148023411631584, + -0.01154583040624857, + -0.011064182966947556, + -0.010815647430717945, + 0.04849931225180626, + -0.030403584241867065, + -0.013056307099759579, + 0.011582770384848118, + 0.008348386734724045, + -0.0008749407134018838 + ] + }, + { + "name": "f4f908da19e2753f3ed679d7b37650ca.png", + "file-path": "E:\\Pratham\\2025\\Harsh Sir\\Scratch Vision\\images\\Backdrops\\jungle.sb3\\f4f908da19e2753f3ed679d7b37650ca.png", + "embeddings": [ + 0.02785743772983551, + -0.003082147566601634, + 0.004259657114744186, + 0.06672155857086182, + 0.0008454584749415517, + 0.045064523816108704, + -0.08329840004444122, + 0.0003194392193108797, + -0.03850414603948593, + -0.0005235394346527755, + -0.007092341780662537, + -0.016899796202778816, + 0.029316820204257965, + 0.0013630043249577284, + -0.007938908413052559, + 0.0037370671052485704, + -0.0018304025288671255, + -0.011997192166745663, + -0.009840860962867737, + -0.038836970925331116, + 0.03383300080895424, + -0.10282295942306519, + 0.02126876637339592, + 0.10833464562892914, + 0.008113360963761806, + 0.004115271847695112, + -0.019669294357299805, + 0.012263648211956024, + -0.025867953896522522, + -0.01063007302582264, + -0.0048840888775885105, + 0.005555399693548679, + 0.05450979992747307, + 0.006302541587501764, + -0.0018591394182294607, + 0.0173359252512455, + 0.05355506390333176, + 0.0020133641082793474, + -0.011623566038906574, + -0.021273314952850342, + -0.0109615009278059, + -0.005289788357913494, + 0.008601946756243706, + -0.0021925377659499645, + -0.07870914041996002, + -0.0008992416551336646, + -0.021597392857074738, + 0.019055387005209923, + -0.014376472681760788, + 0.02997949905693531, + 0.04146803170442581, + -0.019193239510059357, + 0.01653508096933365, + 0.0168339554220438, + 0.019014529883861542, + -0.014919949695467949, + -0.0017203977331519127, + -0.03912297263741493, + -0.036796849220991135, + -0.004873292986303568, + 0.031438469886779785, + -0.0178778525441885, + -0.006258578505367041, + 0.03040100447833538, + 0.045237235724925995, + 0.0018367678858339787, + -0.006716902833431959, + 0.0239158496260643, + 0.01394014060497284, + 0.020906265825033188, + 0.029153071343898773, + 0.01633232831954956, + -0.020228121429681778, + -0.026946237310767174, + -0.01922641322016716, + -0.0056543610990047455, + 0.0013835756108164787, + 0.009934588335454464, + -0.031941983848810196, + -0.030599942430853844, + -0.016837596893310547, + -0.018107157200574875, + 0.05108984559774399, + -0.014409494586288929, + -0.010494085028767586, + 0.0301342885941267, + 0.03798842802643776, + 0.04697268083691597, + 0.013218349777162075, + -0.000528890173882246, + -0.027251362800598145, + 0.005529372021555901, + 0.007366233039647341, + 0.004047098103910685, + -0.01674199476838112, + 0.013836391270160675, + 0.014662356115877628, + -0.012013382278382778, + -0.040434375405311584, + 0.01941063068807125, + 0.0032002099324017763, + -0.012838291935622692, + -0.039059508591890335, + 0.03873595595359802, + -0.020679421722888947, + 0.0008541463757865131, + 0.022224746644496918, + 0.006540194153785706, + 0.02892138808965683, + 6.517142173834145e-05, + -0.021945636719465256, + 0.041849225759506226, + -0.01866181194782257, + -0.013101205229759216, + 0.01887018419802189, + 0.0024949447251856327, + 0.021771058440208435, + 0.0157603956758976, + 0.013579478487372398, + 0.03452673181891441, + 0.023000670596957207, + -0.002484881319105625, + 0.006290624383836985, + -0.0286459568887949, + 0.030817098915576935, + 0.03390848636627197, + 0.012509954161942005, + 0.039128124713897705, + 0.021843163296580315, + -0.00520618399605155, + -0.04921310022473335, + 0.026823723688721657, + 0.003955667372792959, + 0.012470445595681667, + -0.01158536970615387, + 0.009164207614958286, + 0.003780141705647111, + 0.0193482656031847, + -0.03612188622355461, + 0.019238367676734924, + -0.008284582756459713, + -0.0361860953271389, + -0.008930778130888939, + -0.006759535986930132, + 0.004184829071164131, + -0.02367248758673668, + 0.008615972474217415, + -0.028271570801734924, + 0.03856256604194641, + -0.0075016082264482975, + -0.0014720800099894404, + -0.05154439061880112, + -0.007976345717906952, + 0.010199121199548244, + -0.042003802955150604, + -0.01302146352827549, + -0.020195214077830315, + -0.0209072045981884, + 0.032456450164318085, + 0.03257811442017555, + 0.008761150762438774, + -0.0001847405801527202, + -0.039405785501003265, + -0.031037742272019386, + 0.021739330142736435, + -0.0117917126044631, + -0.016453249379992485, + -0.04924481734633446, + -0.024606773629784584, + 0.005204283632338047, + -0.01845313049852848, + 0.004172536078840494, + -0.02627224661409855, + -0.012578165158629417, + -0.015255972743034363, + 0.017009027302265167, + -0.005958744324743748, + -0.00862914975732565, + -0.04331278055906296, + 0.004582424182444811, + 0.008264109492301941, + 0.00566435419023037, + 0.03624744340777397, + -0.02484685182571411, + -0.03831660747528076, + 0.03878128156065941, + 0.011430523358285427, + 0.027821017429232597, + -0.007091274950653315, + 0.007459131069481373, + 0.025552071630954742, + 0.012421755120158195, + -0.0030917020048946142, + 0.03394699841737747, + 0.018370583653450012, + 0.02230643667280674, + 0.010525158606469631, + 0.03370180353522301, + -0.021694641560316086, + -0.00734934164211154, + -0.010556478053331375, + 0.03723077476024628, + 0.03261573240160942, + 0.025041993707418442, + -0.02333950623869896, + 0.004008631221950054, + -0.02447609230875969, + 0.0053107780404388905, + 0.017997097223997116, + -0.005326974205672741, + -0.008987207897007465, + 0.01818724162876606, + 0.03767450898885727, + 0.043925754725933075, + -0.002040875144302845, + 0.024924304336309433, + 0.039995793253183365, + 0.03354564309120178, + -0.004350732080638409, + 0.0100021380931139, + -0.026070740073919296, + 0.00942667480558157, + -0.033870961517095566, + -0.015500904060900211, + 0.03539677709341049, + -0.0038769340608268976, + -0.023106053471565247, + -0.024253638461232185, + -0.030953142791986465, + 0.008666043169796467, + 0.023927515372633934, + 0.05577819421887398, + -0.045464105904102325, + 0.027029113844037056, + -0.0036664390936493874, + -0.007330826949328184, + -0.01738986372947693, + -0.005170730408281088, + -0.04199839010834694, + -0.03249632939696312, + -0.010797211900353432, + 0.009548365138471127, + -0.02726576291024685, + -0.02685805782675743, + -0.007823570631444454, + 0.012220442295074463, + 0.017741471529006958, + -0.020204832777380943, + -0.02660374715924263, + -0.006183607969433069, + 0.08942877501249313, + -0.021764226257801056, + -0.005157824140042067, + -0.02161947451531887, + 0.014356064610183239, + -0.03319175913929939, + -0.02870536781847477, + -0.03777725249528885, + 0.01739981211721897, + 0.020857280120253563, + 0.01630885899066925, + 0.04786501079797745, + -0.018792547285556793, + 0.016298800706863403, + -0.005511672701686621, + 0.07325751334428787, + 0.026626545935869217, + -0.02616303414106369, + -0.018968665972352028, + -0.015760526061058044, + -0.023086994886398315, + 0.0689873918890953, + -0.026415064930915833, + -0.01700119860470295, + -0.02779921144247055, + -0.013040624558925629, + 0.011175848543643951, + -0.015798671171069145, + 0.010517905466258526, + -0.013907616958022118, + -0.003136373357847333, + -0.0528627373278141, + 0.0076729245483875275, + 0.01457982324063778, + 0.03368428722023964, + 0.054658252745866776, + 0.003638354828581214, + -0.004521485883742571, + 0.0005032161716371775, + 0.048516709357500076, + 0.03385596349835396, + 0.03368664160370827, + 0.013754041865468025, + 0.008167899213731289, + 0.04222714528441429, + -0.015945475548505783, + 0.02108687534928322, + 0.0186360664665699, + -0.02750621736049652, + -0.0017372408183291554, + 0.016486532986164093, + -0.011429770849645138, + -0.00837021041661501, + 0.011725688353180885, + -0.012058809399604797, + -0.01662135310471058, + -0.0660548061132431, + 0.02468080259859562, + 0.03480073809623718, + 0.020056050270795822, + -0.0411725677549839, + 0.031160321086645126, + 0.0037963350769132376, + -0.015008104965090752, + 0.027302609756588936, + 0.023141538724303246, + 0.0008490292821079493, + 0.006727452855557203, + 0.0406743623316288, + 0.01760091632604599, + -0.0038048976566642523, + 0.004120380152016878, + -0.02809015102684498, + -0.002028698567301035, + 0.010684443637728691, + -0.010468664579093456, + -0.0673636719584465, + 0.015358731150627136, + 0.013379237614572048, + -0.0129470843821764, + -0.0010485831880941987, + -0.033923499286174774, + 0.03615111857652664, + 0.011217014864087105, + 0.004281980451196432, + 0.028302837163209915, + 0.015026402659714222, + -0.025941375643014908, + -0.006508850026875734, + -0.025572562590241432, + 0.017082493752241135, + -0.0022956589236855507, + 0.050636354833841324, + -0.02303795889019966, + -0.010225227102637291, + -0.00943799689412117, + -0.022366121411323547, + -0.0029303960036486387, + -0.020019691437482834, + 0.005194972734898329, + 0.01127124298363924, + -0.02602161094546318, + -0.0173727385699749, + -0.006719296332448721, + 0.026414187625050545, + -0.03294059634208679, + -0.008798529393970966, + 0.009944399818778038, + 0.02133345790207386, + 0.030953645706176758, + 0.024112608283758163, + 0.06124449893832207, + 0.024186771363019943, + 0.018697964027523994, + 0.031100913882255554, + 0.04064247012138367, + -0.0013211251934990287, + -0.011933431960642338, + 0.025501301512122154, + 0.012669063173234463, + 0.0027305653784424067, + 0.005168197210878134, + 0.006687231361865997, + -0.04734718054533005, + -0.0019062061328440905, + 0.03620888292789459, + -0.029951773583889008, + -0.010570835322141647, + 0.024706728756427765, + -0.012206661514937878, + 0.07155835628509521, + 0.07104545831680298, + -0.02288585528731346, + 0.04244910553097725, + 0.008064046502113342, + 0.008601479232311249, + 0.008671518415212631, + -0.02872302383184433, + -0.01896357163786888, + 0.012604263611137867, + 0.02707000821828842, + 0.009858634322881699, + -0.01296924613416195, + -0.01656167395412922, + 0.0027110453229397535, + 0.024125726893544197, + -0.008051376789808273, + 0.002603409579023719, + 0.03184816241264343, + -0.019391939043998718, + 0.009729325771331787, + -6.288733857218176e-05, + 0.006259221583604813, + 0.048805803060531616, + -0.008072183467447758, + -0.04633437842130661, + 0.022717904299497604, + 0.06332843005657196, + -0.0028997475747019053, + -0.007013394497334957, + -0.03979509696364403, + 0.016498908400535583, + 0.014375605620443821, + 0.00036048845504410565, + -0.007832645438611507, + -0.02801797166466713, + -0.07316353172063828, + -0.013415735214948654, + 0.016019130125641823, + 0.05316008999943733, + 0.0031133689917623997, + -0.05395017936825752, + 0.008557656779885292, + -0.02100665122270584, + -0.07834036648273468, + 0.0029629836790263653, + 0.016104398295283318, + 0.02068261429667473, + -0.024398736655712128, + 0.01576143316924572, + 0.0006257133209146559, + 0.015154018998146057, + -0.001742047374136746, + -0.01975836604833603, + 0.055911339819431305, + 0.03198152035474777, + 0.02307780273258686, + -0.016797708347439766, + 0.009010317735373974, + -0.004568920470774174, + -0.020916879177093506, + 0.01609533280134201, + -0.026364073157310486, + -0.0013010910479351878, + 0.016497470438480377, + -0.01804395392537117, + -0.029397932812571526, + -0.05693500116467476, + -0.01095955166965723, + -0.002310772193595767, + -0.005392378196120262, + -0.008061009459197521, + 0.013959856703877449, + -0.028969410806894302, + -0.022654177621006966, + -0.02067892625927925, + 0.019800333306193352, + 0.3166353106498718, + -0.023878194391727448, + 0.025204870849847794, + 0.021931815892457962, + -0.027265368029475212, + -0.002880893647670746, + -0.0031982786022126675, + 0.050634488463401794, + 0.0018904460594058037, + -0.00659086974337697, + 0.003233797149732709, + 0.06684405356645584, + 0.01823665387928486, + -0.005598897580057383, + -0.03883950784802437, + 0.03891173377633095, + -0.007143223658204079, + 0.0004925118410028517, + -0.07374531030654907, + -0.004500295501202345, + 0.028401710093021393, + 0.03696829080581665, + 0.01381231565028429, + 0.041821300983428955, + -0.005487148184329271, + 0.01601051166653633, + 0.006881040520966053, + -0.0016769161447882652, + -0.008312550373375416, + -0.005684748757630587, + 0.008753851987421513, + 0.11552444845438004, + -0.05644351989030838, + -0.03831562399864197, + 0.01288728229701519, + -0.024614062160253525, + -0.00858817994594574, + -0.02795914001762867, + -0.010418511927127838, + -0.008655985817313194, + 0.004266759846359491, + 0.005211644805967808, + -0.02298547513782978, + -0.015345253050327301, + -0.03507385402917862, + 0.026259563863277435, + -0.0027734022587537766, + 0.014224895276129246, + 0.051341794431209564, + -0.055096257477998734, + -0.001314493827521801, + 0.006016890984028578, + -0.010032241232693195, + -0.019348222762346268, + 0.01355439331382513, + -0.12897904217243195, + -0.011918074451386929, + -0.07418467849493027, + 0.029957015067338943, + -0.00708962045609951, + 0.004047288093715906, + -0.03931768238544464, + 0.03934658691287041, + -0.011910161934792995, + -0.033640291541814804, + -0.029493805021047592, + -0.007855616509914398, + -0.007925111800432205, + 0.0011050739558413625, + -0.0027666939422488213, + 0.03316386416554451, + 0.0581439808011055, + 0.09630835056304932, + -0.0021935966797173023, + -0.01362634263932705, + -0.049351342022418976, + 0.006080607417970896, + 0.003128943033516407, + -0.027441618964076042, + 0.02361210063099861, + -0.036876969039440155, + -0.021311109885573387, + -0.02235540561378002, + 0.026483407244086266, + 0.0011120627168565989, + -0.04212428629398346, + 0.0014206848572939634, + -0.0037097486201673746, + 0.009767151437699795, + -0.01686243899166584, + -0.007412613369524479, + 0.03158879280090332, + 0.005528249777853489, + -0.007655099034309387, + 0.02815900184214115, + 0.04140302538871765, + -0.024994689971208572, + 0.004932221490889788, + 0.00889406818896532, + 0.02780739963054657, + -0.08206107467412949, + -0.006658358033746481, + 0.02022366411983967, + 0.018233656883239746, + -0.031149378046393394, + 0.019099626690149307, + 0.0023240773007273674, + 0.03782489150762558, + 0.03826904296875, + 0.01373060792684555, + 0.01759495586156845, + 0.04501906409859657, + -0.029752178117632866, + -0.020308230072259903, + -0.012959630228579044, + 0.053426969796419144, + -0.020002514123916626, + 0.011153998784720898, + 0.002264349954202771, + -0.029001588001847267, + 0.03429656848311424, + 0.020931439474225044, + -0.013001974672079086, + -0.0018132502445951104, + -0.01376387570053339, + -0.015238904394209385, + 0.04299721121788025, + 0.01724417507648468, + -0.03235240280628204, + -0.008428948000073433, + 0.0017805828247219324, + -0.08103778958320618, + 0.022386202588677406, + -0.01540122739970684, + 0.041026629507541656, + 0.0022371718659996986, + 0.021193059161305428, + -0.020023223012685776, + 0.011801669374108315, + 0.027707792818546295, + 0.0306467916816473, + 0.02988760732114315, + 0.011129237711429596, + -0.007281851954758167, + 0.013190872967243195, + -0.009725183248519897, + -0.029494883492588997, + -0.026340248063206673, + -0.002421681070700288, + 0.019434113055467606, + -0.012986578978598118, + -0.006140802521258593, + -0.005266430322080851, + 0.024421581998467445, + -0.06171727925539017, + 0.029087483882904053, + 0.022729741409420967, + -0.029166419059038162, + 0.00549559760838747, + -0.011598082259297371, + 0.016818365082144737, + 0.014883562922477722, + -0.05740746855735779, + -0.01674073562026024, + -0.036723487079143524, + -0.020387381315231323, + -0.008452954702079296, + -0.003364522010087967, + -0.013159855268895626, + -0.14531902968883514, + -0.015836616978049278, + 0.00928250141441822, + -0.013431215658783913, + 0.02737325057387352, + -0.04727497324347496, + 0.008926527574658394, + -0.02089073322713375, + -0.01952197216451168, + -0.0002290615811944008, + -0.040323954075574875, + -0.028988761827349663, + 0.004834904335439205, + -0.052081186324357986, + -0.01087478268891573, + 0.00948737096041441, + 0.0022670328617095947, + 0.00905112735927105, + 0.007306876592338085, + 0.038356564939022064, + 0.07307118922472, + -0.061368316411972046, + -0.07871778309345245, + 0.014007320627570152, + 0.013981961645185947, + -0.028153056278824806, + 0.011896799318492413, + 0.02833176590502262, + -0.012678097933530807, + -0.007860585115849972, + -0.0010821991600096226, + -0.025587648153305054, + 0.024309245869517326, + -0.013250453397631645, + 0.03174120560288429, + 0.02600991539657116, + -0.02445596642792225, + -0.024980923160910606, + -0.02822277508676052, + 0.020158730447292328, + 0.03821127489209175, + 0.043510425835847855, + -0.01972736045718193, + 0.015429076738655567, + 0.04064030572772026, + 0.008117932826280594, + -0.0012373640201985836, + -0.007005220279097557, + 0.003350285580381751, + 0.03408695384860039, + -0.018185380846261978, + 0.02806675061583519, + 0.006448033265769482, + -0.01689273864030838, + -0.015338270924985409, + -0.012954676523804665, + -0.019372105598449707, + 0.0030225031077861786, + 0.01447817962616682, + -0.0147878872230649, + 0.0182047039270401, + -0.0004019320767838508, + -0.00859267171472311, + -0.0755525678396225, + 0.008109364658594131, + 0.0008534970111213624, + 0.00598682276904583, + 0.016177810728549957, + -0.019228419288992882, + -0.03169068321585655, + 0.0029503314290195704, + 0.034061431884765625, + 0.02646735869348049, + 0.024763258174061775, + 0.029932156205177307, + -0.08835791051387787, + -0.06625842303037643, + -0.01958281733095646, + 0.003670203499495983, + 0.0028320178389549255, + -0.01258014515042305, + -0.06602319329977036, + 0.016353102400898933, + 0.019703928381204605, + 0.01870870776474476, + -0.018739774823188782, + -0.018373703584074974, + 0.00018548070511315018, + -0.0026549082249403, + 0.009695393033325672, + -0.004177299328148365, + -0.012652953155338764, + -0.0034969905391335487, + -0.013078078627586365, + 0.026151146739721298, + 0.023402221500873566, + -0.007289097644388676, + 0.03486742451786995, + 0.02452530339360237, + 0.018118100240826607, + -0.0007206825539469719, + -0.015106791630387306, + -0.009446194395422935, + 0.00663653714582324, + 0.10224856436252594, + 0.02558838203549385, + -0.10280017554759979, + 0.0048937080428004265, + 0.008962014690041542, + 0.031570304185152054, + 0.02291816473007202, + -0.01306076254695654, + -0.019891615957021713, + 0.007857297547161579, + 0.05071820318698883, + -0.004981411620974541, + 0.00883109588176012, + 0.01401008665561676, + 0.003542491002008319, + 0.017065878957509995, + 0.023490767925977707, + 0.005597573239356279, + -0.012102553620934486, + -0.01983855850994587, + 0.019569609314203262, + -0.013353286311030388, + -0.004281389992684126, + 0.001034081564284861, + -0.012231241911649704, + -0.28484445810317993, + 0.03123996965587139, + -0.01240184810012579, + -0.007097114343196154, + -0.037644483149051666, + 0.0002540566783864051, + 0.05323160067200661, + -0.01698656566441059, + -0.0036734757013618946, + -0.017029691487550735, + -0.0017282003536820412, + -0.024168627336621284, + 0.04837992414832115, + -0.013086586259305477, + 0.023176198825240135, + -0.019666217267513275, + -0.023659978061914444, + -0.004240327049046755, + 0.013295688666403294, + 0.012481767684221268, + 0.026898914948105812, + 0.0019563031382858753, + -0.02148273214697838, + 0.012063786387443542, + 0.034277621656656265, + 0.013814866542816162, + -0.02852839045226574, + -0.022243402898311615, + -0.0038401430938392878, + 0.017720751464366913, + 0.0060753244906663895, + 0.030216461047530174, + 0.03047291934490204, + -0.020699355751276016, + 0.006191985681653023, + -0.042394861578941345, + -0.008224120363593102, + 0.012311244383454323, + 0.001288004918023944, + -0.006383628584444523, + 0.04346397519111633, + 0.01057644747197628, + -0.03873481601476669, + -0.02027408592402935, + -0.01153139490634203, + -0.030051041394472122, + 0.0038429656997323036, + -0.03249216824769974, + -0.008241968229413033, + -0.0021300537046045065, + 0.010604300536215305, + -0.0068333763629198074, + -0.0565863661468029, + -0.051566291600465775, + 0.014299721457064152, + 0.006120108999311924, + -0.05057583749294281, + 0.03868425637483597, + 0.034275028854608536, + -0.004611834418028593, + 0.0046366238966584206, + 0.004629781469702721, + -0.009738245978951454, + -0.018909940496087074, + -0.02019626647233963, + 0.012925037182867527, + 0.0013500795466825366, + -0.03597036004066467, + -0.02381938137114048, + -0.008119652979075909, + 0.009273294359445572, + 0.005741660948842764, + -0.0014980551786720753, + 0.029576819390058517, + -0.04029083997011185, + 0.0013423284981399775, + 0.012318792752921581, + 0.03796025738120079, + 0.02542995847761631, + -0.009694250300526619, + 0.013260510750114918, + 0.021910734474658966, + 0.011345933191478252, + -0.0009058844880200922, + 0.00039978304994292557, + -0.013588503003120422, + -0.02108100801706314, + -0.01258576288819313, + 0.03944999724626541, + -0.035269368439912796, + -0.005040574818849564, + -0.009869148954749107, + 0.01079611387103796, + 0.014595037326216698, + 0.013992510735988617, + -0.0354275144636631, + -0.032733283936977386, + -0.019695213064551353, + -0.013625387102365494, + 0.018812550231814384, + -0.06136709451675415, + -0.00600793631747365, + -0.0002453155175317079, + -0.013989515602588654, + 0.002308467635884881, + 0.02452877350151539, + 0.03965020552277565, + 0.030213307589292526, + 0.0016219096723943949, + -0.0014040378155186772, + 0.03251013159751892, + 0.00388539326377213, + -0.023951198905706406, + 0.033324234187603, + -0.060591116547584534, + -0.024132851511240005, + 0.013918176293373108, + 0.02649724669754505, + -0.03420858830213547, + -0.019557323306798935, + 0.03840712457895279, + 0.012455134652554989, + 0.033343568444252014, + -0.08847340941429138, + 0.02023540437221527, + 0.010485327802598476, + 0.0007953542517498136, + 0.005956352688372135, + -0.010211844928562641, + 0.01794237643480301, + 0.0006047532078810036, + -0.014199384488165379, + 0.10999313741922379, + 0.021997695788741112, + -0.014147038571536541, + -0.03827837109565735, + -0.007076679263263941, + -0.012244448065757751, + 0.02124076709151268, + -0.02691873349249363, + 0.02967597357928753, + 0.002470080740749836, + 0.021859606727957726, + 0.011855914257466793, + 0.00921156071126461, + -0.003165096277371049, + -0.01410635095089674, + 0.02014339528977871, + -0.04836241900920868, + 0.0008720073965378106, + 0.018452603369951248, + 0.02085639350116253, + -0.025046318769454956, + 0.00036713763256557286, + -0.01994282566010952, + -0.01328476145863533, + -0.01209900714457035, + 0.04723232984542847, + 0.007008129730820656, + 0.037194933742284775, + -0.04286123067140579, + -0.00010389048111392185, + 0.04833460971713066, + 0.020793825387954712, + -0.022460605949163437, + -0.02196555770933628, + 0.00927022472023964, + 0.010435561649501324, + 0.04117720574140549, + -0.010420658625662327, + -0.026764526963233948, + 0.037086378782987595, + -0.0140154417604208, + -0.03515320643782616, + -0.015232115052640438, + 0.000891643634531647, + 0.04128982499241829, + 0.004778093192726374, + 0.018796406686306, + -0.019675005227327347, + 0.0022547240369021893, + -0.019103016704320908, + 0.004610704258084297, + -0.002026313217356801, + -0.017936352640390396, + 0.010839270427823067, + 0.004392916336655617, + 0.023464176803827286, + 0.022633805871009827, + 0.0407208651304245, + -0.02826087363064289, + 0.01047145389020443, + -0.017601585015654564, + -0.02890811674296856, + 0.008270167745649815, + 0.05583438277244568, + 0.017899926751852036, + 0.008188487030565739, + 0.029962893575429916, + 0.006307520903646946, + 0.06054775416851044, + -0.010898223146796227, + 0.07984879612922668, + -0.007499013561755419, + -0.00908354390412569, + -0.05324167758226395, + 0.0015424592420458794, + 0.02248026244342327, + 0.009074048139154911, + 0.02041560225188732, + -0.00901571661233902, + 0.04750220105051994, + 0.018691567704081535, + -0.025651484727859497, + -0.0018045147880911827, + 0.011286861263215542, + -0.015002327971160412, + 0.025330903008580208, + 0.0007813627598807216, + -0.013659701682627201, + 0.024184515699744225, + 0.08112286776304245, + -0.014728519134223461, + 0.02723785676062107, + -0.02793898805975914, + 0.00948050431907177, + 0.02341216616332531, + -0.049130842089653015, + -0.011201834306120872, + 0.010529138147830963, + 0.007039613090455532, + -0.017387187108397484, + 0.06608215719461441, + -0.00429677264764905, + 0.0033407388255000114, + -0.023115159943699837, + -0.04843423143029213, + 0.011628726497292519, + -0.03639713302254677, + 0.002439794596284628, + -0.006610170006752014, + -0.026118768379092216, + 0.012200801633298397, + 0.021491140127182007, + -0.009170490317046642, + -0.0643317848443985, + -0.000983556848950684, + -0.007370796520262957, + -0.030349845066666603, + 0.02712077647447586, + 0.0052390554919838905, + -0.01777590624988079, + 0.011317381635308266, + -0.03680223599076271, + 0.02736140415072441, + 0.019777553156018257, + -0.010086078196763992, + 0.0030806283466517925, + -0.014147169888019562, + 0.009685670025646687, + -0.03715699911117554, + -0.015177104622125626, + -0.00587204797193408, + 0.0478973388671875, + 0.01995678059756756, + -0.020769519731402397, + -0.04886310175061226, + -0.07183053344488144, + 0.027818478643894196 + ] + }, + { + "name": "baseball_sprite_motion_1.png", + "file-path": "E:\\Pratham\\2025\\Harsh Sir\\Scratch Vision\\images\\sprites\\Batter.sprite3\\baseball_sprite_motion_1.png", + "embeddings": [ + 0.022147687152028084, + 0.007597184274345636, + -0.0032859118655323982, + 0.04339378699660301, + -0.021053949370980263, + 0.033851128071546555, + -0.023394983261823654, + 0.011051983572542667, + 0.01693667285144329, + -0.007991934195160866, + 0.03995959460735321, + 0.014700828120112419, + -0.009248287416994572, + 0.0198433268815279, + 0.03824836015701294, + 0.012932203710079193, + -0.005646036937832832, + 0.013004099950194359, + 0.002463801298290491, + 0.04766537621617317, + -0.002504152012988925, + -0.08765456825494766, + -0.01566997729241848, + 0.03527290001511574, + -0.018665557727217674, + 0.04839005693793297, + 0.026514271274209023, + -0.012884381227195263, + -0.03070109151303768, + 0.006981204263865948, + 0.002890384988859296, + -0.01212216168642044, + 0.005298549775034189, + -0.008475619368255138, + -0.04151637479662895, + 0.016728948801755905, + 0.015876708552241325, + 0.029103366658091545, + -0.038934871554374695, + -0.0044417851604521275, + 0.009866137057542801, + -0.03759973123669624, + 0.023010198026895523, + 0.04211233928799629, + -0.07135797291994095, + -0.03770175948739052, + -0.0010564614785835147, + -0.13581320643424988, + -0.041182443499565125, + 0.03471996635198593, + 0.03418557718396187, + 0.013047107495367527, + 0.005425185430794954, + -0.02524116262793541, + -0.010608342476189137, + 0.011867575347423553, + -0.016229938715696335, + 0.002621706109493971, + -0.0018417792161926627, + 0.007430412340909243, + 0.010022273287177086, + -0.013016323558986187, + -0.021407531574368477, + -0.014684592373669147, + -0.008500725030899048, + -0.02443692274391651, + 0.009537577629089355, + -0.05189519748091698, + 0.03282550349831581, + 0.03509483113884926, + 0.026203518733382225, + -0.0015635500894859433, + -0.031609248369932175, + 0.016349900513887405, + -0.010515461675822735, + -0.010241040959954262, + 0.0037981814239174128, + -0.0031802598387002945, + -0.01430422905832529, + 0.008955767378211021, + -0.021118134260177612, + -0.005325538571923971, + 0.02516097016632557, + 0.009449878707528114, + 0.04143012315034866, + 0.02644285187125206, + -0.010414010845124722, + 0.02146638184785843, + -0.026903169229626656, + 0.009534013457596302, + -0.005090886726975441, + -0.017660178244113922, + 0.028031125664711, + 0.02987797185778618, + 0.007364960387349129, + 0.036547839641571045, + 0.013259693048894405, + -0.015496091917157173, + -0.030259324237704277, + 0.00846378318965435, + 0.04096582531929016, + -0.04575437307357788, + 0.00730034289881587, + 0.021138589829206467, + 0.0059892525896430016, + -0.04012717306613922, + 0.0321812741458416, + -0.0019483969081193209, + 0.07122615724802017, + 0.018112193793058395, + 0.023600270971655846, + -0.003395108273252845, + 0.023301390931010246, + 0.05607791244983673, + 0.00015652042930014431, + 0.04876748472452164, + 0.02029331587255001, + -0.017242832109332085, + -0.03278721868991852, + 0.0007980617228895426, + 0.005562886130064726, + 0.03592754900455475, + -0.018839092925190926, + 0.07162000238895416, + -0.027651727199554443, + 0.0031251683831214905, + 0.0427219420671463, + 0.05898067355155945, + -0.029186805710196495, + 0.02310127764940262, + -0.038239963352680206, + 0.045942239463329315, + 0.03120519407093525, + 0.0002669372479431331, + -0.028540432453155518, + 0.030893374234437943, + 0.04340214282274246, + 0.007507950533181429, + -0.00358012318611145, + -0.007036789786070585, + -0.0161654781550169, + 0.017334604635834694, + 0.004850016441196203, + 0.022763090208172798, + 0.036164090037345886, + -0.003690257202833891, + 0.0027975640259683132, + -0.008843233808875084, + -0.05107931047677994, + -0.03457500785589218, + 0.000875158526469022, + -0.03377746790647507, + -0.02916143648326397, + 0.011994264088571072, + 0.0008331474382430315, + 0.0379365049302578, + -0.023921029642224312, + -0.02173316478729248, + -0.0011920260731130838, + 0.004743731580674648, + -0.018200598657131195, + 0.037135761231184006, + 0.02748831920325756, + 0.014237752184271812, + 5.169375072000548e-05, + 0.027358535677194595, + -0.022525327280163765, + -0.02471347153186798, + 0.010839981958270073, + -0.018539203330874443, + -0.0030060678254812956, + 0.030680347234010696, + 0.04588695243000984, + -0.00857694260776043, + -0.011995121836662292, + 0.010638405568897724, + 0.005152322351932526, + -0.005783697124570608, + 0.01752607524394989, + 0.039343152195215225, + -0.03496309742331505, + 0.03444138914346695, + -0.0035632094368338585, + -0.01689818874001503, + -0.03582601249217987, + -0.0004348166403360665, + 0.0348505973815918, + -0.025692153722047806, + 0.0033176362048834562, + 0.037672292441129684, + -0.00046009139623492956, + 0.018450438976287842, + -0.012510600499808788, + 0.00956093892455101, + -0.026821335777640343, + 0.0099641727283597, + 0.009295918978750706, + -0.012954690493643284, + 0.003013639012351632, + 0.004195708781480789, + 0.004412845242768526, + 0.005716077983379364, + -0.023338107392191887, + 0.025941047817468643, + -0.019874418154358864, + 0.03931569680571556, + -0.007919702678918839, + -0.06397755444049835, + 0.007822125218808651, + -0.009975286200642586, + 0.026235949248075485, + -0.004568996839225292, + -0.023265277966856956, + 0.02163884788751602, + 0.024781441316008568, + 0.018631931394338608, + 0.021228184923529625, + 0.0320611409842968, + 0.0011928413296118379, + 0.1126079261302948, + -0.127414271235466, + 0.02758190594613552, + 0.02706916071474552, + -0.007566841319203377, + 0.035729143768548965, + -0.03159010410308838, + -0.007423206698149443, + 0.016949832439422607, + 0.004187113139778376, + -0.0005590440705418587, + 0.044851288199424744, + -0.024573195725679398, + 0.04944740980863571, + 0.0023782714270055294, + -0.00028500441112555563, + 0.021683406084775925, + 0.005908516235649586, + -0.009145015850663185, + -0.004688090644776821, + 0.008107918314635754, + -0.009123602882027626, + 0.00019097770564258099, + 0.035501863807439804, + 0.014524679630994797, + -0.02652733027935028, + -0.03313400596380234, + -0.008740516379475594, + -0.007941968739032745, + -0.0119171729311347, + -0.002315141260623932, + 0.08579149842262268, + 0.014683999121189117, + 0.002465159399434924, + 0.007268233224749565, + -0.031543612480163574, + -0.018182829022407532, + -0.0068212333135306835, + -0.033146172761917114, + 0.04456909000873566, + -0.003302002791315317, + 0.012140374630689621, + 0.0008111995412036777, + -0.05386742949485779, + -0.013985815457999706, + 0.004727221559733152, + 0.04233771190047264, + -0.012763452716171741, + -0.0532226487994194, + -0.03681676462292671, + 0.006024608388543129, + -0.04711803048849106, + 0.030217252671718597, + 0.024489719420671463, + -0.038869839161634445, + -0.010648120194673538, + -0.01816760003566742, + -0.014799325726926327, + -0.016873415559530258, + 0.02832004241645336, + 0.021269572898745537, + 0.008502956479787827, + 9.808960021473467e-05, + -0.03043912723660469, + -0.09003118425607681, + 0.014431335963308811, + 0.03166928142309189, + -0.0008585253963246942, + -0.031847450882196426, + -0.007602074649184942, + 0.017325004562735558, + 0.01866004802286625, + 0.017119476571679115, + 0.016467854380607605, + -0.0664505660533905, + 0.03518885374069214, + -0.02909868396818638, + 0.004733607638627291, + 0.07865727692842484, + -0.013910351321101189, + 0.0013422532938420773, + -0.017809053882956505, + 0.06572036445140839, + -0.0044638244435191154, + -0.07233849912881851, + -0.008663978427648544, + 0.026542462408542633, + -0.014686495997011662, + 0.005142957903444767, + 0.010136151686310768, + 0.0247514508664608, + 0.05875323712825775, + 0.014099782332777977, + 0.005972602404654026, + -0.006726286839693785, + 0.004732238594442606, + 0.04911116138100624, + 0.0235123410820961, + 0.031830113381147385, + 0.031675469130277634, + 0.017776966094970703, + -0.004560929723083973, + 0.002072218805551529, + 0.02867986634373665, + 0.01651359349489212, + -0.004894664511084557, + 0.04501936957240105, + -0.030372751876711845, + -0.0028881607577204704, + 0.004469172563403845, + 0.019608529284596443, + -0.023062750697135925, + -0.009494803845882416, + 0.03234110772609711, + -0.004973724018782377, + 0.02019779197871685, + 0.017480412498116493, + -0.03658083453774452, + -0.040322404354810715, + -0.009384050033986568, + -0.02774229273200035, + 0.046769943088293076, + -0.024280697107315063, + 0.039926569908857346, + -0.04701551795005798, + 0.0010367651702836156, + -0.00568279717117548, + -0.032896094024181366, + -0.022129984572529793, + 0.0024225900415331125, + -0.01421536784619093, + -0.027370983734726906, + -0.02344682440161705, + 0.004060865379869938, + 0.005088900215923786, + 0.05016809701919556, + 0.013034886680543423, + 0.05704076960682869, + 0.040102265775203705, + -0.05304533615708351, + -0.06574707478284836, + -0.04913630336523056, + -0.12626436352729797, + -0.01984451152384281, + -0.018287863582372665, + -0.021968234330415726, + -0.029618296772241592, + 0.0011247455840930343, + -0.03506520390510559, + 0.019779151305556297, + -0.025921965017914772, + 0.02890416607260704, + -0.025053629651665688, + -0.03385211527347565, + -0.007582887075841427, + 0.054911430925130844, + 0.005331899970769882, + -0.01630171574652195, + -0.03208780661225319, + -0.0023146166931837797, + -0.012850292958319187, + 0.010158460587263107, + 0.08162625133991241, + -0.04364040866494179, + 0.002791609615087509, + 0.021543605253100395, + 0.14065386354923248, + 0.026435354724526405, + 0.000478245405247435, + -0.02128019742667675, + 0.0398673377931118, + -0.038631685078144073, + -0.020747512578964233, + -0.0387636199593544, + -0.010691075585782528, + 0.01658683270215988, + 0.025223983451724052, + 0.039163682609796524, + 0.0026141763664782047, + 0.02923242747783661, + -0.02440878190100193, + 0.04398496821522713, + 0.008903514593839645, + 0.0018461106810718775, + 0.00024478317936882377, + 0.01642078347504139, + -0.014241176657378674, + -0.026913490146398544, + -0.006153032183647156, + -0.02679496258497238, + -0.022243432700634003, + 0.029060397297143936, + 0.013883638195693493, + -0.01718011684715748, + 0.026408787816762924, + -0.023445846512913704, + -0.009075667709112167, + 0.0023473191540688276, + -0.02076946757733822, + -0.025076087564229965, + 0.020739831030368805, + -0.009035521186888218, + 0.04068766534328461, + 0.0006089904927648604, + 0.010520580224692822, + -0.023828746750950813, + 0.01054013054817915, + -0.024225054308772087, + -0.016089478507637978, + -0.008407232351601124, + 0.007267372217029333, + -0.01794995367527008, + 0.029670584946870804, + 0.01039021834731102, + -0.00786595605313778, + -0.012577828951179981, + 0.03149617463350296, + -0.003305362770333886, + 0.0508275143802166, + 0.05677830055356026, + 0.010356759652495384, + -0.03463065251708031, + -0.03395100310444832, + -0.02446518838405609, + -0.013290638104081154, + 0.007634274195879698, + -0.009387196972966194, + -0.028643494471907616, + 0.006770946551114321, + -0.018525827676057816, + -0.026801221072673798, + 0.0052848332561552525, + 0.005917168688029051, + -0.020926564931869507, + 0.02183084562420845, + 0.0026137970853596926, + -0.02545969747006893, + 0.017170608043670654, + 0.2671959102153778, + 0.03932362422347069, + 0.028092900291085243, + -0.007327385246753693, + -0.021001754328608513, + -0.03939923271536827, + -0.011996183544397354, + -0.013932309113442898, + -0.00877124909311533, + -0.04137664660811424, + -0.006804272066801786, + 0.006848086137324572, + -0.01023693848401308, + 0.03341259807348251, + 0.013534436002373695, + 0.03760407119989395, + -0.016518255695700645, + -0.015979908406734467, + -0.06132521480321884, + 0.0206417515873909, + -0.031205015257000923, + -0.038643404841423035, + 0.0081148287281394, + -0.004524885211139917, + 0.042152807116508484, + 0.019690152257680893, + 0.0041754404082894325, + 0.01489576231688261, + 0.022840118035674095, + -0.001945628086104989, + -0.007647657301276922, + 0.014363070018589497, + -0.05279252678155899, + -0.009028198197484016, + 0.025745440274477005, + -0.018208207562565804, + -0.015536573715507984, + 0.018661295995116234, + 0.0508166067302227, + 0.026121312752366066, + -0.00739698251709342, + 0.03529197350144386, + 0.003375741420313716, + 0.023838603869080544, + -0.024656672030687332, + -0.014951837249100208, + -0.008464999496936798, + 0.0369715616106987, + 0.05830777809023857, + -0.015595871955156326, + -0.007737933192402124, + -0.015027085319161415, + 0.02006176859140396, + -0.03422331437468529, + -0.03843245655298233, + -0.07010385394096375, + 0.031762298196554184, + -0.014273840934038162, + 0.010932576842606068, + 0.007623566314578056, + 0.013540120795369148, + 0.0006226179539225996, + -0.0021404726430773735, + -0.045354846864938736, + 0.0021301032975316048, + -0.0064691416919231415, + 0.02432309091091156, + -0.008337283506989479, + 0.005337050650268793, + -0.026705604046583176, + -0.00789045263081789, + 0.027427343651652336, + 0.018097499385476112, + -0.029421892017126083, + -0.03390686959028244, + 0.01751854456961155, + 0.0034388795029371977, + -0.014551978558301926, + 0.04437601566314697, + 0.022072279825806618, + -0.027395687997341156, + -0.053462348878383636, + -0.02736654318869114, + 0.0019994101021438837, + -0.0011645631166175008, + -0.09550508856773376, + -0.05687664449214935, + 0.0041463677771389484, + -0.016562078148126602, + 0.0030362701509147882, + -0.04654382914304733, + -0.00608397088944912, + 0.003779762890189886, + -0.022947272285819054, + 0.011881906539201736, + 0.029955048114061356, + 0.05281824618577957, + 0.0638841763138771, + -0.0021961452439427376, + -0.03202616050839424, + -0.05739208683371544, + 0.011238045990467072, + 0.04258810356259346, + -0.02666269615292549, + 0.018057288601994514, + -0.027037477120757103, + -0.010600544512271881, + -0.005970004480332136, + 0.021670591086149216, + 0.0070473141968250275, + 0.029620593413710594, + 0.014210124500095844, + 0.020321594551205635, + 0.019915871322155, + 0.039728593081235886, + -0.014653341844677925, + -0.03275548666715622, + 0.027579788118600845, + -0.025782275944948196, + 0.036687444895505905, + 0.010293206200003624, + 0.000744784832932055, + -0.03474883735179901, + -0.011682186275720596, + -0.023156408220529556, + -0.0013848240487277508, + 0.07253526896238327, + 0.013403029181063175, + -0.04968048259615898, + -0.0070334081538021564, + -0.001714537269435823, + -0.03307633474469185, + -0.014608701691031456, + -0.005381835158914328, + -0.005943221505731344, + 0.021540118381381035, + 0.005270250607281923, + -0.00934557430446148, + 0.0024828575551509857, + -0.004377906210720539, + -0.0285781342536211, + -0.008701914921402931, + 0.019608499482274055, + -0.016460297629237175, + 0.0008504629950039089, + 0.006903097033500671, + 0.014393215999007225, + -0.030053483322262764, + -0.005837258882820606, + 0.04333949461579323, + -0.014137767255306244, + 0.02655976079404354, + -0.010612282902002335, + 0.020927000790834427, + -0.09018605947494507, + -0.015338919125497341, + 0.02965846285223961, + 0.05299700051546097, + -0.011085236445069313, + 0.03827716037631035, + -0.005883281119167805, + 0.01627104915678501, + -0.011240679770708084, + 0.05563010275363922, + -0.00910177081823349, + 0.021256618201732635, + 0.025858459994196892, + 0.044216424226760864, + 0.002807732904329896, + -0.12867140769958496, + -0.017731254920363426, + 0.00952855870127678, + 0.007338598370552063, + 0.004994578659534454, + -0.0008043372654356062, + -0.013866458088159561, + 0.03383421525359154, + -0.0033094503451138735, + 0.006479040253907442, + 0.011751975864171982, + -0.01888379454612732, + 0.006316208280622959, + -0.009462918154895306, + 0.02718701772391796, + -0.04472263157367706, + 0.015073009766638279, + 0.027919085696339607, + 0.007180436514317989, + -0.016055947169661522, + 0.05776520445942879, + 0.0005876567447558045, + -0.029178228229284286, + 0.029278522357344627, + -0.0004922719672322273, + -0.020044932141900063, + 0.03191195800900459, + -0.011960714124143124, + -0.023341599851846695, + 0.014654276892542839, + -0.02095569297671318, + 0.017963068559765816, + 0.03364502266049385, + 0.055677250027656555, + 0.02246144600212574, + -0.031700216233730316, + 0.023514095693826675, + -0.03059381991624832, + -0.02035394497215748, + 0.005769032984972, + 2.984487400681246e-05, + -0.020312735810875893, + -0.0008034924394451082, + -0.006513804197311401, + -0.020334968343377113, + 0.05347847193479538, + -0.006288643926382065, + -0.0023343339562416077, + -0.027050429955124855, + 0.03649245202541351, + 0.0005919751129113138, + 0.016541706398129463, + 0.01591087318956852, + 0.007579256314784288, + 0.0015951452078297734, + -0.030104193836450577, + 0.008311474695801735, + 0.059026315808296204, + 0.04380744695663452, + 0.0005900250398553908, + 0.01065382082015276, + 0.054274361580610275, + 0.020013881847262383, + -0.12229348719120026, + -0.008683640509843826, + -0.048040084540843964, + 0.021569035947322845, + 0.027248390018939972, + -0.03367435187101364, + 0.004226948134601116, + 0.0077888392843306065, + -0.001399819739162922, + 0.02319539152085781, + 0.055139973759651184, + 0.05657796934247017, + -0.06568989157676697, + -0.030968397855758667, + 0.040246639400720596, + 0.014135400764644146, + 0.028008705005049706, + 0.03538541495800018, + 0.009155257605016232, + -0.024981984868645668, + 0.028609830886125565, + 0.02796531841158867, + -0.0025766349863260984, + 0.01548011600971222, + 0.025229305028915405, + -0.012788057327270508, + -0.0223691426217556, + 0.01080207247287035, + 0.017079956829547882, + -0.006792327854782343, + -0.013834057375788689, + 0.021352780982851982, + 0.015831299126148224, + 0.007239417172968388, + 0.006484704557806253, + 0.0007748982752673328, + 0.02187461405992508, + 0.006419666577130556, + -0.02753227949142456, + 0.012923848815262318, + 0.05481375753879547, + 0.048501141369342804, + 0.021167611703276634, + -0.008767640218138695, + 0.044724225997924805, + 0.03190970793366432, + -0.03760351240634918, + -0.02510974369943142, + 0.036107588559389114, + 0.018606748431921005, + 0.0013486403040587902, + 0.003583558602258563, + -0.0005723497597500682, + -0.01023051980882883, + -0.0014533291105180979, + -0.01704094000160694, + -0.008819067850708961, + -0.000364630512194708, + 0.044731900095939636, + -0.01791875809431076, + -0.003537974786013365, + -0.01289265975356102, + -0.012916864827275276, + 0.024609452113509178, + -0.10247180610895157, + -0.002991416258737445, + -0.24295282363891602, + 0.011598913930356503, + 0.025462577119469643, + 0.008338489569723606, + -0.0038610671181231737, + 0.03725805506110191, + 0.03301859274506569, + -0.004735979251563549, + 0.05780889093875885, + -0.031289421021938324, + -0.020042849704623222, + 0.030781004577875137, + -0.03701555356383324, + 0.012256136164069176, + 0.004996793810278177, + -0.006800163071602583, + -0.030369289219379425, + 0.03819079324603081, + 0.045333683490753174, + -0.04375169798731804, + 0.00840410403907299, + -0.0038509019650518894, + -0.007090441882610321, + 0.03222572058439255, + 0.02958499640226364, + -0.01293813157826662, + -0.0014112554490566254, + -0.030609378591179848, + -0.031445007771253586, + -0.049391236156225204, + 0.034869033843278885, + 0.019697802141308784, + -0.001195654971525073, + -0.005373305641114712, + -0.0005598984425887465, + 0.08037514984607697, + 0.015163823962211609, + 0.033604688942432404, + -0.018393903970718384, + -0.038605887442827225, + 0.013582331128418446, + 0.03631427884101868, + 0.0026640761643648148, + 0.00023398705525323749, + 0.0220998153090477, + -0.01587837003171444, + -0.04844058305025101, + 0.00840139202773571, + -0.032643191516399384, + -0.0041824388317763805, + 0.03264247998595238, + 0.03699221834540367, + -0.03759465366601944, + -0.02239030785858631, + 0.011140204034745693, + -0.001871823682449758, + -0.051223982125520706, + 0.05368606746196747, + -0.055197883397340775, + -0.03498657047748566, + -0.00863657146692276, + -0.026188483461737633, + 0.014528170228004456, + -0.016433481127023697, + -0.001992054982110858, + 0.0320773720741272, + 0.02666051685810089, + -0.008178249932825565, + 0.006640659645199776, + 0.025008682161569595, + -0.011911988258361816, + 0.02045847848057747, + 0.017233621329069138, + -0.01692521758377552, + -0.011785862036049366, + -0.03882060945034027, + 0.0252886600792408, + 0.03421497344970703, + 0.026224985718727112, + -0.05969436466693878, + -0.0030934466049075127, + 0.04582706093788147, + -0.014059755951166153, + -0.048203956335783005, + 0.02619677223265171, + 0.001276739058084786, + -0.002458001486957073, + -0.0016284692101180553, + 0.002541160909458995, + -0.01103358343243599, + 0.00971656758338213, + -0.020702321082353592, + 0.026620903983712196, + -0.05990889295935631, + -0.04678182676434517, + 0.009472327306866646, + 0.017065012827515602, + -0.0030098927672952414, + 0.0036550371441990137, + 0.018740961328148842, + -0.011360288597643375, + -0.008621584624052048, + 0.022231267765164375, + 0.058572471141815186, + -0.04653991013765335, + 0.012018862180411816, + -0.012046482414007187, + 0.055675096809864044, + -0.005464829970151186, + 0.0210801400244236, + 0.00823825504630804, + -0.0013199371751397848, + -0.02809707261621952, + 0.011078724637627602, + -0.05820984020829201, + 0.0384279265999794, + 0.04795660451054573, + 0.010070789605379105, + 0.006662407889962196, + 0.0026539480313658714, + 0.02680898830294609, + -0.043219611048698425, + -0.0168137326836586, + 0.010922158136963844, + 0.04206697270274162, + 0.01832614839076996, + -0.009391307830810547, + 0.03756701946258545, + -0.02663751319050789, + 0.017614342272281647, + 0.005461606662720442, + -0.006811159197241068, + -0.03193753585219383, + 0.012270264327526093, + -0.015109249390661716, + -0.014580895192921162, + 0.00023300228349398822, + -0.00425089243799448, + 0.011478322558104992, + -0.013336959294974804, + 0.02974882535636425, + 0.021488718688488007, + 0.01252420898526907, + 0.02335747517645359, + -0.0326257161796093, + 0.015276833437383175, + -0.006026203744113445, + 0.018406681716442108, + -0.024624329060316086, + 0.04814275726675987, + 0.04380162060260773, + 0.0027461140416562557, + -0.02145235612988472, + 0.021563228219747543, + -0.013421284034848213, + 0.027600057423114777, + -0.01501473318785429, + -0.0034741756971925497, + -0.010983256623148918, + 0.016431735828518867, + 0.020161539316177368, + 0.009254430420696735, + 0.0424148328602314, + 0.03756491839885712, + -0.007651074789464474, + -0.019989371299743652, + 0.01290120929479599, + 0.018727872520685196, + 0.015445931814610958, + -0.03238695487380028, + -0.020279057323932648, + -0.004759599454700947, + 0.019192593172192574, + -0.017359480261802673, + 0.029874345287680626, + 0.019825436174869537, + -0.017534790560603142, + -0.03534829989075661, + 0.03340782970190048, + -0.02759840153157711, + -0.036295536905527115, + -0.00029222870944067836, + 0.0013421204639598727, + -0.018522636964917183, + 0.005591934081166983, + -0.018951281905174255, + 0.009226460009813309, + 0.006501433905214071, + 0.011178688146173954, + 0.027929726988077164, + 0.01031883992254734, + 0.006816364359110594, + 0.008157644420862198, + -0.0033298826310783625, + 0.04031805321574211, + 0.016376735642552376, + 0.007111456245183945, + -0.022918079048395157, + -0.008503425866365433, + 0.007045740261673927, + 0.016068842262029648, + 0.022056875750422478, + 0.060774125158786774, + 0.013712126761674881, + 0.012013175524771214, + 0.009080863557755947, + 0.026097966358065605, + 0.03231704235076904, + -0.005953023675829172, + 0.021540803834795952, + 0.00511836027726531, + -1.2363897440081928e-05, + 0.0002088645996991545, + -0.014014060609042645, + -0.031571611762046814, + 0.03381577506661415, + 0.014915112406015396, + 0.008633784018456936, + -0.03693591058254242, + -0.005792846437543631, + 0.02876618131995201, + -0.01466277800500393, + -0.037371210753917694, + 0.021682575345039368, + 0.03624630719423294, + 0.014295686036348343, + -0.02895914763212204, + -0.022079819813370705, + -0.01677045039832592, + 0.01606665551662445, + 0.031675074249506, + -0.009572780691087246, + -0.031233984977006912, + 0.058748386800289154, + -0.07357829809188843, + 0.0074105579406023026, + -0.022062908858060837, + 0.0010210539912804961, + -0.0327998511493206, + -0.005799780134111643, + -0.029380563646554947, + 0.03825956583023071, + 0.009036656469106674, + -0.03333304822444916, + -0.013072335161268711, + 0.02874562330543995, + 0.059515006840229034, + 0.0020079552195966244, + -0.009610190987586975, + -0.012871215119957924, + 0.04052883759140968, + 0.006436359137296677, + 0.028164170682430267, + -0.0014216587878763676, + -0.032230887562036514, + 0.00883815810084343, + -0.02568723075091839, + -0.0037008498329669237, + 0.025722183287143707, + 0.014880141243338585, + 0.007783344015479088, + -0.04918356612324715, + -0.01022561639547348, + 0.021005278453230858, + 0.03504839539527893, + -0.03176739439368248, + -0.04689481854438782, + 0.03609900549054146, + -0.00944937951862812 + ] + }, + { + "name": "bear_motion_2.png", + "file-path": "E:\\Pratham\\2025\\Harsh Sir\\Scratch Vision\\images\\sprites\\Bear.sprite3\\bear_motion_2.png", + "embeddings": [ + 0.00040901536704041064, + -0.022078847512602806, + -0.01539201382547617, + 0.018699584528803825, + 0.008808658458292484, + -0.010410488583147526, + -0.007828498259186745, + 0.031416285783052444, + -0.03825430944561958, + 0.0005649153608828783, + 0.023776764050126076, + -0.003772653406485915, + 0.0038379300385713577, + 0.024695374071598053, + 0.027231406420469284, + 0.005333045497536659, + -0.006977793760597706, + -0.023264721035957336, + -0.01997135952115059, + -0.0074860407039523125, + -0.03630887344479561, + 0.014975609257817268, + 0.009710656479001045, + 0.033280689269304276, + -0.0015259748324751854, + 0.018481697887182236, + -0.016960781067609787, + 0.03628723695874214, + -0.019314909353852272, + 0.0056377314031124115, + 0.005074959248304367, + -0.023167314007878304, + 0.0375058576464653, + 0.013122618198394775, + -0.00604321388527751, + -0.026850242167711258, + 0.02037687599658966, + 0.027128979563713074, + -0.012984945438802242, + 0.02376280166208744, + 0.00015925026673357934, + 0.017194893211126328, + -0.016381269320845604, + -0.04916417971253395, + -0.0223353561013937, + -0.030468881130218506, + -0.029218096286058426, + -0.04354773461818695, + -0.04241354018449783, + 0.012620147317647934, + -0.006793234962970018, + -0.037638720124959946, + 0.000858261832036078, + -0.0010691932402551174, + -0.015307290479540825, + -0.0052240691147744656, + 0.00461441557854414, + -0.00791766494512558, + -0.029151346534490585, + -0.00598522974178195, + 0.005134657025337219, + -0.0077479686588048935, + -0.03404620289802551, + 0.01840890757739544, + 0.029338788241147995, + -0.03461034968495369, + -0.020650455728173256, + 0.003050255123525858, + 0.044569287449121475, + 0.00013883366773370653, + 0.003699576249346137, + 0.006234190426766872, + -0.00851910375058651, + 0.0006619697087444365, + -0.05564011260867119, + -0.02309955656528473, + 0.003560899291187525, + 0.04834059253334999, + -0.01608293503522873, + 0.045714735984802246, + -0.022263457998633385, + 0.010644728317856789, + 0.012361031025648117, + -0.025031356140971184, + -0.004054626915603876, + 0.028471702709794044, + 0.023580282926559448, + -0.026735343039035797, + -0.006484506651759148, + -0.018726229667663574, + 0.031537894159555435, + -0.009788228198885918, + 0.024923747405409813, + 0.011740275658667088, + -0.0019242559792473912, + -0.0010925827082246542, + -0.05889561027288437, + -0.011005979962646961, + 0.02715776301920414, + -0.019004924222826958, + 0.020534710958600044, + -0.009408699348568916, + -0.010451853275299072, + 0.0343015193939209, + 0.030173923820257187, + 0.011027436703443527, + 0.00864448957145214, + -0.0027034496888518333, + 0.010926232673227787, + 0.018548699095845222, + 0.016526715829968452, + 0.015018163248896599, + 0.016441496089100838, + 0.04686852544546127, + 0.012851388193666935, + 0.04196794331073761, + -0.0015283217653632164, + 0.004913083277642727, + 0.011578396894037724, + -0.020183762535452843, + 0.002765174489468336, + -0.03419386222958565, + 0.05098593980073929, + -0.0037075229920446873, + 0.028249546885490417, + 0.012329714372754097, + 0.07968511432409286, + 0.09651383012533188, + 0.0032325773499906063, + -0.03537837043404579, + -0.0500064492225647, + 0.05516548454761505, + -0.01565915159881115, + -0.017598416656255722, + 0.010947810485959053, + 0.02639191597700119, + 0.004373111296445131, + 0.04656539112329483, + 0.011707049794495106, + 0.020198388025164604, + -0.01919354312121868, + -0.02239813096821308, + 0.0067018670961260796, + 0.04812968522310257, + 0.011871838010847569, + 0.02231936901807785, + 0.004126652609556913, + -0.0021851963829249144, + -0.026182034984230995, + -0.005496711004525423, + 0.05104588344693184, + 0.017206396907567978, + -0.021855121478438377, + 0.021449711173772812, + -0.01643637754023075, + -0.0012675339821726084, + 0.024204498156905174, + -0.01689431257545948, + -0.015485878102481365, + -0.01661885529756546, + 0.01201312243938446, + -0.002927052555605769, + -0.04302264750003815, + 0.009058456867933273, + 0.002654146868735552, + -0.02021971344947815, + -0.04732313007116318, + -0.016897324472665787, + 0.001985144568607211, + -0.04916156828403473, + -0.018386753275990486, + 0.00023575396335218102, + 0.018314776942133904, + -0.0033807719592005014, + -0.013556815683841705, + -0.013527429662644863, + -0.012537509202957153, + -0.013784998096525669, + -0.006447178777307272, + 0.02195827104151249, + -0.025076402351260185, + -0.021792618557810783, + 0.021252362057566643, + 0.014175591990351677, + 0.01749737747013569, + 0.005755906458944082, + -0.024024315178394318, + -0.017848659306764603, + -0.004883652087301016, + 0.02564408630132675, + -0.00451777596026659, + -0.000210362282814458, + -0.005347165744751692, + 0.03431791439652443, + -0.0015646847896277905, + 0.021663246676325798, + 0.017858510836958885, + -0.06123644858598709, + 0.029421010985970497, + 0.02522961050271988, + -0.0003633705491665751, + -0.0034214884508401155, + -0.01722271740436554, + 0.04073714464902878, + 0.0007614793139509857, + 0.008795813657343388, + 0.03582550585269928, + 0.006932573392987251, + 0.004142696037888527, + -0.015499768778681755, + -0.05037078633904457, + 0.004146840423345566, + 0.012320205569267273, + 0.06834297627210617, + 0.016246583312749863, + 0.03439807519316673, + 0.022373011335730553, + 0.027315640822052956, + 0.005600132513791323, + 0.0494762547314167, + -0.03940538689494133, + 0.019645681604743004, + -0.004890758544206619, + 0.00481922272592783, + 0.07814991474151611, + 0.02172827534377575, + -0.023326249793171883, + -0.03227873891592026, + 0.0071288906037807465, + -0.0052856570109725, + 0.04168561473488808, + -0.010063051246106625, + -0.00698934867978096, + -0.020378559827804565, + -0.035640593618154526, + -0.02066226676106453, + 0.01774892397224903, + 0.028659077361226082, + 0.007376376073807478, + -0.02789323776960373, + -0.02003202587366104, + 0.006596304941922426, + 0.007290573325008154, + -0.014351842924952507, + 0.004035871010273695, + -0.05011385306715965, + 0.02130047418177128, + -0.004226986784487963, + -0.0034663674887269735, + -0.027945036068558693, + 0.049052514135837555, + 0.02578091062605381, + 0.011828964576125145, + 0.02394547127187252, + -0.02169831655919552, + -0.030064187943935394, + 0.024945847690105438, + -0.0322868712246418, + -0.05502729117870331, + -0.01575576886534691, + -0.004041827749460936, + -0.004172641783952713, + -0.008459696546196938, + -0.0011181844165548682, + 0.016279835253953934, + 0.025979500263929367, + -0.018920186907052994, + -0.03893492743372917, + 0.004930630326271057, + 0.0006201877840794623, + -0.012233962304890156, + 0.015862930566072464, + -0.021272148936986923, + 0.020392917096614838, + -0.0077965594828128815, + -0.042944759130477905, + -0.012945136055350304, + 0.004704964347183704, + -0.005424723960459232, + -0.006643326487392187, + -0.019215285778045654, + 0.009458139538764954, + 0.01877816952764988, + -0.006938401143997908, + -0.007817002013325691, + 0.00843231100589037, + 0.021507127210497856, + 0.00708385044708848, + -0.018331414088606834, + 0.01580633409321308, + -0.014647296629846096, + 0.02910773828625679, + 0.007712711114436388, + 0.0008089964394457638, + 0.02211267501115799, + 0.00026092599728144705, + 0.013145188800990582, + 0.022565048187971115, + -0.02706596627831459, + 0.022666364908218384, + -0.05419398844242096, + 0.044695790857076645, + -0.025167815387248993, + 0.023773154243826866, + 0.0035802104976028204, + 0.0019564011599868536, + -0.030488573014736176, + 0.018455032259225845, + 0.05379881337285042, + 0.033686213195323944, + 0.034906886518001556, + 0.059969957917928696, + -0.003867038758471608, + 0.023647578433156013, + 0.03623906150460243, + 0.005677386652678251, + 0.008979045785963535, + 0.01627204194664955, + 0.01705142669379711, + 0.027020549401640892, + -0.006836355198174715, + 0.005206563044339418, + 0.006254024337977171, + -0.015634169802069664, + 0.016796018928289413, + 0.012134664691984653, + -0.021197425201535225, + 0.025981396436691284, + 0.016087671741843224, + -0.002097497694194317, + -0.05560467764735222, + 0.006047071423381567, + -0.0026093332562595606, + 0.007794591598212719, + 0.02575090527534485, + 0.014507596381008625, + 0.034877195954322815, + 0.03067062795162201, + 0.009604418650269508, + 0.006209539249539375, + 0.003864188678562641, + 0.0035803469363600016, + 0.04743169993162155, + -0.03806695342063904, + -0.013606091029942036, + 0.05367889627814293, + -0.06681440025568008, + 0.030462661758065224, + 0.0007062521181069314, + 0.027944456785917282, + 0.003263624617829919, + -0.014729281887412071, + -0.02446640655398369, + -0.025636762380599976, + 0.05815225467085838, + -0.007762229070067406, + -0.020193206146359444, + 0.053473565727472305, + 0.017646197229623795, + -0.048336926847696304, + -0.019574256613850594, + 0.0004000403278041631, + -0.013919027522206306, + -0.03376977518200874, + 0.0036517998669296503, + -0.0163055919110775, + 0.010390563867986202, + -0.02267933078110218, + -0.004213074222207069, + -0.04440861567854881, + 0.01801346428692341, + 0.014467280358076096, + -0.024913201108574867, + -0.04208369553089142, + 0.015477697364985943, + 0.02443588711321354, + 0.006593239493668079, + -0.035070400685071945, + -0.010488396510481834, + -0.0071951295249164104, + 0.039179570972919464, + 0.07491316646337509, + -0.05939101800322533, + -0.010720381513237953, + -0.008165200240910053, + 0.10308793932199478, + -0.031485673040151596, + 0.022191716358065605, + -0.008440766483545303, + 0.023397982120513916, + -0.024631422013044357, + -0.022110478952527046, + -0.023470386862754822, + 0.002950417809188366, + 0.013680823147296906, + 0.024289065971970558, + 0.005632245913147926, + 0.004542394541203976, + 0.036350879818201065, + -0.03860032930970192, + -0.012049657292664051, + -0.026575570926070213, + -0.021483566612005234, + 0.018055178225040436, + 0.017605988308787346, + 0.007328787352889776, + -0.01775098778307438, + 0.028624167665839195, + 0.009167879819869995, + 0.018599100410938263, + -0.0331423282623291, + -0.009435568004846573, + 0.015221264213323593, + -0.021967235952615738, + 0.0005461439723148942, + 0.003600248834118247, + -0.027362853288650513, + 0.010796155780553818, + -0.00041944015538319945, + 0.01871187426149845, + -0.0390925332903862, + -0.00190246244892478, + -0.0036331091541796923, + -0.004297832027077675, + -0.04347951337695122, + 0.0037508923560380936, + -0.022861473262310028, + 0.01363447681069374, + 0.01667631044983864, + -0.0344092883169651, + 0.03939684480428696, + 0.01912301778793335, + 0.024753008037805557, + 0.026493234559893608, + -0.06983503699302673, + -0.05568719655275345, + -0.02204647660255432, + 0.03144027292728424, + 0.028749139979481697, + 0.008685301057994366, + -0.041555874049663544, + -0.011527029797434807, + -0.03908563405275345, + 0.00769397895783186, + -0.0020359372720122337, + -0.024526720866560936, + -0.03417372703552246, + 0.0006176437600515783, + -0.031333256512880325, + -0.06431937217712402, + -0.060122597962617874, + 0.013744422234594822, + -0.02107076160609722, + -0.0028869444504380226, + 0.004278257023543119, + -0.018932510167360306, + 0.0028515602461993694, + 0.38253939151763916, + 0.07467208057641983, + 0.001982528017833829, + 0.04184574633836746, + 0.010367144830524921, + -0.035407911986112595, + -0.008830595761537552, + 0.03445715457201004, + -0.02481008507311344, + -0.016087450087070465, + -0.013537653721868992, + -0.04976027086377144, + 0.008933636359870434, + 0.017860792577266693, + 0.030587103217840195, + 0.01229412853717804, + -0.0420963317155838, + 0.009614218026399612, + -0.04927247017621994, + 0.011895288713276386, + 0.008709785528481007, + -0.03423893451690674, + 0.013795176520943642, + 0.020292336121201515, + 0.004404045641422272, + 0.027378659695386887, + 0.01157448347657919, + 0.023881696164608, + -0.030338499695062637, + 0.008205404505133629, + 0.01823464222252369, + 0.07635098695755005, + -0.02286611869931221, + -0.034007564187049866, + -0.0003314009227324277, + -0.020675379782915115, + -0.028424398973584175, + 0.03924654796719551, + -0.02082856185734272, + -0.026427576318383217, + -0.034930866211652756, + 0.03458318114280701, + -0.007264660205692053, + -0.0035792572889477015, + 0.020544065162539482, + -0.029112430289387703, + -0.028942592442035675, + 0.025567280128598213, + 0.020372096449136734, + 0.016806604340672493, + -5.407896969700232e-05, + 0.00878643337637186, + 0.02107759192585945, + -0.022517971694469452, + -0.02949199266731739, + -0.030772296711802483, + 0.008126931264996529, + -0.03466416522860527, + 0.02106177806854248, + 0.010819166898727417, + 0.03313470631837845, + 0.02484981343150139, + 0.003447914496064186, + -0.028923820704221725, + 0.009418657049536705, + 0.013463056646287441, + 0.02889206074178219, + 0.010766712948679924, + -0.03166252002120018, + 0.027865655720233917, + 0.01021889504045248, + -0.04908077418804169, + 0.04669271782040596, + 0.024003159254789352, + 0.0028412623796612024, + -0.034643515944480896, + 0.029377948492765427, + 0.0244160033762455, + 0.021948019042611122, + 0.021939346566796303, + -0.018688401207327843, + -0.052250951528549194, + -0.0008306775707751513, + -0.00013832800323143601, + -0.003002853598445654, + -0.08413282781839371, + -0.0040143015794456005, + 0.00014651498349849135, + 0.0007348849321715534, + 0.021402908489108086, + -0.03336241468787193, + 0.008331465534865856, + 0.02207220159471035, + -0.008145787753164768, + 0.02780132368206978, + 0.021479995921254158, + 0.08773963898420334, + 0.0012849493650719523, + -0.014686455950140953, + 0.0328444205224514, + -0.01518197450786829, + -0.0023253532126545906, + -0.04129938781261444, + -0.04410260170698166, + -0.021776923909783363, + -0.024200022220611572, + 0.027426037937402725, + -0.03886721655726433, + 0.015873197466135025, + 0.006665338296443224, + -0.0009920307202264667, + -0.006233362015336752, + -0.00856848806142807, + -0.015470926649868488, + -0.0036056817043572664, + 0.026524493470788002, + -0.016676299273967743, + -0.009551770985126495, + -0.005291708745062351, + -0.01277504675090313, + -0.022594526410102844, + -0.010418501682579517, + -0.019817836582660675, + -0.02804332785308361, + -0.03822173923254013, + -0.017345989122986794, + 0.04450060427188873, + -0.02520289272069931, + 0.014048594050109386, + -0.013219239190220833, + 0.05273018032312393, + -0.06468071788549423, + 0.015421735122799873, + -0.05475083738565445, + 0.04157957062125206, + 0.02655240334570408, + 0.013532306998968124, + 0.006044568959623575, + 0.033870670944452286, + -0.028228547424077988, + 0.02568536438047886, + 0.03174055740237236, + 0.019721174612641335, + -0.014681270346045494, + -0.007778167724609375, + 0.017443491145968437, + -0.009923777543008327, + 0.020686274394392967, + -0.007969791069626808, + -0.009823059663176537, + -0.005877213086932898, + 0.02583521232008934, + 0.041300009936094284, + 0.028866546228528023, + -0.05503305420279503, + -0.008335486985743046, + 0.008461209945380688, + 0.011607931926846504, + -0.015851423144340515, + 0.0022903415374457836, + -0.05994541198015213, + 0.006178572773933411, + 0.0030248111579567194, + 0.015589585527777672, + -0.0028401105664670467, + -0.03149203956127167, + 0.002457548165693879, + 0.008623536676168442, + -0.022297624498605728, + -0.08705076575279236, + -0.028805427253246307, + -0.03832714259624481, + 0.027374520897865295, + 0.019129281863570213, + -0.019449060782790184, + -0.003228847635909915, + -0.008437708020210266, + -0.005252630449831486, + -0.028165366500616074, + 0.05757277458906174, + -0.019111156463623047, + 0.014222710393369198, + -0.05594760552048683, + -0.015625860542058945, + 0.010868813842535019, + 0.03014875389635563, + 0.0077211433090269566, + -0.009445532225072384, + -0.015071737580001354, + 0.046234577894210815, + -0.03096093237400055, + -0.07224995642900467, + 0.005013321992009878, + 0.004479968920350075, + -0.0045149484649300575, + -0.014001984149217606, + -0.009181005880236626, + -0.013960031792521477, + -0.020046181976795197, + -0.016600709408521652, + 0.0023266479838639498, + -0.014350228011608124, + 0.029886387288570404, + 0.004976233933120966, + -0.012108982540667057, + -0.05427687242627144, + -0.005554371979087591, + -0.047145742923021317, + 0.037067390978336334, + 0.040418561547994614, + -0.02410430647432804, + -0.029162343591451645, + 0.06193315237760544, + -0.018884645774960518, + 0.01419132947921753, + -0.007516161072999239, + 0.009673641063272953, + 0.01296802144497633, + 0.04834702983498573, + 0.022170737385749817, + 0.02046489529311657, + 0.01784972846508026, + 0.024030495434999466, + -0.0005739539628848433, + 0.05577002838253975, + -0.012140913866460323, + 0.006585475988686085, + -0.019186198711395264, + -0.01822042465209961, + -0.020247329026460648, + 0.05412713438272476, + 0.001994319027289748, + -0.12642136216163635, + -0.04556901380419731, + -0.03554723411798477, + -0.015205470845103264, + -0.000789691403042525, + -0.01892881654202938, + -0.009078520350158215, + 0.008543089032173157, + 0.00865248218178749, + 0.008242939598858356, + 0.008234256878495216, + 0.026666341349482536, + -0.09028824418783188, + -0.045409195125103, + 0.037552207708358765, + -0.007170874625444412, + 0.013059290125966072, + -0.0037255901843309402, + -0.025770602747797966, + -0.022676490247249603, + 0.00956576969474554, + -0.005659780465066433, + -0.07010911405086517, + -0.008131599053740501, + -0.003840080928057432, + 0.006600881926715374, + -0.021668728440999985, + 0.004141606390476227, + 0.006050471682101488, + -0.009904494509100914, + -0.01644042320549488, + 0.007243528962135315, + -0.008531039580702782, + -0.04418778792023659, + 0.03905688226222992, + 0.005892476532608271, + 0.0016837043222039938, + 0.010679632425308228, + -0.023903196677565575, + 0.014761981554329395, + 0.028508245944976807, + -0.014095810241997242, + 0.010340197943150997, + 0.01045604981482029, + 0.031078442931175232, + 0.009767348878085613, + 0.005047545302659273, + -0.022148458287119865, + 0.013167050667107105, + -0.015924599021673203, + -0.05691109970211983, + -0.07564733922481537, + -0.046848081052303314, + -0.0013646329753100872, + 0.014748113229870796, + -0.015560140833258629, + -0.030614091083407402, + 0.011612873524427414, + 0.001322538242675364, + -0.018303748220205307, + -0.013677291572093964, + -0.02594265528023243, + -0.0026362573262304068, + 0.006311435252428055, + -0.050040118396282196, + 0.03193243592977524, + -0.3407336175441742, + 0.03767876699566841, + 0.0368039496243, + 0.011470247060060501, + -0.007933596149086952, + 0.0290034431964159, + 0.03212568536400795, + -0.024027986451983452, + 0.0446811318397522, + 0.009044925682246685, + -0.006297517102211714, + 0.015729106962680817, + -0.05329449102282524, + 0.034700553864240646, + 0.04040362313389778, + -0.0026561259292066097, + -0.018401261419057846, + 0.014198065735399723, + 0.03911636397242546, + 0.003914118278771639, + -0.039387401193380356, + 0.07134009152650833, + 0.002046010922640562, + -0.026040583848953247, + -0.011610281653702259, + 0.06189154461026192, + -0.021557530388236046, + -0.007829380221664906, + 0.011354026384651661, + -0.011169398203492165, + 0.035114604979753494, + 0.011397154070436954, + 0.002827616408467293, + 0.033360909670591354, + -0.006442180834710598, + 0.06964869052171707, + 0.0027264729142189026, + -0.014011461287736893, + 0.02792457304894924, + -0.035420529544353485, + -0.01777271181344986, + 0.0023923174012452364, + 0.00274100573733449, + -0.008639995008707047, + -0.011870869435369968, + -0.004904134199023247, + 0.005506603512912989, + -0.03515595197677612, + 0.014839553274214268, + -0.013303865678608418, + 0.023123057559132576, + -0.0188902597874403, + -0.05729237571358681, + -0.03216319531202316, + -0.037770796567201614, + 0.006401685997843742, + -0.02915327250957489, + 0.0034721256233751774, + 0.0016136313788592815, + -0.06803353875875473, + 0.03967099264264107, + 0.025091590359807014, + 0.010773370042443275, + -0.011401623487472534, + -0.025601550936698914, + 0.00792152713984251, + 0.003297259798273444, + -0.02365518920123577, + 0.014416634105145931, + 0.013847865164279938, + 0.02965405024588108, + 0.017079409211874008, + -0.026607491075992584, + 0.009678014554083347, + 0.03434285521507263, + -0.026392413303256035, + -0.017103204503655434, + 0.010204966180026531, + 0.010195090435445309, + -0.02090533636510372, + 0.008728502318263054, + -0.0055791824124753475, + -0.0049914647825062275, + -0.034618448466062546, + 0.01594240963459015, + 0.0021271295845508575, + -0.014067430049180984, + -0.011258439160883427, + -0.021224291995167732, + -0.04355097934603691, + -2.3320597392739728e-05, + 0.03200468420982361, + 0.0061884680762887, + -0.006852100137621164, + -0.018147950991988182, + -0.0018296828493475914, + 0.021995464339852333, + 0.03528605401515961, + 0.014254895970225334, + 0.02822519838809967, + -0.02527862787246704, + 0.0314120315015316, + 0.0200178362429142, + 0.013658180832862854, + -0.02656961791217327, + 0.01732529141008854, + -0.011777146719396114, + 0.010169684886932373, + -0.01415513176470995, + 0.011145984753966331, + -0.01071961224079132, + -0.012136455625295639, + -0.017899010330438614, + -0.014941270463168621, + -0.08236493915319443, + -0.015898503363132477, + -0.001471128431148827, + 0.010122520849108696, + -0.032129183411598206, + 0.001046184916049242, + 0.03668518736958504, + -0.04203959181904793, + 0.032328709959983826, + -0.05443091690540314, + 0.07419463992118835, + 0.01752868853509426, + 0.018744751811027527, + 0.018526537343859673, + -0.00098281295504421, + 0.0172415878623724, + -0.0037814516108483076, + 0.011623548343777657, + 0.038373321294784546, + 0.019973894581198692, + 0.010041741654276848, + 0.024701755493879318, + 0.03745478764176369, + -0.012367289513349533, + 0.010637037456035614, + -0.015360535122454166, + -0.004307935480028391, + -0.002482782583683729, + -0.009629474021494389, + 0.001228120643645525, + -0.03509479761123657, + -0.009993644431233406, + 0.016778219491243362, + 0.005138650070875883, + -0.012095668353140354, + 0.026047727093100548, + -0.006291487719863653, + 0.0614120177924633, + 0.01500829216092825, + 0.0015276053454726934, + 0.002896019956097007, + 0.03896510601043701, + 0.014779752120375633, + -0.02387095056474209, + -0.02289419248700142, + -0.01179139781743288, + -0.010806357488036156, + 0.04529980570077896, + 0.022431058809161186, + -0.009780790656805038, + -0.012089011259377003, + -0.045099496841430664, + 0.04122015833854675, + 0.002608413575217128, + -0.004287667106837034, + -0.040103040635585785, + -0.017043817788362503, + 0.034263987094163895, + 0.002461482770740986, + -0.013623721897602081, + 0.019236542284488678, + 0.0337061882019043, + -0.01074964739382267, + -0.01732976734638214, + -0.00906462874263525, + -0.03019011951982975, + 0.0003470746742095798, + 0.01912073977291584, + 0.002835890045389533, + -0.00455661304295063, + -0.05258354917168617, + -0.056549716740846634, + -0.01235918514430523, + -0.0005048388266004622, + -0.004631245043128729, + 0.05775855854153633, + -0.010098994709551334, + 0.002210016595199704, + 0.00824183039367199, + -0.02727901004254818, + 0.02331133931875229, + 0.034581348299980164, + -0.024982325732707977, + 0.01793224923312664, + -0.006244600750505924, + -0.01782258413732052, + 0.027264373376965523, + 0.0018676556646823883, + -0.0003813039802480489, + 0.0026135107036679983, + 0.002584462985396385, + -0.05158800259232521, + 0.029616666957736015, + -0.013433343730866909, + -0.008285541087388992, + 0.007484233472496271, + 0.02252098359167576, + -0.018409425392746925, + -0.019319618120789528, + 0.01653243973851204, + 0.00810432992875576, + 0.02846876159310341, + 0.006868876051157713, + 0.010328571312129498, + -0.026749128475785255, + -0.0027676147874444723, + 0.013659272342920303, + 0.0515713132917881, + -0.0238807313144207, + 0.010247286409139633, + 0.016459966078400612, + -0.0189349465072155, + 0.00013323880557436496, + -0.012213656678795815, + 0.010755067691206932, + -0.005591605789959431, + 0.006753847468644381, + 0.0020042031537741423, + -0.02021702006459236, + 7.202838787634391e-07, + -0.046493977308273315, + -0.005949323996901512, + -0.006946288514882326, + -0.009967037476599216, + -0.013657726347446442, + -0.0035261940211057663, + 0.010752638801932335, + -0.01142567303031683, + -0.015034984797239304, + -0.03462614491581917, + -0.0054417080245912075, + 0.0375983901321888, + 0.01215152908116579, + 0.033203352242708206, + 0.0020755210425704718, + 0.01371041964739561, + 0.08193949609994888, + -0.03070175088942051, + 0.002192677464336157, + -0.027456749230623245, + -0.02427103742957115, + 0.016668440774083138, + -0.006352184806019068, + -0.011165834963321686, + 0.02872513234615326, + -0.011827089823782444, + 0.001560646458528936, + -0.0030968324281275272, + -0.03077867068350315, + 0.005962778814136982, + -0.02756991796195507, + -0.01807462051510811, + -0.03960555046796799, + 0.0187880527228117, + -0.01018371433019638 + ] + }, + { + "name": "46d0dfd4ae7e9bfe3a6a2e35a4905eae.png", + "file-path": "E:\\Pratham\\2025\\Harsh Sir\\Scratch Vision\\images\\sprites\\Beetle.sprite3\\46d0dfd4ae7e9bfe3a6a2e35a4905eae.png", + "embeddings": [ + 0.024529458954930305, + 0.04336182773113251, + 0.028507797047495842, + 0.011231881566345692, + -0.015658600255846977, + -0.007266619708389044, + -0.014766303822398186, + 0.007060535717755556, + 0.012510063126683235, + 0.007451394107192755, + -0.010216553695499897, + 0.01272477488964796, + -0.01760878786444664, + 0.02372109517455101, + -0.012233493849635124, + 0.0059547568671405315, + 0.011678457260131836, + 0.002156068105250597, + -0.0012980258325114846, + 0.009572167880833149, + 0.004317903891205788, + 0.00796960573643446, + 0.0396432988345623, + 0.06606945395469666, + -0.020475750789046288, + 0.033130060881376266, + 0.008743830025196075, + 0.018918706104159355, + -0.04948264732956886, + 0.030778538435697556, + -0.014723309315741062, + -0.008039673790335655, + 0.052569612860679626, + 0.022506099194288254, + 0.011674818582832813, + 0.007753254845738411, + 0.0390344001352787, + -0.02867419831454754, + 0.0137738436460495, + 0.023930389434099197, + -0.009309654124081135, + 0.02653605490922928, + 0.013841502368450165, + 0.01346572395414114, + -0.059482064098119736, + -0.04542672634124756, + 0.010673893615603447, + -0.10726621747016907, + -0.045791514217853546, + 0.01319844275712967, + -0.03263912349939346, + -0.028297292068600655, + -0.015860537067055702, + 0.03130434826016426, + 0.014855047687888145, + -0.023896057158708572, + 0.031854793429374695, + -0.007077410817146301, + -0.03212318941950798, + 0.01175601128488779, + 0.009494255296885967, + -0.007909378036856651, + -0.024034611880779266, + -0.06613323092460632, + -0.002263500355184078, + -0.007816223427653313, + -0.0017428628634661436, + -0.021632101386785507, + -0.014944064430892467, + -0.01807709038257599, + -0.028685295954346657, + 0.03860640525817871, + 0.0037680733948946, + -0.02079314924776554, + -0.01177684124559164, + -0.004167786333709955, + -0.02484995126724243, + -0.0005659160087816417, + -0.012124557979404926, + 0.005066233221441507, + 0.04342322796583176, + -0.04664655402302742, + 0.07160403579473495, + 0.0006084238993935287, + 0.009945462457835674, + -0.02340456284582615, + -0.010202629491686821, + 0.006549075711518526, + -0.00405152328312397, + 0.028291499242186546, + -0.007369575556367636, + 0.010284134186804295, + -0.06289586424827576, + 0.018844671547412872, + -0.01144601684063673, + -0.007599617354571819, + -0.056878652423620224, + -0.0025925221852958202, + 0.0201027262955904, + -0.010310633108019829, + 0.054225169122219086, + 0.0075644622556865215, + -0.010076377540826797, + 0.013957638293504715, + -0.022270534187555313, + -0.008417997509241104, + -0.010553703643381596, + -0.03332606703042984, + -0.006888790987432003, + 0.001852608984336257, + 0.04862145707011223, + -0.022113656625151634, + 0.00445129070430994, + 0.03580443933606148, + 0.0043292115442454815, + 0.005746839102357626, + 0.01651591621339321, + -0.019019456580281258, + -0.034154362976551056, + 0.008375555276870728, + 0.020990964025259018, + 0.003640363924205303, + 0.00818820483982563, + 0.06790927797555923, + -0.020116690546274185, + 0.024598145857453346, + -0.014064695686101913, + -0.0046109650284051895, + 0.013918603770434856, + 0.020054101943969727, + -0.02740136720240116, + 0.019049152731895447, + -0.042273152619600296, + -0.008328866213560104, + -0.016863660886883736, + 0.0027346729766577482, + 0.04635702446103096, + 0.03367219120264053, + -0.01207262184470892, + -0.011630967259407043, + -0.0005781939253211021, + 0.0026121409609913826, + -0.005202265921980143, + 0.016849234700202942, + -0.022645100951194763, + -0.03530164062976837, + -0.02389662154018879, + -0.020429326221346855, + 0.027892054989933968, + -0.008035932667553425, + 0.011551804840564728, + -0.03255942836403847, + -0.028512554243206978, + -0.00979018583893776, + 0.029069827869534492, + 0.0053589725866913795, + 0.04654945433139801, + -0.05857473239302635, + -0.011717719957232475, + -0.04173620417714119, + 0.021227452903985977, + -0.016943417489528656, + -0.06223401427268982, + 0.02502167783677578, + -0.028115319088101387, + 0.024523796513676643, + -0.017137764021754265, + 0.03829408809542656, + -0.03639434278011322, + -0.009511495009064674, + 0.006155854556709528, + -0.020646855235099792, + -0.019382476806640625, + 0.008983013220131397, + -0.021060610190033913, + -0.037417467683553696, + 0.034354180097579956, + 0.006521334405988455, + -0.018427452072501183, + -0.011691414751112461, + -0.003674635663628578, + -0.06249295175075531, + 0.003771686227992177, + 0.00886549148708582, + 0.015252322889864445, + 0.024315904825925827, + 0.051231373101472855, + 0.02940710447728634, + 0.00480522820726037, + -0.001626207958906889, + -0.013882063329219818, + 0.012624863535165787, + 0.008892901241779327, + -0.01201489195227623, + -0.018547382205724716, + 0.037988364696502686, + -0.0003855471732094884, + 0.00804213434457779, + 0.019862893968820572, + 0.0034732127096503973, + 0.005863431375473738, + -0.005892650224268436, + 0.00788841675966978, + 0.012703047133982182, + -0.015877656638622284, + -0.007659660652279854, + 0.034210655838251114, + -0.0014744042418897152, + 0.006733350921422243, + 0.01621384359896183, + -0.018276266753673553, + 0.026055380702018738, + -0.013765291310846806, + -0.00305331964045763, + 0.005577726289629936, + 0.016893817111849785, + -0.03623313084244728, + -0.021803168579936028, + 0.06273423135280609, + 0.06436827033758163, + -0.07818461209535599, + 0.004006554838269949, + 0.009023168124258518, + -0.016082176938652992, + 0.002554692793637514, + 0.04345088079571724, + -0.028377395123243332, + -0.011002772487699986, + 0.015903981402516365, + -0.005382508505135775, + -0.02573459967970848, + 0.01491892896592617, + -0.005420243367552757, + -0.012247598730027676, + -0.001783160725608468, + -0.05448601022362709, + -0.014406291767954826, + 0.02466302365064621, + 0.0373673215508461, + -0.04901468753814697, + 0.013290767557919025, + -0.012841320596635342, + -0.0032679298892617226, + 0.024977687746286392, + -0.010586504824459553, + -0.013962515629827976, + 0.018169168382883072, + -0.025664115324616432, + -0.032155025750398636, + -0.038199469447135925, + 0.06920257210731506, + 0.030271735042333603, + 0.028910957276821136, + -0.035679932683706284, + 0.005477956961840391, + -0.023071419447660446, + -0.02780570462346077, + 0.020539704710245132, + -0.02321271039545536, + -0.021072566509246826, + 0.0028825902845710516, + 0.02122172713279724, + -0.051557477563619614, + -0.011841896921396255, + -0.0076434011571109295, + -0.007810292765498161, + 0.00494269048795104, + -0.011240034364163876, + -0.0789778083562851, + -0.007720335386693478, + -0.024168020114302635, + -0.014891134575009346, + -0.0075421747751533985, + -0.012486246414482594, + -0.0018585413927212358, + -0.010187300853431225, + 0.003489340888336301, + -0.013353738933801651, + -0.01561862975358963, + -0.010265828110277653, + 0.00423838896676898, + -0.0013626624131575227, + 0.0013817346189171076, + 0.03340994194149971, + -0.02446434460580349, + 0.002381477737799287, + 0.008002850227057934, + -0.024070605635643005, + 0.020378293469548225, + 0.05808156356215477, + 0.014018137939274311, + 0.013899228535592556, + 0.01596909575164318, + -0.011146662756800652, + 0.02246437966823578, + -0.01802825555205345, + -0.03832932189106941, + 0.08625277131795883, + -0.03489840403199196, + 0.040973592549562454, + -0.02784019336104393, + 0.025657394900918007, + 0.047860097140073776, + 0.011149357073009014, + -0.023418430238962173, + 0.03550741448998451, + 0.0510995090007782, + 0.04281871020793915, + 0.0067490641959011555, + 0.005826541688293219, + 0.016476519405841827, + 0.01564566045999527, + 0.00038829975528642535, + 0.0041975905187428, + 0.0012451501097530127, + -0.021984340623021126, + -0.048528943210840225, + 0.03095550276339054, + -0.01776459440588951, + 0.004029592964798212, + -0.011207814328372478, + -0.02948939800262451, + -0.010411778464913368, + -0.016781127080321312, + 0.009186855517327785, + 0.017576562240719795, + -0.046855974942445755, + 0.015968183055520058, + -0.004169895313680172, + -0.04415014013648033, + -0.03624304383993149, + 0.010653631761670113, + 0.018738018348813057, + 0.007851507514715195, + -0.002783265896141529, + 0.01168618630617857, + 0.007202580571174622, + 0.024299457669258118, + -0.012822261080145836, + 0.009437903761863708, + 0.025172317400574684, + 0.0346326120197773, + 0.09020403027534485, + 0.056190717965364456, + 0.04509761929512024, + 0.031134765595197678, + -0.05680999904870987, + -0.0002415405324427411, + 0.014707030728459358, + 0.1451435536146164, + 0.03394880145788193, + -0.01996588706970215, + -0.019716482609510422, + -0.015515146777033806, + 0.0075468155555427074, + -0.007048833183944225, + -0.03022966906428337, + 0.007794538047164679, + 0.016846932470798492, + -0.014322306960821152, + -0.024555251002311707, + -0.034001030027866364, + -0.03347695618867874, + 0.019833790138363838, + -0.03592868149280548, + 0.0430813767015934, + 0.03585321828722954, + -0.012028858996927738, + -0.003957044333219528, + -0.05239105224609375, + 0.019690051674842834, + 0.011738869361579418, + -0.023721493780612946, + 0.010735368356108665, + 0.0017421668162569404, + 0.008922743611037731, + 0.012500711716711521, + -0.005715249106287956, + 0.00253295642323792, + -0.01138758659362793, + -0.008963638916611671, + 0.07700899243354797, + -0.04035664349794388, + 0.05202252045273781, + 0.019913073629140854, + 0.021350108087062836, + 0.010285393334925175, + 0.018750643357634544, + -0.013235373422503471, + 0.11742366850376129, + 0.008375339210033417, + 0.02332000620663166, + 0.009913298301398754, + 0.020685993134975433, + -0.0017225913470610976, + 0.02575492113828659, + -0.011214329861104488, + 0.0066567291505634785, + 0.04446873813867569, + -0.005431791301816702, + 0.04390042647719383, + 0.004613987170159817, + 0.02924373373389244, + 0.0013222966808825731, + -0.0004267361364327371, + -0.013835417106747627, + 0.012161055579781532, + 0.08079631626605988, + -0.04006288945674896, + 0.012872917577624321, + -0.011422249488532543, + -0.0056664347648620605, + 0.04384637996554375, + -0.011932013556361198, + 0.01928657479584217, + -0.010620164684951305, + -0.06412991881370544, + -0.03597697988152504, + -0.00908433087170124, + 0.005715543404221535, + 0.0028452984988689423, + 0.022953709587454796, + -0.026532147079706192, + -0.02137111686170101, + -0.013942020013928413, + -0.004565349780023098, + -0.004669824615120888, + 0.023325689136981964, + -0.05928375944495201, + 0.02663375250995159, + -0.011939853429794312, + 0.018880091607570648, + 0.016250276938080788, + 0.07025370001792908, + -0.006652215961366892, + 0.08275728672742844, + 0.007060853764414787, + 0.022738784551620483, + 0.007162613328546286, + 0.01397215947508812, + -0.06979604065418243, + 0.004467119462788105, + -0.021354716271162033, + 0.011972103267908096, + 0.025776511058211327, + 0.04101739451289177, + -0.043509259819984436, + 0.011854507029056549, + 0.017376502975821495, + -0.005440095905214548, + 0.0024563029874116182, + 0.008180536329746246, + 0.018485909327864647, + 0.03465786948800087, + -0.03530365601181984, + -0.02545824646949768, + -0.029911763966083527, + 0.2738979458808899, + 0.05541259050369263, + 0.004657433368265629, + 0.014678627252578735, + -0.007053883746266365, + 0.016873901709914207, + 0.003410494653508067, + 0.036901816725730896, + -0.03398659825325012, + -0.008100832812488079, + -0.026502203196287155, + 0.016364891082048416, + -0.02119630016386509, + 0.010524817742407322, + 0.030737798660993576, + 0.0409526601433754, + -0.06017129123210907, + -0.021929023787379265, + -0.06064983457326889, + 0.027623247355222702, + -0.04799782857298851, + -0.020152362063527107, + -0.019962137565016747, + 0.030300525948405266, + 0.017851298674941063, + 0.022673489525914192, + 0.014335011132061481, + -0.014792487025260925, + 0.0022412179969251156, + 0.024558942764997482, + 0.008447240106761456, + 0.05443180724978447, + 0.032127831131219864, + 0.013212096877396107, + 0.011318845674395561, + 0.019124835729599, + 0.0033415756188333035, + 0.030432073399424553, + -0.006923231761902571, + 0.03258680924773216, + 0.001848159939981997, + -0.024767884984612465, + 0.020783862099051476, + 0.02073555439710617, + 0.007397613022476435, + -0.007394650485366583, + -0.02200639247894287, + 0.03700115159153938, + -0.010880581103265285, + 0.002334804041311145, + -0.014681041240692139, + -0.009813109412789345, + 0.002105474006384611, + -0.04025264084339142, + -0.018500683829188347, + -0.06698804348707199, + 0.03971784561872482, + -0.03569016605615616, + -0.0005743703222833574, + -0.024556869640946388, + 0.04147128388285637, + 0.0035183397121727467, + 0.013729519210755825, + -0.0017934736097231507, + -0.05282258614897728, + -0.00403930526226759, + 0.046101734042167664, + 0.017868302762508392, + -0.06129007041454315, + -0.001210665563121438, + -0.0043671526946127415, + 0.0008402676321566105, + -0.005800714250653982, + -0.03884335234761238, + -0.00010938302875729278, + 0.00127988844178617, + 0.04423094168305397, + -0.03023979812860489, + 0.0506865493953228, + 0.013038674369454384, + -0.02845936268568039, + 0.005897623021155596, + 0.004341836553066969, + 0.003343133954331279, + -0.03936352580785751, + -0.05439991503953934, + 0.05811430513858795, + 0.023783614858984947, + -0.0071906959637999535, + 0.03237750753760338, + -0.02198539860546589, + 0.04627877473831177, + -0.004884136840701103, + 0.02403109520673752, + 0.013692723587155342, + -0.026898004114627838, + 0.037098586559295654, + 0.03856730833649635, + 0.021383052691817284, + 0.02329617366194725, + 0.012010744772851467, + 0.012105380184948444, + -0.019941864535212517, + 0.017008258029818535, + -0.0034308121539652348, + -0.054456476122140884, + -0.017297295853495598, + -0.0373440757393837, + -0.020381709560751915, + 0.014683703891932964, + -0.00844024121761322, + 0.011969344690442085, + -0.05005604773759842, + 0.0474468357861042, + 0.007488470524549484, + 0.020287444815039635, + -0.03611317276954651, + 0.011579821817576885, + 0.0010316194966435432, + 0.02751622535288334, + 0.00038772638072259724, + -0.01365725975483656, + 0.011729633435606956, + -0.017980389297008514, + -0.002395400544628501, + -0.045794278383255005, + 0.08041071891784668, + 0.013881610706448555, + -0.01073005236685276, + -0.04619758948683739, + 0.039574041962623596, + -0.07260511070489883, + -0.03721263259649277, + -0.05168570205569267, + 0.027411216869950294, + -0.00918076653033495, + -0.0031628378201276064, + 0.020700208842754364, + -0.015398457646369934, + -0.016788624227046967, + -0.0017802397487685084, + 0.019300060346722603, + -0.0147533118724823, + 0.04164614528417587, + -0.03852716088294983, + 0.0037478029262274504, + 0.030448829755187035, + -0.006074624136090279, + 0.01192153338342905, + -0.0009224953246302903, + -0.005467936396598816, + 0.042242299765348434, + 0.01863630674779415, + 0.02607550472021103, + -0.1466270387172699, + -0.01312936283648014, + 0.0031792239751666784, + 0.032623276114463806, + 0.0105947470292449, + -0.0019430775428190827, + -0.0036779316142201424, + -0.020096782594919205, + 0.012609679251909256, + 0.01695760153234005, + -0.020233940333127975, + -0.04363742843270302, + 0.013502857647836208, + -0.018969731405377388, + -0.008480369113385677, + -0.09409406781196594, + -0.007272662594914436, + 0.017084384337067604, + -0.006304149050265551, + 0.03015323542058468, + -0.1416141539812088, + -0.01891583949327469, + -0.011446851305663586, + -0.02169056236743927, + 0.002310081385076046, + -0.0239911787211895, + 0.01793191209435463, + 0.0005106934695504606, + -0.007108879275619984, + -0.002031320007517934, + -0.012280403636395931, + 0.02441207878291607, + 0.013477647677063942, + 0.0005450400640256703, + 0.019664423540234566, + 0.05772020295262337, + 0.04879884049296379, + -0.021452201530337334, + 0.01257314719259739, + -0.03902440145611763, + 0.005721932742744684, + 0.02174096368253231, + 0.002970724366605282, + 0.015617027878761292, + -0.014115331694483757, + 0.014181886799633503, + -0.026639098301529884, + 0.008364041335880756, + 0.023086093366146088, + -0.029793409630656242, + 0.014350734651088715, + 0.03870106115937233, + 0.010038751177489758, + -0.038151711225509644, + -0.03850999101996422, + 0.029811706393957138, + -0.021889884024858475, + -0.023422373458743095, + 0.050152603536844254, + -0.029163915663957596, + 0.0056865522637963295, + 3.8225836760830134e-05, + -0.01689818874001503, + -0.0067302631214261055, + 0.09036044776439667, + -0.03600035980343819, + 0.023334505036473274, + -0.020052200183272362, + -0.001833771588280797, + -0.003862217999994755, + 0.04015923663973808, + -0.045529693365097046, + 0.011332469061017036, + 0.020346015691757202, + 0.0005541574209928513, + 0.023517925292253494, + -0.0013966733822599053, + -0.022353550419211388, + -0.05866079032421112, + -0.013491279445588589, + -0.01834847219288349, + 0.03682452440261841, + -0.006551498547196388, + -0.01149067934602499, + -0.0011335277231410146, + -0.03791382163763046, + -0.0067673237062990665, + -0.011782082729041576, + 0.04424068331718445, + -0.04213711991906166, + -0.10175268352031708, + -0.02343754656612873, + 0.018499892204999924, + 0.01621915213763714, + -0.01628255844116211, + -0.028985338285565376, + -0.0061026387847959995, + 0.0027891509234905243, + 0.0358651727437973, + 0.035528212785720825, + -0.004352435003966093, + 0.009097985923290253, + -0.003568274201825261, + -0.010076836682856083, + 0.010354791767895222, + 0.028923137113451958, + 0.027125712484121323, + -0.0065004341304302216, + -0.024642029777169228, + -0.025982091203331947, + 0.004258206579834223, + -0.049371957778930664, + 0.026005512103438377, + -0.02160421945154667, + -0.013166355900466442, + -0.04348377883434296, + 0.007450313773006201, + -0.020778322592377663, + 0.003462765831500292, + 0.06311915069818497, + 0.02515995129942894, + 0.026574959978461266, + 0.033275336027145386, + 0.019914427772164345, + -0.033119622617959976, + -0.021958211436867714, + 0.004317828454077244, + 0.05319757014513016, + 0.006617746781557798, + -0.02411378175020218, + -0.0559113547205925, + 0.019570745527744293, + -0.020067472010850906, + -0.00536569207906723, + -0.01203561294823885, + 0.01389599870890379, + -0.006274145096540451, + -0.019129976630210876, + 0.007925041019916534, + -0.00019062330829910934, + -0.0026442937087267637, + 0.008732209913432598, + -0.04459332674741745, + 0.030845213681459427, + -0.24786442518234253, + 0.011233367957174778, + 0.047295041382312775, + -0.026780493557453156, + -0.02660624124109745, + 0.01374040450900793, + 0.04812741279602051, + -0.01603677310049534, + 0.04367730766534805, + 0.0018826837185770273, + -0.0017134856898337603, + -0.013165207579731941, + -0.022745182737708092, + 0.004068814218044281, + 0.009587319567799568, + 0.009712869301438332, + -0.018551064655184746, + -0.0008725060033611953, + -0.007340182084590197, + -0.03177532181143761, + 0.034050337970256805, + 0.031352587044239044, + -0.012229656800627708, + 0.020656930282711983, + 0.038262419402599335, + -0.012864985503256321, + -0.03429627791047096, + -0.02207597717642784, + -0.015301991254091263, + 0.0033221584744751453, + 0.026507455855607986, + 0.039062656462192535, + -0.03462766855955124, + 0.019314244389533997, + -0.02051410637795925, + 0.16434967517852783, + 0.020685655996203423, + 0.020520683377981186, + -0.056452296674251556, + -0.023530183359980583, + 0.03858445584774017, + 0.056922320276498795, + 0.012284900061786175, + -0.02295764721930027, + 0.00254343100823462, + -0.0008386356057599187, + 0.009450583718717098, + -0.004669366870075464, + 0.009803245775401592, + -0.020639793947339058, + -0.015263636596500874, + -0.01063199806958437, + -0.025316815823316574, + 0.006885600741952658, + -0.014739946462213993, + -0.009130449034273624, + -0.02806609496474266, + 0.011858292855322361, + -0.008113726042211056, + -0.007346983999013901, + 0.006865581497550011, + 0.002459634095430374, + 0.04762347415089607, + -0.013176175765693188, + -0.029173575341701508, + 0.01945815421640873, + 0.017146576195955276, + -0.008029104210436344, + -0.0025385490152984858, + -0.004160366021096706, + -0.008879687637090683, + 0.015239683911204338, + -0.012532712891697884, + -0.0508299395442009, + 0.013648554682731628, + 0.006029302254319191, + -0.004327795002609491, + 0.030411457642912865, + 0.005774928256869316, + -0.018104596063494682, + -0.005241964478045702, + 0.0017149882623925805, + 0.007918445393443108, + -0.023854801431298256, + 0.026835937052965164, + -0.02336057275533676, + 0.021663779392838478, + -0.0056176804937422276, + -0.0007360683521255851, + -0.05521417409181595, + 0.019843585789203644, + 0.03553074598312378, + 0.005898460280150175, + 0.03967032954096794, + 0.03887774795293808, + -0.018715977668762207, + 0.03206166625022888, + 0.03277480974793434, + -0.01856287755072117, + -0.014723697677254677, + -7.299742719624192e-05, + 0.015091408044099808, + 0.012202983722090721, + 0.009386090561747551, + 0.01089276559650898, + 0.008617997169494629, + -0.0015996076399460435, + 0.01784866489470005, + -0.024886783212423325, + 0.037290289998054504, + 0.0068253022618591785, + -0.024575505405664444, + -0.029554326087236404, + -0.003691369201987982, + -0.04586397483944893, + 0.007666220422834158, + 0.028716901317238808, + -0.020672626793384552, + 0.006604395806789398, + -0.07854625582695007, + 0.0031167673878371716, + -0.009271525777876377, + 0.04070982709527016, + -0.08453558385372162, + 0.007368541322648525, + 0.005500282626599073, + 0.013359110802412033, + 0.018378090113401413, + 0.04071178287267685, + 0.002565004164353013, + -0.03147098049521446, + -0.00018461233412381262, + -0.029975876212120056, + 0.0010838881134986877, + -0.02828286401927471, + -0.006982073187828064, + 0.0039761182852089405, + -0.034487780183553696, + 0.028258809819817543, + 0.02193993143737316, + -0.003213794669136405, + 0.016335833817720413, + 0.05032869800925255, + 0.013315126299858093, + -0.006144736893475056, + -0.008006528951227665, + -0.026776185259222984, + -0.0017576466780155897, + 0.019151195883750916, + 0.060787949711084366, + -0.010297156870365143, + -0.0031729780603200197, + -0.006880441214889288, + -0.011138761416077614, + -0.006517430767416954, + 0.011454691179096699, + 0.01387561485171318, + 0.020181510597467422, + 0.001781907631084323, + -0.010379369370639324, + 0.008951786905527115, + 0.044926661998033524, + 0.0036649880930781364, + -0.0077244737185537815, + -0.03825925290584564, + -0.022278573364019394, + 0.052638351917266846, + -0.027359455823898315, + -0.013731162995100021, + 0.003621000098064542, + 0.01172955147922039, + 0.016645241528749466, + 0.007240036502480507, + 0.023250220343470573, + -0.039668742567300797, + 0.016623228788375854, + 0.020372936502099037, + 0.016147281974554062, + -0.06837623566389084, + 0.007283383514732122, + -0.030322382226586342, + 0.041147541254758835, + 0.050429798662662506, + 0.011375327594578266, + -0.02788657695055008, + -0.06987473368644714, + -0.006766228005290031, + -0.026447826996445656, + -0.03526138514280319, + -0.005925341043621302, + -0.009038588032126427, + -0.05068721994757652, + 0.005660328082740307, + -0.015398780815303326, + 0.008873246610164642, + 0.02588827535510063, + -0.01736731454730034, + -0.023200830444693565, + -0.010121006518602371, + -0.014318390749394894, + 0.026727085933089256, + 0.01688198372721672, + 0.0771477073431015, + -0.005403186660259962, + -0.002722696168348193, + 0.027581417933106422, + -0.009126011282205582, + -0.004626026842743158, + 0.011354781687259674, + 0.004674981348216534, + 0.018168475478887558, + 0.0013400774914771318, + 0.007943239994347095, + -0.0387321412563324, + -0.011258375830948353, + 0.02503369189798832, + -0.005543656647205353, + -0.007212002296000719, + 0.0024669794365763664, + 0.04640971124172211, + 0.023662488907575607, + 0.03112361766397953, + 0.005391424521803856, + -0.00786618236452341, + -0.021503960713744164, + -0.014646693132817745, + -0.010343854315578938, + -0.04167474806308746, + 0.00447339890524745, + 0.022961603477597237, + -0.005895640701055527, + -0.027170147746801376, + 0.02553216740489006, + -0.00019958899065386504, + -0.021526755765080452, + -0.006001652684062719, + -0.018227675929665565, + -0.027352573350071907, + -0.0006553376442752779, + 0.005673595704138279, + 0.0014846163103356957, + 0.047136981040239334, + -0.026741107925772667, + 0.0004642151470761746, + 0.012401403859257698, + 0.03553721681237221, + 0.025927014648914337, + -0.004560310859233141, + -0.0037884246557950974, + -0.008374199271202087, + 0.013358736410737038, + 0.016605595126748085, + 0.0043884459882974625, + -0.027149662375450134, + -0.043035414069890976, + 0.014537854120135307, + -0.02675795741379261, + -0.010243803262710571, + 0.03137079253792763, + -0.023142199963331223, + -0.0023561015259474516, + -0.05225926265120506, + -0.021635999903082848, + 0.0003772160562220961, + 0.00850500538945198, + -0.012522186152637005, + 0.03937123343348503, + 0.024170488119125366, + -0.04637014493346214 + ] + }, + { + "name": "cat_motion_1.png", + "file-path": "E:\\Pratham\\2025\\Harsh Sir\\Scratch Vision\\images\\sprites\\cat\\cat_motion_1.png", + "embeddings": [ + 0.01269167847931385, + -0.018239056691527367, + -0.0016025197692215443, + 0.04196883365511894, + 0.010430092923343182, + 0.020610226318240166, + -0.043392207473516464, + -0.02082117833197117, + -0.002737843431532383, + -0.0006089959060773253, + -0.0037808450870215893, + 0.012307620607316494, + -0.00048697233432903886, + 0.02522646263241768, + 0.004454454407095909, + -0.002267530420795083, + 0.033628448843955994, + -0.005047930404543877, + -0.033171236515045166, + -0.007750981952995062, + -0.022064339369535446, + 0.0011032270267605782, + -0.05667378008365631, + 0.0400712788105011, + -0.018579920753836632, + 0.003447598312050104, + 0.023964550346136093, + 0.029874781146645546, + 0.010787352919578552, + 0.0464145727455616, + -0.00018140094471164048, + 0.021335287019610405, + 0.03621942549943924, + 0.03164331242442131, + 0.007047041319310665, + 0.024161294102668762, + 0.0051458049565553665, + -0.02669116109609604, + -0.027444448322057724, + -0.00496227340772748, + -0.01039306353777647, + -0.007549087516963482, + 0.02429472841322422, + -0.008237326517701149, + -0.020497439429163933, + -0.01586436852812767, + -0.027521928772330284, + -0.09319742769002914, + -0.048859551548957825, + -0.007663758005946875, + -0.023486312478780746, + -0.00041053121094591916, + 0.026972508057951927, + -0.0010041504865512252, + -0.0157004501670599, + -0.020079554989933968, + -0.022509116679430008, + -0.008897910825908184, + -0.021139759570360184, + 0.013339338824152946, + 0.03333397954702377, + 0.015326232649385929, + -0.028042860329151154, + -0.05544659495353699, + -0.031078213825821877, + 0.011607030406594276, + -0.02181166224181652, + -0.00744574936106801, + 0.05782549828290939, + -0.016746319830417633, + 0.009471503086388111, + -0.003316384507343173, + -0.035465724766254425, + 0.05768056586384773, + -0.012004142627120018, + -0.014986960217356682, + 0.03720732778310776, + 0.02143043652176857, + 0.026075713336467743, + 0.015485046431422234, + 0.00331083289347589, + 0.00036578206345438957, + -0.01043119840323925, + 0.009521800093352795, + 0.003878184361383319, + 0.017995253205299377, + 0.05336521565914154, + -0.01376193854957819, + 0.010164910927414894, + -0.00801568292081356, + 0.023966368287801743, + -0.02204309031367302, + -0.01951799914240837, + -0.004580623935908079, + -0.007353594526648521, + 0.004211555700749159, + -0.007187132723629475, + 0.021527139469981194, + 0.004361414350569248, + 0.010818098671734333, + 0.031135402619838715, + -0.0186318289488554, + -0.010472129099071026, + -0.008804370649158955, + 0.038984186947345734, + -0.018582260236144066, + 0.006971979513764381, + -0.026357606053352356, + 0.03751755505800247, + 0.005514270160347223, + 0.05770224705338478, + 0.017626848071813583, + 0.018335195258259773, + 0.04369540885090828, + -0.008641945198178291, + 0.005750620272010565, + 0.08613482117652893, + 0.07864011079072952, + -0.005305185914039612, + 0.032157596200704575, + 0.014743405394256115, + -0.05273181200027466, + 0.029285039752721786, + 0.07465964555740356, + 0.03914283215999603, + 0.019027600064873695, + 0.017596468329429626, + -0.045072562992572784, + 0.009995588101446629, + -0.01110843475908041, + 0.00895054079592228, + 0.04809734597802162, + -0.003352812957018614, + -0.00011072283086832613, + 0.00457728561013937, + 0.014848344959318638, + -0.02248082496225834, + 0.008382567204535007, + -0.023109909147024155, + 0.005615995265543461, + -0.03022787719964981, + -0.011841406114399433, + -0.014284231700003147, + 0.015692271292209625, + -0.02613728865981102, + 0.0075065516866743565, + -0.028794744983315468, + 0.020227724686264992, + 0.05544402077794075, + -0.0011603679740801454, + -0.012777047231793404, + -0.0019513964653015137, + -0.01253324095159769, + -0.006144615821540356, + -0.0355195552110672, + 0.0043947892263531685, + 0.041877444833517075, + -0.012097779661417007, + -0.024319801479578018, + -0.005516470409929752, + 0.022453265264630318, + 0.012092233635485172, + -0.018631823360919952, + 0.004110769368708134, + 0.004368194378912449, + 0.010542603209614754, + -0.02452724426984787, + 0.007471759337931871, + 0.004462759010493755, + -0.007058088202029467, + -0.02097858488559723, + 0.026994572952389717, + -0.01731478050351143, + -0.013699651695787907, + -0.06810880452394485, + -0.003258147742599249, + -0.005500667728483677, + -0.012672522105276585, + 0.005896999966353178, + 0.03149664029479027, + 0.016226323321461678, + -0.006759614683687687, + 0.026323486119508743, + -0.00036873749922960997, + -0.0395895354449749, + -0.009028695523738861, + 0.05751972645521164, + 0.0209882203489542, + 0.020981181412935257, + -0.029480844736099243, + -0.007317953277379274, + -0.050578828901052475, + 0.00012342628906480968, + -0.00842003058642149, + -0.009423480369150639, + 0.030454136431217194, + 0.0012356776278465986, + -0.04846932739019394, + 0.022158678621053696, + -0.013994214124977589, + -0.008453655056655407, + -0.026119496673345566, + 0.002183667616918683, + 0.022440550848841667, + -0.030610263347625732, + -0.007750372868031263, + 0.06631051003932953, + -0.00824473425745964, + -0.0013906226959079504, + 0.02753399685025215, + 0.012078984640538692, + -0.015522884204983711, + 0.018594682216644287, + 0.0463859997689724, + 0.01884094998240471, + -0.049418799579143524, + -0.001487390953116119, + 0.01839595101773739, + 0.010682133957743645, + 0.08011963218450546, + -0.05727260187268257, + 0.04246359318494797, + -0.003195695811882615, + 0.0143673624843359, + 0.011099841445684433, + 0.04391103982925415, + 0.0036981625016778708, + 0.027839766815304756, + 0.024802904576063156, + -0.024362701922655106, + 0.00811538565903902, + 0.008220718242228031, + 0.031728439033031464, + 0.022544482722878456, + -0.029001522809267044, + -0.0733179971575737, + -0.021884683519601822, + -0.013320781290531158, + 0.06584840267896652, + 0.04199589788913727, + -0.009062287397682667, + 0.01451245415955782, + 0.004775299225002527, + 0.02503146603703499, + -0.0017211701488122344, + -0.024539373815059662, + -0.025105668231844902, + 0.0037928582169115543, + -0.014548735693097115, + -0.015957320109009743, + 0.04810233414173126, + -0.027347736060619354, + -0.05513868108391762, + -0.00015267710841726512, + -0.007838308811187744, + 0.0051926374435424805, + 0.0143553102388978, + 0.004393322858959436, + -0.033720217645168304, + 0.004871895536780357, + -0.045043762773275375, + 0.020454885438084602, + -0.010215746238827705, + 0.041811808943748474, + 0.00856564287096262, + 0.012836712412536144, + 0.016999786719679832, + -0.028676986694335938, + 0.01788490079343319, + 0.02263660356402397, + -0.021822340786457062, + 0.031773440539836884, + 0.00011908864689758047, + -0.02653655596077442, + 0.014309991151094437, + -0.025309862568974495, + -0.016876988112926483, + 0.014040700159966946, + 0.042401548475027084, + 0.006201902870088816, + 0.01920478045940399, + -0.00858673732727766, + -0.000752504332922399, + -0.008763100020587444, + -0.011957245878875256, + -0.027775391936302185, + 0.03061947412788868, + 0.02117232233285904, + 0.011400447227060795, + -0.04427286237478256, + 0.0063116904348134995, + -0.023160859942436218, + -0.0016754893586039543, + 0.006971938069909811, + -0.003005431964993477, + -0.03533594310283661, + -0.030157901346683502, + 0.037966515868902206, + -0.003658087458461523, + -0.03643280640244484, + -0.02461797185242176, + -0.018394727259874344, + -0.006387891247868538, + 0.01883014291524887, + -0.010561177507042885, + -0.01592956855893135, + 0.007240273058414459, + 0.00893967691808939, + 0.00817447155714035, + -0.009307540021836758, + -0.000180691436980851, + -0.012182447127997875, + 0.013882054015994072, + -0.0007283342420123518, + 0.030733667314052582, + -0.017924996092915535, + -0.003155083628371358, + -0.02134404145181179, + 0.02057349868118763, + -0.016535140573978424, + 0.009717334061861038, + -0.035321611911058426, + 0.021730216220021248, + 0.03770708292722702, + 0.003122395370155573, + 0.006734122987836599, + -0.004072919953614473, + 0.03469962626695633, + -0.012319761328399181, + -0.042083851993083954, + -0.02126132883131504, + -0.00846139807254076, + 0.0173293836414814, + 0.028772201389074326, + -0.01987832970917225, + 0.020904209464788437, + 0.01185655314475298, + -0.01662599854171276, + 8.965319466369692e-06, + -0.0006499029113911092, + 0.005172225646674633, + 0.01210333127528429, + 0.0316598005592823, + -0.013624005950987339, + -0.03041727840900421, + -0.0068354676477611065, + -0.10690348595380783, + -0.013686462305486202, + 0.007368266116827726, + 0.07012838125228882, + 0.028857100754976273, + -0.027713637799024582, + -0.045704614371061325, + -0.0037751111667603254, + 0.024900561198592186, + 0.03589540719985962, + -0.012865065596997738, + -0.011187703348696232, + 0.010488738305866718, + -0.027665071189403534, + -0.020254353061318398, + -0.04516737163066864, + -0.02388857677578926, + 0.007138604763895273, + -0.005479269195348024, + 0.0021866026800125837, + -0.016858316957950592, + -0.03538447991013527, + 0.013350137509405613, + -0.03802548348903656, + -0.0031675193458795547, + -0.0213316660374403, + -0.0034856311976909637, + -0.030803728848695755, + -0.010223610326647758, + 0.011720321141183376, + -0.043232183903455734, + -0.002200757386162877, + -0.030387338250875473, + -0.03327200189232826, + 0.001804622821509838, + 0.133257657289505, + -0.07145995646715164, + -0.00010006758384406567, + 0.014143972657620907, + 0.03155612200498581, + 0.00027115881675854325, + -0.0013089904095977545, + -0.00862295925617218, + 0.04045950621366501, + 0.03219681978225708, + 0.005045637954026461, + 0.047995056957006454, + 0.013339469209313393, + 0.009451907128095627, + -0.01407670322805643, + 0.02776471897959709, + -0.04560351371765137, + 0.038622014224529266, + 0.04649149999022484, + 0.005390972830355167, + -0.011090230196714401, + 0.022530898451805115, + -0.002944455947726965, + -0.010902962647378445, + -0.0036920146085321903, + 0.03390452265739441, + 0.006232519168406725, + -0.027645180001854897, + 0.04865559935569763, + 0.006606714800000191, + 0.01833975873887539, + -0.005053694825619459, + 0.027132248505949974, + 0.0010912693105638027, + -0.02378745563328266, + 0.017672061920166016, + -0.03454513102769852, + 0.034710656851530075, + 0.05326724052429199, + -0.011379681527614594, + -0.0038961239624768496, + -0.055364709347486496, + 0.03013015165925026, + 0.0050949458964169025, + -0.036113191395998, + 0.01792505383491516, + 0.03640623763203621, + -0.019061047583818436, + 0.03173552826046944, + -0.011304119601845741, + 0.006594215519726276, + 0.0009040216682478786, + 0.03219037130475044, + -0.029810475185513496, + -0.005289234220981598, + -0.021566757932305336, + -0.020201565697789192, + -0.028364701196551323, + -0.03963004797697067, + -0.03743515536189079, + 0.0009709704900160432, + -0.014810365624725819, + -0.0034124874509871006, + 0.01875241845846176, + -0.031185777857899666, + -0.03774070739746094, + -0.005431803874671459, + -0.015750717371702194, + 0.0557379387319088, + -0.04321564733982086, + 0.04165023937821388, + 0.058045849204063416, + -0.011787686496973038, + 0.026903973892331123, + 0.008081678301095963, + -0.036499984562397, + 0.3291316330432892, + 0.007087294477969408, + 0.03418394550681114, + 0.006193292792886496, + -0.012648221105337143, + 0.035153839737176895, + 0.009790368378162384, + 0.04038240388035774, + 0.003971776459366083, + 0.006641939282417297, + 0.001522514154203236, + -0.0022603704128414392, + 0.006364156026393175, + 0.016456661745905876, + 0.019805165007710457, + -0.00025375906261615455, + -0.013333062641322613, + -0.03182097151875496, + -0.04013547673821449, + 0.015477046370506287, + -0.03742130845785141, + 0.0004951651790179312, + 0.030869020149111748, + 0.023822015151381493, + 0.02760954201221466, + -0.0222511924803257, + -0.03666316717863083, + 0.03580308333039284, + 0.013095731846988201, + -0.021154288202524185, + 0.026679186150431633, + 0.040485091507434845, + -0.025690019130706787, + -0.006267736200243235, + 0.021163983270525932, + -0.005833519157022238, + 0.0011587785556912422, + 0.014611647464334965, + -0.011747763492166996, + 0.02077130228281021, + -0.046982698142528534, + 0.00858493521809578, + 0.006907902657985687, + 0.0185556598007679, + 0.0022377069108188152, + 0.00380428833886981, + -0.04350116848945618, + 0.014638874679803848, + 0.012997323647141457, + -0.04934140667319298, + 0.00411253934726119, + -0.0006067358190193772, + 0.0018191315466538072, + -0.022285746410489082, + 0.013051612302660942, + -0.024564724415540695, + -0.01260941382497549, + -0.0173653457313776, + 0.03843557834625244, + 0.0015222654910758138, + 0.027990497648715973, + 0.06925760954618454, + -0.005984327290207148, + 0.018118835985660553, + -0.022659096866846085, + -0.01438351720571518, + 0.024457549676299095, + 0.02154591865837574, + -0.031399887055158615, + 0.0426500104367733, + 0.0017170199425891042, + 0.035855669528245926, + 0.019374435767531395, + 0.012484557926654816, + -0.02369934692978859, + -0.031176511198282242, + -0.0060703326016664505, + -0.026621079072356224, + 0.022939322516322136, + 0.015607956796884537, + -0.008707663044333458, + -0.019681399688124657, + 0.042899250984191895, + 0.0013959152856841683, + -0.028189165517687798, + -0.09409601986408234, + 0.04154641926288605, + -0.01621236838400364, + 0.039989836513996124, + -0.007078574039041996, + 0.0005576875992119312, + 0.027672821655869484, + 0.029395459219813347, + 0.02846991829574108, + 0.002519391244277358, + 0.029595186933875084, + 0.07933732867240906, + 0.0123729994520545, + 0.01577797345817089, + 0.01965729147195816, + 0.003649025224149227, + -0.01413724385201931, + -0.0866907611489296, + -0.05757179856300354, + -0.006801299285143614, + 0.0037519806064665318, + 0.029673609882593155, + -0.010215532034635544, + -0.02522902563214302, + 0.04482772946357727, + -0.013827609829604626, + -0.012521802447736263, + 0.01818835362792015, + 0.03634721785783768, + 0.0159120075404644, + -0.013935456983745098, + -0.018886666744947433, + 0.045394398272037506, + 0.008666123263537884, + -0.016347303986549377, + -0.04969014972448349, + -0.0012570299440994859, + 0.00048599569709040225, + -0.006113376934081316, + -0.005923687946051359, + 0.008783435449004173, + 0.06595330685377121, + -0.05098467692732811, + -0.02819635346531868, + -0.00783533975481987, + 0.02677443064749241, + -0.030277537181973457, + -0.022862957790493965, + -0.014377163723111153, + 0.011482004076242447, + -0.017662694677710533, + -0.06124087795615196, + 0.016468992456793785, + -0.012421035207808018, + -0.014061876572668552, + 0.015157531946897507, + -0.007627006154507399, + 0.026947787031531334, + 0.016765303909778595, + 0.07291170954704285, + -0.0377034917473793, + 0.007322181947529316, + 0.010150049813091755, + 0.008556824177503586, + -0.004104339983314276, + -0.019883567467331886, + 0.0009354851790703833, + 0.04593205824494362, + 0.001781244995072484, + -0.1304609477519989, + -0.025364786386489868, + 0.0016889396356418729, + -0.0024631780106574297, + 0.02371254749596119, + 0.010473520494997501, + -0.04305413365364075, + -0.021383089944720268, + 0.04257691279053688, + -0.004939475562423468, + -9.597963799024001e-05, + 0.0018859859555959702, + 0.01987624168395996, + 0.02768769860267639, + -0.018472861498594284, + -0.023840077221393585, + -0.007859773933887482, + 0.05947216972708702, + 0.01701110601425171, + 0.03580983355641365, + -0.09342750906944275, + -0.011005472391843796, + 0.00967797264456749, + -0.03869393467903137, + -0.001522036618553102, + -0.03404369205236435, + -0.026313630864024162, + -0.005679016467183828, + -0.011243043467402458, + -0.020049739629030228, + -0.06370924413204193, + 0.024434708058834076, + 0.015167590230703354, + -0.017356026917696, + -0.01681535877287388, + 0.03601039573550224, + -0.013664702884852886, + -0.022697273641824722, + 0.0027690655551850796, + -0.04017261043190956, + -0.003727213479578495, + 0.017249224707484245, + 0.012136352248489857, + -0.011421870440244675, + -0.00259218318387866, + -0.034619539976119995, + 0.03672388941049576, + -0.00827953964471817, + 0.04988362640142441, + 0.023023830726742744, + 0.035931579768657684, + -0.04348704218864441, + 0.05230642855167389, + -0.027122080326080322, + 0.02346341498196125, + 0.0035161313135176897, + 0.03402484208345413, + -0.027698615565896034, + 0.03231494501233101, + -0.038205016404390335, + -0.05646304041147232, + -0.02725435048341751, + -0.04216828942298889, + 0.002987552434206009, + 0.00277600041590631, + -0.02368459664285183, + 0.0292605459690094, + -0.020004162564873695, + -0.027155539020895958, + -0.016446659341454506, + 0.026298440992832184, + -0.0024479494895786047, + 0.026316460222005844, + 0.006907031871378422, + -0.024478290230035782, + -0.0035826361272484064, + -0.005618308205157518, + 0.008147415705025196, + -0.13708558678627014, + -0.026438215747475624, + -0.013173813000321388, + 0.014830844476819038, + 0.005770901218056679, + 0.018324270844459534, + -0.013159680180251598, + 0.002565005561336875, + 0.012892335653305054, + 0.06300216168165207, + -0.02512231096625328, + -0.0024181194603443146, + -0.1015968844294548, + -0.022356215864419937, + 0.02710459567606449, + -0.019052784889936447, + -0.025383321568369865, + 0.0028883388731628656, + -0.019903376698493958, + -0.029139695689082146, + 0.0014898208901286125, + 0.015088409185409546, + -0.02676120586693287, + -0.03300388157367706, + -0.03420499712228775, + 0.006534016225486994, + 0.006231397390365601, + -0.024784360080957413, + 0.023459862917661667, + -0.014854158274829388, + 0.02815832756459713, + -0.045918650925159454, + -0.036314163357019424, + -0.024837536737322807, + 0.018967218697071075, + 0.019847117364406586, + -0.03127732500433922, + 0.020959051325917244, + -0.020691823214292526, + 0.0011701913317665458, + 0.042644061148166656, + -0.005187626462429762, + 0.018471356481313705, + -0.007088168989866972, + 0.045014239847660065, + 0.04114348068833351, + -0.0056430865079164505, + -0.02893855795264244, + 0.028651107102632523, + 0.03604142740368843, + -0.0278923436999321, + -0.029798494651913643, + -0.0014420218067243695, + 0.02331109344959259, + -0.007324368227273226, + 0.005217229016125202, + 0.008608055301010609, + -0.016003165394067764, + 0.002263362519443035, + 0.0026951716281473637, + -0.022563280537724495, + 0.006696629803627729, + -0.001321129035204649, + -0.003536898409947753, + -0.07142043113708496, + -0.005508871283382177, + -0.29002636671066284, + 0.011134093627333641, + -0.02007598988711834, + 0.015605523250997066, + 0.02935725264251232, + 0.021638184785842896, + 0.023648466914892197, + -0.011153833009302616, + 0.02878279611468315, + 0.008552688173949718, + -0.01580663025379181, + 0.01615075208246708, + -0.010293019004166126, + -0.0005325222155079246, + 0.0036680158227682114, + -0.02820221520960331, + -0.05849284678697586, + 0.01686594821512699, + 0.0026474858168512583, + -0.03740735352039337, + 0.01269643846899271, + 0.004143435508012772, + 0.0011036429787054658, + -0.012850945815443993, + 0.0389273464679718, + 0.022800389677286148, + -0.030440187081694603, + -0.0099356509745121, + -0.0038986308500170708, + 0.016727512702345848, + -0.007901391945779324, + 0.012128198519349098, + 3.340172042953782e-05, + 0.022091658785939217, + 0.0031102136708796024, + 0.14124497771263123, + 0.016496555879712105, + -0.030081454664468765, + -0.033317700028419495, + -0.010294877924025059, + 0.007392433471977711, + -0.030991461127996445, + 0.011363434605300426, + 0.000787076773121953, + 0.04528233781456947, + -0.004042895045131445, + 0.003929607570171356, + 0.021753840148448944, + -0.030891839414834976, + 0.008093244396150112, + -0.010576410219073296, + -0.03800053894519806, + -0.014480950310826302, + 0.0012653199955821037, + -0.023412499576807022, + 0.015010868199169636, + -0.05397195369005203, + -0.018853861838579178, + -0.011677669361233711, + -0.038960665464401245, + -0.0206446573138237, + 0.015063580125570297, + 0.007933633401989937, + -0.02048766426742077, + -0.025853658095002174, + -0.01507315132766962, + 0.0068449173122644424, + -0.028235210105776787, + 0.009093067608773708, + 0.03312407433986664, + 0.03928187116980553, + 0.024684850126504898, + 0.008027438074350357, + 0.014631290920078754, + -0.018319953233003616, + -0.03446175530552864, + 0.027261661365628242, + 0.021440856158733368, + 0.01823660545051098, + -0.0011000604135915637, + -0.010481880977749825, + 0.029842909425497055, + 0.00663276482373476, + -0.03302101418375969, + 0.00869046151638031, + -0.012993873097002506, + 0.032400377094745636, + -0.023902548477053642, + 0.016524463891983032, + -0.014954266138374805, + 0.016376188024878502, + -0.009001698344945908, + 0.0013872520066797733, + -0.01563446782529354, + 0.04259336739778519, + -0.01636028103530407, + 0.027126256376504898, + 0.021439608186483383, + -0.036541473120450974, + -0.018487120047211647, + -0.015714187175035477, + -0.010763557627797127, + -0.0030594556592404842, + 0.04577641934156418, + 0.009201133623719215, + 0.0023992268834263086, + 0.02488527074456215, + 0.004446775186806917, + -0.001729969633743167, + 0.013659251853823662, + -0.015723351389169693, + -0.025369394570589066, + -0.022259492427110672, + -0.0616975873708725, + -0.12356645613908768, + 0.00032469749567098916, + 0.01927884854376316, + 0.0010244097793474793, + -0.013254866003990173, + -0.046788539737463, + 0.0353209525346756, + -0.020668625831604004, + 0.05392200127243996, + 0.012227986007928848, + 0.013972807675600052, + -0.025879614055156708, + -0.014261682517826557, + 0.046910036355257034, + 0.022773202508687973, + -0.021432194858789444, + -0.029247554019093513, + 0.002641901606693864, + -0.05472875386476517, + 0.03165946900844574, + 0.01777735911309719, + 0.026643384248018265, + -0.0036612190306186676, + -0.033252961933612823, + -0.02913052588701248, + 0.02685139887034893, + 0.01310968492180109, + 0.033311836421489716, + 0.011002023704349995, + 0.007192468270659447, + -0.015553736127912998, + -0.05244443938136101, + -0.006967964582145214, + 0.018643498420715332, + 0.006685522384941578, + -0.0020583791192620993, + -0.007721593137830496, + -0.01571636088192463, + -0.0314825139939785, + -0.014723719097673893, + -0.009628428146243095, + 0.02372767962515354, + -0.0025545156095176935, + 0.018474183976650238, + 0.0170663520693779, + -0.0453076995909214, + -0.013333948329091072, + -0.010815403424203396, + -0.01105255726724863, + 0.02509847842156887, + -0.02292001061141491, + 0.004261199850589037, + 0.043001946061849594, + -0.009463608264923096, + 0.0010573064209893346, + -0.06277351081371307, + 0.005590242333710194, + -0.030856583267450333, + 0.008856328204274178, + 0.03191614896059036, + 0.03261955827474594, + 0.03299246355891228, + -0.013751746155321598, + -0.012725804932415485, + -0.021607697010040283, + -0.008593536913394928, + -0.00551748089492321, + 0.013141285628080368, + 0.004716394003480673, + -0.03995538502931595, + -0.018093286082148552, + 0.0048091528005898, + 0.0017331234412267804, + 0.026262203231453896, + 0.00988792534917593, + 0.030077336356043816, + -0.001006538514047861, + -0.012305229902267456, + 0.05561097711324692, + -0.01801428757607937, + -0.004888317547738552, + -0.010445359162986279, + -0.01880705915391445, + 0.029394233599305153, + 0.004680701065808535, + -0.01790020428597927, + 0.022100351750850677, + 0.01031972374767065, + -0.03606348857283592, + 0.0006588859250769019, + 0.008825104683637619, + 0.0036487507168203592, + 0.0038208516780287027, + 0.025906618684530258, + -0.01562798209488392, + -0.018792657181620598, + 0.017516646534204483, + -0.054175034165382385, + -0.04201400279998779, + -0.0037108471151441336, + 0.01666123978793621, + -0.0049594007432460785, + 0.026161396875977516, + -0.019220639020204544, + -0.04168197885155678, + 0.010052165947854519, + 0.008739178068935871, + 0.02256884053349495, + -0.0017195763066411018, + 0.015820026397705078, + 0.030502814799547195, + 0.04251708835363388, + -0.005211654584854841, + -0.01986524648964405, + 0.023804210126399994, + 0.012978902086615562, + -0.030545314773917198, + 0.04386553540825844, + 0.01322642806917429, + -0.008794640190899372, + -0.0261642225086689, + -0.0029116501100361347, + -0.01910964958369732, + 0.013602974824607372, + -0.016435761004686356, + 0.04246348887681961, + 0.03191722556948662, + -0.002532098675146699, + 0.011628295294940472, + 0.007733588572591543, + -0.012056558392941952, + 0.04089832678437233, + 0.01453109085559845, + -0.04611733928322792, + -0.015184852294623852, + -0.006043344270437956, + 0.07319765537977219, + 0.05466412752866745, + -0.0036608362570405006, + -0.03835102170705795, + 0.009893464855849743, + 0.03928321599960327, + 0.00263407826423645, + -0.014992455951869488, + 0.012139874510467052, + -0.030705595389008522, + 0.0476648285984993, + -0.03014526702463627, + -0.01836489327251911, + -0.004555985331535339, + 0.008258629590272903, + -0.012033958919346333, + -0.003646592376753688, + 0.008295332081615925, + -0.001375445513986051 + ] + }, + { + "name": "2373556e776cad3ba4d6ee04fc34550b.png", + "file-path": "E:\\Pratham\\2025\\Harsh Sir\\Scratch Vision\\images\\sprites\\Centaur.sprite3\\2373556e776cad3ba4d6ee04fc34550b.png", + "embeddings": [ + -0.02040322870016098, + -0.011779353022575378, + -0.026042019948363304, + -0.015030301176011562, + 0.006419997662305832, + -0.007156440522521734, + -0.0036694721784442663, + -0.011446668766438961, + -0.020913857966661453, + -0.005027877166867256, + 0.07352589070796967, + -0.021105218678712845, + 0.052903395146131516, + -0.0016353352693840861, + 0.04982427507638931, + -0.014670618809759617, + 0.0015634790761396289, + -0.0022955506574362516, + -0.050844043493270874, + -0.018875928595662117, + -0.09929130226373672, + -0.00954428967088461, + 0.0049812402576208115, + 0.01909489370882511, + -0.025362752377986908, + -0.02081240527331829, + 0.003916037268936634, + 0.003156543942168355, + -0.036535248160362244, + -0.010294781066477299, + -0.02889120578765869, + 0.012439662590622902, + 0.011013253591954708, + 0.025323498994112015, + 0.03310198336839676, + -0.002559324260801077, + -0.0005566620966419578, + 0.017538834363222122, + -0.03977232426404953, + 0.0045003946870565414, + 0.025789899751544, + -0.01237522903829813, + 0.013843918219208717, + 0.0004267070326022804, + -0.024775411933660507, + -0.038985591381788254, + -0.02702009305357933, + -0.12318912893533707, + -0.03338748589158058, + 0.022083543241024017, + 0.004657786805182695, + 0.01853891834616661, + -0.005776570178568363, + 0.019740235060453415, + 0.008435922674834728, + -0.0034486635122448206, + 0.017431078478693962, + -0.014991696923971176, + -0.019776709377765656, + -0.03360338136553764, + 0.04113442823290825, + -0.020290544256567955, + -0.02487938478589058, + 0.0037705237045884132, + -0.007555087562650442, + 0.008619977161288261, + -0.030342919752001762, + -0.03424571081995964, + 0.018969660624861717, + 0.0199174452573061, + 0.013886651024222374, + -0.015399263240396976, + -0.010508819483220577, + -0.025636618956923485, + -0.015509084798395634, + -0.0061296275816857815, + -0.03181342035531998, + 0.04121096059679985, + -0.013616866432130337, + 0.03601327911019325, + -0.001630915328860283, + -0.03130805119872093, + 0.01207235548645258, + 0.024623844772577286, + -0.003939715214073658, + 0.020835446193814278, + 0.004643104504793882, + -0.029711473733186722, + -0.008795657195150852, + -0.03755544498562813, + 0.01641388237476349, + 0.036244891583919525, + 0.006875028368085623, + 0.01576322317123413, + -0.008614132180809975, + -0.02498878538608551, + -0.02277626469731331, + -0.01195400021970272, + -0.0048058186657726765, + -0.021660052239894867, + 0.013392184861004353, + -0.004562141373753548, + 0.04340604320168495, + 0.026732226833701134, + 0.0557609423995018, + -0.029000800102949142, + 0.024686267599463463, + -0.007523071952164173, + 0.03417843580245972, + 0.0016186283901333809, + 0.022180354222655296, + 0.02114676870405674, + 0.008984224870800972, + 0.029468487948179245, + 0.011776325292885303, + 0.030080324038863182, + -0.007628134451806545, + -0.009907660074532032, + 0.0018028202466666698, + -0.006972756702452898, + 0.02114793471992016, + 0.015212959609925747, + -0.04134838283061981, + -0.008327450603246689, + 0.07527995854616165, + 0.020533936098217964, + 0.04179998114705086, + 0.08574199676513672, + -0.02372080273926258, + -0.022428471595048904, + -0.016816280782222748, + 0.024793606251478195, + 0.016339784488081932, + 0.027566784992814064, + 0.025411691516637802, + 0.037019532173871994, + 0.002934520598500967, + 0.014388528652489185, + -0.021829580888152122, + 0.02746635675430298, + -0.007053676061332226, + -0.042710814625024796, + -0.008294575847685337, + 0.003449293551966548, + 0.04185746982693672, + -0.010235890746116638, + 0.0207089614123106, + -0.00498789269477129, + -0.01658124476671219, + 0.018121130764484406, + 0.03833554685115814, + -0.0017904300475493073, + -0.030101431533694267, + 0.014028279110789299, + 0.0029930900782346725, + 0.0017367486143484712, + 0.008350864984095097, + 0.005850798916071653, + 0.0031978501938283443, + 0.021204357966780663, + 0.010410802438855171, + 0.021230638027191162, + -0.03302321583032608, + 0.022121760994195938, + -0.0020669950172305107, + 0.015144088305532932, + -0.03121102973818779, + -0.036560457199811935, + -0.02612433210015297, + -0.05994890630245209, + 0.0016454167198389769, + 0.00540504464879632, + 0.015746871009469032, + -0.04800901561975479, + -0.022148530930280685, + -0.014198699034750462, + -0.00025170930894091725, + 0.0063296109437942505, + -0.014766600914299488, + -0.01805000752210617, + -0.0019230040488764644, + -0.022252846509218216, + 0.024424772709608078, + -0.009245113469660282, + -0.0032790994737297297, + 0.00833715870976448, + -0.03608478605747223, + -0.007985266856849194, + -0.011300981976091862, + 0.016940325498580933, + 0.024649573490023613, + 0.027625244110822678, + -0.009207750670611858, + 0.04052404686808586, + 0.04516449570655823, + 0.010728328488767147, + 0.01920684240758419, + -0.025457996875047684, + -0.0041138106025755405, + 0.04038482531905174, + -0.013934919610619545, + 0.023581048473715782, + 0.024570593610405922, + -0.023282071575522423, + -0.0048602973110973835, + -0.026135798543691635, + -0.0002542799338698387, + -0.026331471279263496, + 0.029702013358473778, + -0.02660532295703888, + -0.011839376762509346, + -0.05302191153168678, + -0.018574308604002, + 0.09587732702493668, + 0.0044633676297962666, + 0.009340131655335426, + 0.01993877999484539, + 0.023083431646227837, + -0.043346378952264786, + 0.03915983811020851, + -0.041681427508592606, + 0.018879514187574387, + 0.004796542692929506, + -0.01716797798871994, + 0.06355898082256317, + -0.0027443617582321167, + -0.037621427327394485, + -0.01932782679796219, + -0.07075297832489014, + -0.04871203005313873, + 0.025871334597468376, + 0.016645392403006554, + -0.005391391925513744, + 0.005098348017781973, + 0.005516975652426481, + -0.05780743435025215, + 0.024551184847950935, + -0.013409141451120377, + -0.00923842005431652, + -0.013616540469229221, + 0.000729229359421879, + -0.0007649092003703117, + -0.012409422546625137, + 0.03273510932922363, + -0.0026862253434956074, + -0.008963070809841156, + 0.012499509379267693, + -0.003516562981531024, + 0.012888478115200996, + 0.003892210079357028, + 0.08193135261535645, + 0.06853079795837402, + -0.002249831100925803, + 0.0006675170734524727, + -0.007413538172841072, + 0.0009291364694945514, + -0.017004596069455147, + -0.007093456573784351, + 0.013931706547737122, + 0.04687671735882759, + 0.011075465008616447, + 0.018003977835178375, + -0.05028803274035454, + 0.020922483876347542, + -0.0116731571033597, + 0.001587051316164434, + -0.04092801734805107, + -0.019292563199996948, + 0.020121462643146515, + -0.027741415426135063, + 0.009468069300055504, + 0.04837178438901901, + -0.03851111978292465, + -0.003542449325323105, + -0.0010258202673867345, + -0.021054107695817947, + -0.020139502361416817, + -0.010828414000570774, + -0.0016298230038955808, + 0.024053649976849556, + 0.014238324947655201, + -0.019472382962703705, + -0.005419694818556309, + -0.029131058603525162, + 0.002694298978894949, + 0.016435867175459862, + -0.03255382552742958, + -0.011315807700157166, + -0.006091877352446318, + 0.010064320638775826, + 0.012884805910289288, + -0.009853392839431763, + 0.019380124285817146, + -0.029362738132476807, + 0.06479351967573166, + 0.00637975474819541, + 0.04077954590320587, + 0.03648192808032036, + -0.0296634528785944, + 0.02004002220928669, + -0.01426544040441513, + 0.018029090017080307, + 0.010707019828259945, + -0.00492175854742527, + -0.0350310355424881, + -0.008102829568088055, + -0.0216833483427763, + 0.04496943578124046, + 0.02841218188405037, + -0.021062159910798073, + 0.00478726252913475, + 0.03953965753316879, + 0.025068702176213264, + 0.002023280132561922, + 0.01537678949534893, + -0.012396937236189842, + 0.029557926580309868, + 0.01483600027859211, + 0.03117080219089985, + 0.013299457728862762, + -0.004945883061736822, + -0.03149307146668434, + -0.0051337359473109245, + -0.043869949877262115, + 0.03031029738485813, + 0.01883513666689396, + -0.009607811458408833, + -0.02157571166753769, + 0.008341116830706596, + 0.0009606581879779696, + -0.032357439398765564, + -0.004697002470493317, + -0.01503208838403225, + -0.018923668190836906, + -0.0006246254779398441, + 0.022167658433318138, + -0.006903757806867361, + 0.00026904657715931535, + 0.01479239109903574, + -0.0058302064426243305, + 0.04443185403943062, + -0.009263262152671814, + 0.04078178480267525, + -0.027606936171650887, + -0.013186892494559288, + -0.009032881818711758, + -0.11253103613853455, + -0.0037201326340436935, + 0.0026854791212826967, + 0.051507044583559036, + 0.06445091217756271, + 0.013609115965664387, + -0.021996017545461655, + -0.012593044899404049, + 0.050429247319698334, + -0.010908618569374084, + -0.00043103989446535707, + 0.031009845435619354, + -0.03511245176196098, + -0.06317605078220367, + -0.011689971201121807, + -0.06515280902385712, + 0.037120506167411804, + -0.060430314391851425, + -0.003503078129142523, + -0.054060183465480804, + -0.0056373924016952515, + -0.0339219830930233, + 0.016588309779763222, + -0.04153316840529442, + 0.03294036164879799, + 0.007165399380028248, + -0.026964347809553146, + -0.05733633413910866, + -0.011594763956964016, + -0.009339589625597, + 0.004724917002022266, + -0.039738815277814865, + -0.0159702580422163, + -0.0012171297566965222, + 0.01829410344362259, + 0.05604629963636398, + -0.045275747776031494, + 0.006742038764059544, + 0.011666650883853436, + 0.09841977804899216, + -0.00957927294075489, + 0.04001479223370552, + 0.02919100783765316, + 0.0029025888070464134, + -0.01635247841477394, + 0.010959132574498653, + 0.004885802511125803, + 0.0064320131205022335, + 0.01424854714423418, + -0.0005234726704657078, + -0.002216059947386384, + -0.01849869079887867, + 0.03233764320611954, + -0.041939627379179, + 0.01796739548444748, + -0.0075469231233000755, + -0.019259899854660034, + -0.005263150669634342, + -0.026803776621818542, + 0.0008099196711555123, + 0.03087284229695797, + 0.00674911355599761, + 0.012956351973116398, + -0.013434460386633873, + -0.031873445957899094, + -0.007360625080764294, + -0.007283997721970081, + 0.019360003992915154, + -0.02472146973013878, + -0.007624189835041761, + -0.02630472555756569, + 0.010991906747221947, + 0.004305047914385796, + 0.049565043300390244, + -0.007351458538323641, + 0.02810593880712986, + 0.013958591036498547, + -0.01024914626032114, + -0.017742808908224106, + -0.029715783894062042, + 0.01128528080880642, + -0.009409707970917225, + -0.009812815114855766, + -0.0009496307466179132, + 0.017695721238851547, + 0.0297545213252306, + 0.0033662598580121994, + 0.006770005449652672, + -0.06707356125116348, + -0.03723251074552536, + 0.003832438727840781, + 0.02718740701675415, + 0.03323225677013397, + -0.002119861077517271, + 0.013776380568742752, + 0.0024620722979307175, + -0.009194769896566868, + 0.004306454211473465, + 0.049668628722429276, + -0.021575648337602615, + -0.030927365645766258, + -0.025498241186141968, + -0.033011581748723984, + -0.005904610734432936, + -0.014468261040747166, + 0.01409752294421196, + 0.007503827568143606, + 0.006805282551795244, + -0.016051966696977615, + -0.005373565014451742, + -0.012152696028351784, + 0.2881563901901245, + 0.07887668907642365, + -9.060365846380591e-05, + 0.026971466839313507, + -0.004629587288945913, + -0.03899221494793892, + -0.008762761950492859, + -0.010424223728477955, + -0.04702020809054375, + -0.011295806616544724, + -0.020199034363031387, + -0.024715756997466087, + -0.011754110455513, + 0.005639164242893457, + 0.08981415629386902, + -0.007272153161466122, + 0.015171072445809841, + -0.0064344992861151695, + -0.058702126145362854, + 0.020541664212942123, + 0.029362648725509644, + -0.024913547560572624, + 0.02194419503211975, + 0.031635165214538574, + 0.02360408380627632, + 0.012745159678161144, + 0.005783027037978172, + -0.0024047880433499813, + -0.0053387973457574844, + 0.0013341403100639582, + 0.04295164346694946, + 0.06311783194541931, + -0.04103713855147362, + 0.0014959615655243397, + 0.006802363786846399, + -0.007457978557795286, + 0.005759845953434706, + 0.05409679561853409, + -0.0035845728125423193, + -0.02722923643887043, + -0.05144504830241203, + 0.026428207755088806, + -0.0265580415725708, + 0.028573384508490562, + -0.037466321140527725, + -0.000188450823770836, + -0.018164079636335373, + 0.012452426366508007, + 0.00019770582730416209, + -0.03249678760766983, + -0.041304685175418854, + -0.005491655319929123, + 0.05801688879728317, + 0.012262100353837013, + -0.026482505723834038, + -0.05229686200618744, + 0.017498832195997238, + -0.055683694779872894, + 0.0005440773675218225, + 0.012426324188709259, + 0.04643592983484268, + -0.018539605662226677, + 0.01776398904621601, + -0.02115151472389698, + -0.013711907900869846, + 0.02048197016119957, + 0.014885108917951584, + 0.0032693850807845592, + -0.0021520969457924366, + -0.016149025410413742, + -0.00011124555749120191, + 0.004531004466116428, + 0.02052767388522625, + 0.038023434579372406, + -0.02876165136694908, + -0.019882457330822945, + 0.026772022247314453, + 0.010506746359169483, + 0.020233476534485817, + -0.010711572133004665, + -0.006746758706867695, + -0.035562075674533844, + -0.032283008098602295, + 0.013435670174658298, + -0.03700455650687218, + -0.12743651866912842, + 0.0314926877617836, + -0.05511580780148506, + -0.019020944833755493, + 0.025219177827239037, + -0.016507361084222794, + -0.002151209395378828, + -0.008237983100116253, + -0.038139283657073975, + 0.013038055039942265, + 0.030574660748243332, + 0.13661085069179535, + 0.028007084503769875, + 0.031781021505594254, + 0.0256658922880888, + -0.01578819379210472, + -0.011208418756723404, + -0.011052479967474937, + -0.024352816864848137, + 0.005241728387773037, + -0.02860260382294655, + 0.02735300362110138, + -0.03866366297006607, + 0.02029889076948166, + 0.01832808181643486, + 0.0035452551674097776, + -0.021795222535729408, + 0.00346196792088449, + 0.009600168094038963, + -0.042419105768203735, + 0.020574666559696198, + 0.0060147070325911045, + -0.013353517279028893, + -0.021772250533103943, + 0.0007353860419243574, + -0.018510909751057625, + -0.031931810081005096, + -0.019367629662156105, + -0.030109714716672897, + -0.02342919446527958, + -0.010244220495223999, + 0.06387631595134735, + -0.03128013387322426, + -0.026227755472064018, + 0.02304415963590145, + 0.00792866200208664, + -0.01804346963763237, + 0.053866732865571976, + -0.03049534559249878, + 0.03164921700954437, + 0.010115942917764187, + -0.004882157780230045, + -0.0014247562503442168, + -0.024121766909956932, + -0.045329030603170395, + -0.023642726242542267, + 0.022808730602264404, + -0.009814511053264141, + 0.010274404659867287, + 0.031227003782987595, + -0.002212500898167491, + 0.008872995153069496, + 0.007906971499323845, + 0.008062259294092655, + 0.03891483694314957, + 0.006211965810507536, + 0.02088572271168232, + 0.027674153447151184, + 0.013534207828342915, + -0.012374448589980602, + -0.0005131279467605054, + 0.05027543753385544, + 0.00509762205183506, + -0.02280377224087715, + 0.005234390497207642, + -0.036159124225378036, + -0.028929974883794785, + 0.00541875371709466, + 0.026166928932070732, + 0.018499543890357018, + 0.008074621669948101, + -0.04828250780701637, + 0.010536515153944492, + -0.02951975166797638, + -0.14123313128948212, + -0.03202543780207634, + -0.01927781105041504, + -0.008990037254989147, + 0.019336646422743797, + 0.009308786131441593, + 0.011607983149588108, + -0.008150543086230755, + -0.022360559552907944, + -0.03195969760417938, + 0.034364692866802216, + -0.029166031628847122, + 0.04244419187307358, + 0.008165200240910053, + -0.009263196028769016, + 0.01103781908750534, + -0.02518920786678791, + -0.006250094622373581, + -0.007131472229957581, + -0.01813921518623829, + 0.05701287090778351, + 0.012934373691678047, + -0.05680515617132187, + 0.014466790482401848, + -0.033208202570676804, + -0.012590457685291767, + 0.023467665538191795, + 0.010388425551354885, + -0.00040045438800007105, + -0.020793547853827477, + 0.002691134810447693, + -0.01781783066689968, + -0.018044432625174522, + -0.011968089267611504, + -0.037653181701898575, + -0.023331521078944206, + -0.008203897625207901, + -0.02817484177649021, + -0.0035583695862442255, + 0.06279686093330383, + 0.01064272690564394, + -0.035200707614421844, + -0.011216254904866219, + 0.017183341085910797, + -0.0060703689232468605, + 0.015138957649469376, + 0.02808695286512375, + 0.00774177024140954, + -0.009633180685341358, + 0.05158788710832596, + -0.03589963912963867, + 0.0034264205023646355, + 0.026273615658283234, + -0.0005510112387128174, + 0.018015218898653984, + 0.038293905556201935, + -0.012757818214595318, + 0.009529180824756622, + -0.0038012589793652296, + 0.004826507531106472, + 0.057489898055791855, + 0.012157417833805084, + -0.012399115599691868, + -0.09416709095239639, + -0.020853182300925255, + -0.0340195894241333, + 0.024556223303079605, + 0.04616117477416992, + -0.02404194511473179, + -0.019535034894943237, + -0.0032578613609075546, + -0.0013244933215901256, + 0.005627864506095648, + 0.01825633831322193, + 0.036378439515829086, + -0.08275071531534195, + -0.05819538235664368, + 0.03789583593606949, + 0.03115813247859478, + -0.011655071750283241, + -0.02203832007944584, + -0.0659371167421341, + -0.005651067476719618, + 0.030045248568058014, + 0.009839142672717571, + -0.08190368860960007, + 0.004166670609265566, + -0.006367772817611694, + -0.017027638852596283, + 0.0070760236121714115, + -0.04476861283183098, + -0.0007299165590666234, + 0.0005759323248639703, + -0.0135417515411973, + 3.953903615183663e-06, + 0.006498381029814482, + -0.04686267673969269, + 0.058834124356508255, + 0.014131828211247921, + 0.04309327155351639, + -0.004104668274521828, + 0.011447013355791569, + -0.015771916136145592, + 0.03723001480102539, + -0.05024956539273262, + 0.0016771587543189526, + 0.020442774519324303, + 0.029943116009235382, + -0.026962237432599068, + 0.0026712447870522738, + -0.01595892757177353, + 0.002527944277971983, + -0.002573662670329213, + 0.006991742178797722, + -0.019394809380173683, + -0.010624141432344913, + 0.030350608751177788, + -0.007201896049082279, + -0.0495617538690567, + -0.01757756434381008, + 0.03574826940894127, + 0.058472588658332825, + -0.024904385209083557, + -0.014438584446907043, + 0.009046492166817188, + 0.0008322989451698959, + 0.03599182143807411, + -0.05979560688138008, + 0.007674768101423979, + -0.2585822343826294, + 0.01734151504933834, + 0.08340229094028473, + 0.01478484459221363, + -0.007445883937180042, + 0.010772173292934895, + 0.018121937289834023, + -0.038840457797050476, + 0.018656928092241287, + 0.03434866666793823, + 0.0064879292622208595, + 0.01151273213326931, + -0.013023336417973042, + 0.010205483995378017, + 0.06451355665922165, + -0.01871865801513195, + 0.0009584269719198346, + 0.02066168561577797, + 0.026015814393758774, + -0.0535600483417511, + -0.02694530040025711, + 0.05984343960881233, + -0.05219922214746475, + -0.013834848999977112, + -0.002832205267623067, + 0.0013530012220144272, + 0.00507719349116087, + 0.004082224331796169, + -0.013960053212940693, + -0.013861648738384247, + 0.029603643342852592, + 0.04707377031445503, + 0.019708529114723206, + 0.021777749061584473, + 0.0002469379105605185, + 0.034774623811244965, + -0.015401417389512062, + -0.03125453367829323, + 0.01432784553617239, + -0.038516778498888016, + 0.010579641908407211, + 0.020292211323976517, + -0.01832546293735504, + -0.012077967636287212, + -0.0001858751493273303, + -0.028516391292214394, + -0.003430349286645651, + -0.009581249207258224, + -0.01978292688727379, + -0.020898044109344482, + 0.007550987880676985, + -0.02065727487206459, + -0.0535818450152874, + -0.0062805968336761, + -0.028838783502578735, + -0.022711588069796562, + -0.047706201672554016, + 0.00022807104687672108, + -0.028494926169514656, + -0.04738543555140495, + 0.024518677964806557, + 0.023565690964460373, + -0.012655645608901978, + 0.03056023083627224, + 0.02047627791762352, + 0.007442548871040344, + 0.01998300477862358, + -0.0002072700735880062, + -0.0326068140566349, + 0.006852815859019756, + 0.05966593325138092, + -0.015067974105477333, + -0.029621858149766922, + -0.010330806486308575, + 0.013918097130954266, + -0.05854140222072601, + -0.01789749227464199, + 0.04599462449550629, + 0.020671984180808067, + 0.023059291765093803, + 0.029877636581659317, + 0.02567923441529274, + -0.03413612022995949, + -0.0014584753662347794, + 0.010338982567191124, + -0.0025666740257292986, + 0.009815478697419167, + -0.015606933273375034, + -0.018889907747507095, + -0.01989138498902321, + 0.024186046794056892, + 0.04824864864349365, + 0.04942148178815842, + -0.008085576817393303, + -0.022932210937142372, + 0.022431043907999992, + 0.032114241272211075, + 0.011884761042892933, + -0.03369845077395439, + 0.02075924538075924, + -0.019601328298449516, + 0.0028507451061159372, + 0.02875312976539135, + 0.034993018954992294, + 0.0046341316774487495, + 0.003849978093057871, + -0.021972553804516792, + 0.029689136892557144, + -0.017993886023759842, + 0.01885174959897995, + -0.020955124869942665, + -0.01189493015408516, + -0.0028902622871100903, + -0.011528149247169495, + -0.03619121387600899, + 0.006466348189860582, + 0.004310544114559889, + -0.0012659821659326553, + -0.0029800788033753633, + -0.041476424783468246, + 0.02482731267809868, + -0.04940095543861389, + 0.019519472494721413, + -0.047617483884096146, + 0.036753494292497635, + 0.0038803883362561464, + 0.029286473989486694, + 0.03409821167588234, + 0.0074598114006221294, + 0.007267788518220186, + -0.0035264973994344473, + 0.023228604346513748, + 0.00966121070086956, + 0.042136453092098236, + 0.051981378346681595, + 0.020403556525707245, + 0.05352180078625679, + -0.003502313047647476, + 0.011797036975622177, + 0.006426384672522545, + 0.02086455561220646, + 0.023627247661352158, + -0.014527961611747742, + -0.04504439979791641, + -0.042435891926288605, + 0.0134170176461339, + 0.004479496739804745, + 0.003974638879299164, + -0.021862156689167023, + 0.05811745300889015, + 0.021768616512417793, + 0.04254244640469551, + 0.005249790847301483, + 0.028611518442630768, + -0.008379785344004631, + 0.06502453982830048, + 0.029056817293167114, + 0.00986592099070549, + 0.0044156392104923725, + 0.0649297833442688, + -0.0401436910033226, + 0.028627250343561172, + -0.0006411317153833807, + -0.01122457068413496, + -0.002207246609032154, + -0.02677294984459877, + 0.04842854291200638, + 0.022328343242406845, + 0.03669353201985359, + -0.06049223616719246, + -0.0038892454467713833, + 0.008568587712943554, + 0.015795281156897545, + -0.011862102895975113, + -0.013596811331808567, + 0.02129245549440384, + -0.015290050767362118, + 0.02370292879641056, + -0.03207307681441307, + -0.02295273169875145, + 0.026172799989581108, + 0.048647984862327576, + -0.06500499695539474, + -0.018429484218358994, + -0.028017904609441757, + -9.351455810246989e-05, + -0.00751701183617115, + 0.019448479637503624, + -0.029102690517902374, + 0.01217736303806305, + -0.013790017925202847, + -0.002821924164891243, + 0.004444966558367014, + -0.025311393663287163, + 0.012052806094288826, + -0.018711328506469727, + -0.0005701354239135981, + -0.010409295558929443, + -0.040133267641067505, + 0.011306192725896835, + -0.011218227446079254, + 0.04228435456752777, + 0.08567925542593002, + 0.02047117054462433, + 0.010847886092960835, + -0.0320727601647377, + 0.009990941733121872, + -0.042289089411497116, + -0.003495533252134919, + -0.03127940371632576, + -0.002685330342501402, + -0.00852841604501009, + -0.028796032071113586, + 0.023202836513519287, + -0.0032926341518759727, + -0.0010384871857240796, + -0.036835044622421265, + 0.008949656039476395, + -0.004258713684976101, + 0.0020170300267636776, + -0.012750919908285141, + 0.018905986100435257, + -0.04570912569761276, + 0.053537238389253616, + -0.013625803403556347, + -0.01336485706269741, + 0.021644383668899536, + 0.018777210265398026, + 0.01741783507168293, + 0.0036562427412718534, + 0.04314912483096123, + -0.01913224160671234, + -0.03877461329102516, + 0.012941104359924793, + -0.03061842918395996, + 0.013240127824246883, + -0.00986413937062025, + -0.0032053659670054913, + -0.004914342425763607, + -0.0010060988133773208, + 0.005343350116163492, + 0.02048865146934986, + -0.010434799827635288, + -0.004700447432696819, + 0.0026804453227669, + 0.020229782909154892, + 0.016109174117445946, + -0.005530528724193573, + -0.08334147185087204, + 0.012557138688862324, + 0.0563800223171711, + -0.003688754513859749, + 0.027297671884298325, + -0.0643203854560852, + -0.026604607701301575, + 0.007622312288731337, + -0.0032815570011734962, + -0.008118266239762306, + -0.011945657432079315, + 0.01599787548184395, + -0.006956716999411583, + -0.02147419936954975, + -0.035729821771383286, + 0.02922646515071392, + 0.008324120193719864, + -0.0005295340670272708, + 0.018589671701192856, + 0.061220213770866394, + -0.02808997966349125 + ] + }, + { + "name": "bear_element.png", + "file-path": "E:\\Pratham\\2025\\Harsh Sir\\Scratch Vision\\images\\sprites\\Crab.sprite3\\bear_element.png", + "embeddings": [ + 0.02556098811328411, + 0.011502371169626713, + -0.0051629748195409775, + 0.0436253547668457, + -0.037908896803855896, + -0.0006200941279530525, + -0.01681610383093357, + 0.028769679367542267, + 0.005475977435708046, + -0.002976482966914773, + 0.018688129261136055, + -0.038391754031181335, + -0.0046108742244541645, + -0.0018085845513269305, + -0.02952691726386547, + 0.004313873592764139, + -0.0009137043380178511, + -0.006190458312630653, + -0.02822096087038517, + 0.009331317618489265, + 0.018335994333028793, + -0.027282292023301125, + 0.002795372623950243, + 0.026233838871121407, + -0.022285962477326393, + 0.021796727553009987, + 0.014581870287656784, + -0.0004203942371532321, + -0.015281791798770428, + 0.047347065061330795, + -0.023206163197755814, + -0.035298291593790054, + 0.0728083923459053, + 0.012285429984331131, + 0.01349678635597229, + -0.00966518186032772, + -0.007896743714809418, + -0.0027678736951202154, + 0.00693237129598856, + -0.004822486080229282, + 0.020102163776755333, + -0.018324121832847595, + -0.05230552330613136, + 0.02318648062646389, + -0.04821712151169777, + -0.03567837178707123, + -0.004599842242896557, + -0.08132287114858627, + -0.0530119352042675, + 0.01804574392735958, + -0.023135105147957802, + -0.015089661814272404, + -0.015570645220577717, + 0.027327675372362137, + 0.009841038845479488, + -0.01151880994439125, + 0.01146008912473917, + 0.018222689628601074, + -0.020414795726537704, + 0.0038252780213952065, + 0.013978620059788227, + -0.04778526723384857, + -0.02442742884159088, + -0.018525827676057816, + 0.0071939025074243546, + -0.04278504103422165, + -0.010746455751359463, + -0.03172704577445984, + -0.007718024775385857, + -0.006975787226110697, + -0.00987299531698227, + -0.00283073540776968, + -0.021184774115681648, + 0.021058013662695885, + -0.028264980763196945, + -0.03130811080336571, + -0.019586367532610893, + 0.016836993396282196, + -0.0235541220754385, + 0.03350731357932091, + -0.01646370254456997, + -0.0006473857210949063, + 0.029153672978281975, + 0.027986688539385796, + 0.028583304956555367, + 0.0009029775392264128, + 0.01566714607179165, + 0.01109684631228447, + -0.024841148406267166, + 0.02814912050962448, + 0.06967531144618988, + -0.04563752934336662, + -0.01487212534993887, + -0.01306851301342249, + -0.0363834910094738, + 0.0011218179715797305, + -0.05979827791452408, + -0.02443738840520382, + 0.025355124846100807, + -0.03857090696692467, + 0.03674735501408577, + -0.013067703694105148, + -0.008720171637833118, + 0.01643729954957962, + 0.048478733748197556, + -0.008044347167015076, + 0.009999512694776058, + -0.029333675280213356, + -0.00857921876013279, + -0.009160390123724937, + 0.06495407968759537, + -0.0237282644957304, + 0.03456752002239227, + 0.05268894135951996, + 0.047544170171022415, + 0.030094482004642487, + 0.005343287251889706, + 0.013388116843998432, + 0.0036987639032304287, + 0.03225847706198692, + 0.009361227974295616, + -0.05346684157848358, + 0.01952073909342289, + -0.02397874929010868, + 0.003521918784826994, + -0.0058708288706839085, + -0.009491952136158943, + 0.02388645149767399, + 0.042590513825416565, + -0.0007164630806073546, + 0.024187792092561722, + 0.018518149852752686, + 0.016604039818048477, + 0.019131191074848175, + -0.016852740198373795, + -0.0017955529037863016, + 0.001044194446876645, + 0.004207115154713392, + 0.013759464956820011, + -0.00924478005617857, + -0.010024898685514927, + -0.019918136298656464, + 0.012698685750365257, + 0.02228432334959507, + 0.0052351499907672405, + 0.008417652919888496, + -0.0024837099481374025, + 0.0025420216843485832, + -0.022030549123883247, + 0.01188267394900322, + 0.020830363035202026, + -0.02290644310414791, + -0.0150225181132555, + 0.020923975855112076, + -6.84217520756647e-05, + 0.03016756847500801, + 0.055606674402952194, + 0.03202379494905472, + -0.004693050403147936, + -0.0026580116245895624, + 0.016576025635004044, + -0.004202487412840128, + -0.05335667356848717, + 0.04908804967999458, + 0.0003053570108022541, + -0.029391096904873848, + -0.03715343028306961, + 0.004281850066035986, + -0.013745222240686417, + 0.016410615295171738, + -0.017451096326112747, + 0.005395356100052595, + 0.03109496273100376, + -0.003177183447405696, + -0.03657061234116554, + -0.03699575364589691, + 0.016251640394330025, + 0.0015795964282006025, + -0.03732867166399956, + -0.0005690358811989427, + -0.012173639610409737, + -0.03878533095121384, + 7.652193744434044e-05, + 0.0006338401581160724, + -0.04076721519231796, + 0.01887569949030876, + 0.027062539011240005, + 0.0062966179102659225, + -0.0019501791102811694, + -0.038907986134290695, + -0.02423950470983982, + 0.01808713749051094, + -0.017352135851979256, + 0.006847265176475048, + -0.013735783286392689, + 0.026038488373160362, + 0.018641192466020584, + -0.0045979502610862255, + 0.03356511890888214, + 0.027102531865239143, + -0.0005854255286976695, + -0.026422904804348946, + 0.009460767731070518, + -0.014049982652068138, + 0.005139847286045551, + -0.006422979291528463, + 0.02630705013871193, + 0.008197594434022903, + -0.009394073858857155, + -0.002444290090352297, + -0.03743884339928627, + 0.04615950956940651, + -0.01701219566166401, + -0.0566788911819458, + 0.013749335892498493, + 0.01036797184497118, + -0.0013363182079046965, + -0.004306148737668991, + 0.02677506022155285, + 0.04165072366595268, + -0.01787552237510681, + -0.0013531610602512956, + 0.00863900501281023, + -0.005789424758404493, + 0.020515916869044304, + 0.06747570633888245, + 0.021973196417093277, + -0.011082876473665237, + 0.026691287755966187, + 0.05015275999903679, + 0.040825698524713516, + 0.009167381562292576, + 0.005323716904968023, + 0.002930066082626581, + -0.01954825595021248, + 0.029585575684905052, + 0.010865533724427223, + 0.010687473230063915, + 0.013644304126501083, + -0.026616685092449188, + 0.007466684095561504, + -0.009147022850811481, + -0.008637762628495693, + 0.012152026407420635, + -0.024855617433786392, + -0.01615947298705578, + -0.003716186387464404, + 0.023722857236862183, + -0.041134778410196304, + -0.012405319139361382, + 0.02628844976425171, + -0.0019134010653942823, + 0.025985827669501305, + -0.04563719406723976, + -0.014632560312747955, + 0.011597873643040657, + 0.008586627431213856, + 0.004986768588423729, + -0.042119354009628296, + -0.0003519020392559469, + 0.012867670506238937, + 0.03192673623561859, + -0.06314748525619507, + -0.003608313389122486, + -0.0003025943005923182, + 0.03445766121149063, + -0.04680929705500603, + 0.0246609915047884, + -0.06660401821136475, + -0.010254819877445698, + -0.010444882325828075, + -0.011727736331522465, + 0.03758089244365692, + 0.008716955780982971, + -0.013606492429971695, + 0.010171628557145596, + 0.0097034377977252, + 0.010343124158680439, + -0.018833747133612633, + -0.006116850767284632, + -0.01129472628235817, + 0.008424071595072746, + -0.02906453236937523, + 0.014602027833461761, + -0.016981130465865135, + 0.014308469370007515, + -0.018449462950229645, + 0.02009984664618969, + -0.015298078767955303, + -0.02607552893459797, + 0.01477847807109356, + 0.019865229725837708, + 0.022893095389008522, + 0.018806250765919685, + 0.014412331394851208, + -0.009468324482440948, + -0.053464192897081375, + 0.08408714830875397, + -0.07719925791025162, + 0.01573217660188675, + -0.021988192573189735, + 0.005414909217506647, + 0.04149647802114487, + -0.0073148407973349094, + -0.007677739951759577, + 0.039213281124830246, + 0.020047303289175034, + 0.03374459594488144, + 0.03645540773868561, + 0.016381502151489258, + 0.005048627499490976, + 0.021418867632746696, + -0.01737847551703453, + 0.02009940892457962, + -0.023963311687111855, + 0.007128184195607901, + 0.012335286475718021, + 0.0017772010760381818, + 0.007209223695099354, + 0.014476930722594261, + -0.0045447032898664474, + -0.0554347038269043, + -0.04345432668924332, + -0.01063324324786663, + 0.007767289411276579, + 0.039030034095048904, + -0.10352420806884766, + -0.011881699785590172, + -0.0016605070559307933, + -0.018047885969281197, + -0.02096637524664402, + 0.015470478683710098, + 0.029731623828411102, + 0.01622934825718403, + 0.0030441274866461754, + 0.012824962846934795, + 0.0210123173892498, + 0.011934502050280571, + -0.015191853046417236, + 0.0003814706578850746, + -0.012170054018497467, + 0.060538195073604584, + 0.06079729273915291, + 0.022170795127749443, + -0.003424414899200201, + 0.02972349151968956, + -0.014171641319990158, + 0.0049892994575202465, + -0.02441447228193283, + 0.07497933506965637, + -0.010906252078711987, + -0.03725620359182358, + -0.014006007462739944, + -0.011540034785866737, + -0.00990314967930317, + 0.009955627843737602, + -0.013691228814423084, + 0.022928694263100624, + -0.0011762715876102448, + 0.025178801268339157, + -0.0237630195915699, + -0.07754138112068176, + -0.034499023109674454, + -0.001151945791207254, + -0.03186844661831856, + 0.017588883638381958, + 0.02550288662314415, + -0.0030305651016533375, + 0.025113288313150406, + -0.03053460828959942, + 0.04397403821349144, + 0.01023132260888815, + -0.007951589301228523, + -0.011964253149926662, + 0.04340824857354164, + -0.02024080790579319, + -0.012268639169633389, + 0.006978665944188833, + 0.007368641905486584, + -0.021912522614002228, + -0.014746938832104206, + 0.09214051067829132, + 0.02013055793941021, + 0.025131946429610252, + 0.00901895109564066, + 0.09570984542369843, + 0.018261879682540894, + -0.014862739481031895, + -0.0018930137157440186, + 0.07378973066806793, + 0.014173159375786781, + 0.02351781539618969, + -0.0035039247013628483, + 0.015095447190105915, + -0.0017294251592829823, + 0.018164103850722313, + 0.020192483440041542, + 0.006332744378596544, + 0.0426800511777401, + -0.019529996439814568, + 0.037746887654066086, + 0.02696172147989273, + 0.04093559458851814, + 0.0326605886220932, + -0.0015999035676941276, + -0.008941901847720146, + 0.05808928981423378, + 0.011855904012918472, + 0.017244858667254448, + -0.008210682310163975, + -0.012625934556126595, + 0.02672041393816471, + 0.02826070599257946, + 0.022666890174150467, + 0.029877793043851852, + 0.020890314131975174, + -0.053132377564907074, + 0.010047225281596184, + -0.03956146165728569, + 0.05005091056227684, + -0.01989956572651863, + 0.01145513541996479, + -0.030603844672441483, + 0.00481931259855628, + 0.018539225682616234, + 0.018952548503875732, + -0.003700512694194913, + 0.003494326723739505, + -0.04159507527947426, + -0.006070271600037813, + 0.010595745407044888, + 0.004969056695699692, + 0.015659984201192856, + 0.025144508108496666, + -0.03503162041306496, + 0.046372201293706894, + -0.010184367187321186, + -0.003739998210221529, + 0.031659942120313644, + 0.004786341916769743, + -0.05951942503452301, + 0.020329004153609276, + 0.02784733660519123, + -0.011810366995632648, + 0.03394698351621628, + 0.023875365033745766, + -0.041521284729242325, + 0.002396400086581707, + 0.009736046195030212, + 0.012265956029295921, + -0.020514726638793945, + 0.015785150229930878, + -0.0030328957363963127, + -0.0010627286974340677, + -0.03234894573688507, + -0.022299733012914658, + 0.0007513018208555877, + 0.33839792013168335, + 0.04370327666401863, + 0.0358358696103096, + -0.004671506118029356, + 0.011797970160841942, + 0.001084591494873166, + 0.018607864156365395, + 0.035413503646850586, + -0.01563408225774765, + -0.016765575855970383, + -0.016225213184952736, + 0.03072979487478733, + -0.00034671605681069195, + -0.005505079869180918, + 0.01310252957046032, + 0.03736795112490654, + -0.055046699941158295, + -0.03957564756274223, + -0.026588454842567444, + 0.03262913599610329, + -0.015517736785113811, + -0.01832914724946022, + 0.01894935593008995, + -0.010833368636667728, + 0.00993028748780489, + 0.002597394399344921, + 0.03565065562725067, + 0.01825602725148201, + 0.013700859621167183, + 0.05054479464888573, + 0.0035737664438784122, + 0.04104366898536682, + 0.014927166514098644, + 0.0042803543619811535, + -0.008595366030931473, + -0.03282317891716957, + -0.013741291128098965, + -0.042469050735235214, + 0.005518120247870684, + 0.017866352573037148, + -0.01806105487048626, + -0.013567459769546986, + 0.011069634929299355, + 0.0008447462460026145, + 1.0605792340356857e-05, + -0.013858203776180744, + -0.025854261592030525, + 0.01261363085359335, + -0.00358252483420074, + -0.02291952632367611, + -0.010519318282604218, + 0.004002823028713465, + 0.03094489313662052, + -0.06292951852083206, + -0.005772528238594532, + -0.052788618952035904, + 0.055644743144512177, + -0.003327433718368411, + 0.015571493655443192, + 0.02019667997956276, + -0.0011267857626080513, + -0.009074386209249496, + -0.02481171488761902, + -0.04169576242566109, + 0.014335793443024158, + 0.023687129840254784, + 0.019008694216609, + 0.01711670681834221, + -0.0011591978836804628, + -0.005924501921981573, + -0.02538609877228737, + 0.004982580430805683, + -0.06431341171264648, + -0.018800079822540283, + -0.0030224069487303495, + 0.008173895999789238, + 0.03317354992032051, + -0.0189686082303524, + 0.022656980901956558, + 0.02595754712820053, + -0.023872768506407738, + -0.009319940581917763, + 0.009733899496495724, + -0.014380908571183681, + -0.00854206457734108, + -0.10176932066679001, + 0.03010736033320427, + -0.011209527961909771, + -0.0019406367791816592, + 0.03150976449251175, + -0.009267066605389118, + 0.046537142246961594, + 0.01171769667416811, + 0.035693924874067307, + 0.026990342885255814, + -0.0119371572509408, + -0.020532650873064995, + -0.0024205863010138273, + -0.01073269359767437, + 0.026546049863100052, + -0.00444863410666585, + 0.0012356092920526862, + -0.004021095111966133, + -0.02118581347167492, + 0.007762129884213209, + -0.04272783547639847, + 0.012143013998866081, + -0.04120420292019844, + -0.027556223794817924, + 0.004636908881366253, + -0.002121320692822337, + -0.03000183403491974, + -0.03229709342122078, + 0.016247354447841644, + -0.0015324353007599711, + -0.022482603788375854, + -0.01572534814476967, + 0.02641770802438259, + -0.001564330654218793, + -0.009903084486722946, + -0.02509685419499874, + -0.030552444979548454, + -0.026655375957489014, + -0.021161751821637154, + -0.051747068762779236, + -0.07460848242044449, + 0.07214924693107605, + -0.024966837838292122, + -0.0556822307407856, + 0.006571745965629816, + 0.03712422400712967, + 0.014684172347187996, + -0.02746608853340149, + -0.034638356417417526, + 0.06247363239526749, + 0.006444902159273624, + -0.039715416729450226, + 0.019662093371152878, + 0.014374813996255398, + 0.00383274769410491, + 0.03279615566134453, + 0.02849326655268669, + -0.01370356883853674, + 0.03240194916725159, + 0.005666728131473064, + 0.026619410142302513, + 0.01650313474237919, + 0.01849249191582203, + -0.011891629546880722, + 4.0093018469633535e-05, + 0.0284720491617918, + 0.012318932451307774, + 0.018438493832945824, + -0.025380996987223625, + -0.14681553840637207, + -0.032614730298519135, + 0.029338570311665535, + 0.015632329508662224, + 0.023794449865818024, + 0.014280767180025578, + -0.025009362027049065, + -0.058914683759212494, + 0.03184060379862785, + 0.03593733161687851, + -0.0013810812961310148, + -0.0008066491573117673, + 0.019001604989171028, + -0.01909794472157955, + -0.009606725536286831, + -0.07175978273153305, + -0.04340861365199089, + 0.04108563810586929, + 0.01444215141236782, + 0.009612729772925377, + -0.12252438813447952, + 0.008485118858516216, + -0.022645819932222366, + -0.012594773434102535, + -0.015165764838457108, + 0.009748097509145737, + 0.021659240126609802, + -0.03295842558145523, + 0.005637080874294043, + 0.0024120465386658907, + -0.010912865400314331, + 0.035955093801021576, + 0.0034177934285253286, + -0.01618375815451145, + 0.05438176542520523, + 0.02277355268597603, + 0.011102888733148575, + -0.01864372380077839, + 0.03576132282614708, + -0.021623432636260986, + 0.004671172704547644, + -0.006061000749468803, + 0.0031361838337033987, + -0.012650002725422382, + -0.030599897727370262, + 0.00487810792401433, + -0.006993528921157122, + 0.01579160988330841, + 0.012186902575194836, + 0.010452386923134327, + 0.004145363345742226, + 0.00041520578088238835, + 0.023249607533216476, + -0.025592779740691185, + -0.0068407077342271805, + -0.0029244625475257635, + -0.011556916870176792, + 0.004322821740061045, + 0.02901916764676571, + -0.0019571774173527956, + -0.040112294256687164, + -0.011689530685544014, + -0.01968275010585785, + -0.02646758034825325, + 0.06057392805814743, + 0.012607998214662075, + -0.010348107665777206, + -0.020521942526102066, + 0.019296513870358467, + -0.002709656488150358, + 0.04539391025900841, + 0.01391286589205265, + 0.029106037691235542, + 0.027159523218870163, + -0.01763160713016987, + -0.019114455208182335, + -0.007151790428906679, + -0.028746820986270905, + -0.04965644329786301, + -0.017029836773872375, + -0.0740097314119339, + -0.00583773385733366, + -0.029605921357870102, + -0.01724756695330143, + 0.02067277394235134, + -0.01736585423350334, + 0.011088849045336246, + -0.027176085859537125, + 0.022956490516662598, + 0.026426728814840317, + -0.09324867278337479, + -0.03764476254582405, + 0.005499219987541437, + -0.011925498954951763, + 0.016730211675167084, + -0.0024502724409103394, + -0.006880265660583973, + -0.02218540571630001, + -0.004207944497466087, + 0.0045911394990980625, + 0.009395943023264408, + -0.011441650800406933, + 0.007085580378770828, + -0.010823924094438553, + -0.030743137001991272, + 0.02988821268081665, + 0.020800642669200897, + -0.010801350697875023, + -0.022052668035030365, + -0.027421902865171432, + 0.001214876538142562, + -0.024305114522576332, + 0.015629952773451805, + -0.015336151234805584, + -0.014743881300091743, + -0.045346252620220184, + 0.004957849625498056, + 0.012024608440697193, + -0.0021407834719866514, + 0.008446263149380684, + 0.003582902019843459, + 0.033741094172000885, + 0.02181413024663925, + 0.01834653690457344, + -0.0038602249696850777, + -0.01562584936618805, + -0.016306517645716667, + 0.030650828033685684, + -0.012253272347152233, + -0.062437787652015686, + 0.003201731014996767, + 0.0095592699944973, + -0.003427974646911025, + 0.0007207119488157332, + -0.019774656742811203, + -0.0061795674264431, + -0.015887323766946793, + -0.013433593325316906, + -0.009615356102585793, + 0.01139331515878439, + 0.02053152397274971, + 0.02644341252744198, + -0.06730831414461136, + 0.03404027223587036, + -0.3004054129123688, + 0.018412010744214058, + 0.04023841768503189, + 8.658505248604342e-05, + 0.03488780930638313, + 0.01553675439208746, + 0.025122782215476036, + 0.012213564477860928, + 0.03735355660319328, + -0.014150695875287056, + -0.021764278411865234, + -0.025458641350269318, + -0.07593899220228195, + 0.016107192263007164, + -0.019087007269263268, + 0.021462908014655113, + -0.03697432577610016, + 0.026711879298090935, + 0.019081152975559235, + -0.03540521860122681, + -0.02078273519873619, + 0.03265736252069473, + -0.012272155843675137, + -0.015457313507795334, + 0.06280294805765152, + 0.017310917377471924, + -0.016644079238176346, + -0.029893944039940834, + -0.0459936261177063, + 0.005250537768006325, + 0.023150784894824028, + 0.03811965137720108, + -0.012358272448182106, + 0.010889274068176746, + -0.001931625884026289, + 0.16249865293502808, + -6.0035068599972874e-05, + 0.007993204519152641, + -0.023996366187930107, + 0.002588556380942464, + 0.010467162355780602, + 0.014630530960857868, + 0.009298831224441528, + -0.039454128593206406, + -0.015119518153369427, + -0.02015094645321369, + 0.0012273195898160338, + 0.008522264659404755, + 0.01181861013174057, + 0.005992530379444361, + -0.019784647971391678, + -0.005101362243294716, + -0.03560056537389755, + -0.0032793220598250628, + -0.01633816957473755, + -0.04869871214032173, + -0.012965206056833267, + 0.05511541664600372, + 0.025178909301757812, + -0.03243374451994896, + -0.002722375327721238, + 0.01197644229978323, + 0.006182772107422352, + 0.01552958320826292, + -0.017986426129937172, + 0.04686801880598068, + 0.03633829578757286, + -0.004349411465227604, + -0.011057820171117783, + 0.027299940586090088, + 0.029868407174944878, + 0.037460699677467346, + 0.01858469657599926, + -0.013089598156511784, + 0.011516757309436798, + 0.021066952496767044, + 0.013102879747748375, + -0.009006853215396404, + -0.012652488425374031, + 0.004545167088508606, + -0.02205120027065277, + -0.008039355278015137, + -0.0003732381737791002, + -0.026176944375038147, + 0.016224278137087822, + -0.01005128026008606, + -0.009962505660951138, + -0.037076063454151154, + 0.020961912348866463, + -0.050711099058389664, + -0.003153823083266616, + 0.0365004725754261, + 0.004901927895843983, + 0.007920121774077415, + -0.0033220676705241203, + 0.0021113003604114056, + 0.019208408892154694, + 0.021710403263568878, + -0.031088704243302345, + 0.007431652396917343, + 0.007486969698220491, + -0.017206424847245216, + 0.011468335054814816, + 0.024906085804104805, + 0.024825982749462128, + 0.011985770426690578, + -0.0010131846647709608, + 0.03312094137072563, + -0.010943551547825336, + 0.010625344701111317, + 0.034397296607494354, + -0.044644828885793686, + -0.00967247411608696, + -0.03393249958753586, + -0.05569672957062721, + -0.016479913145303726, + -0.010977680794894695, + -0.06970995664596558, + 0.0013448488898575306, + -0.05104810371994972, + 0.038260191679000854, + -0.0145398685708642, + 0.058656059205532074, + -0.007685782853513956, + 0.03490374982357025, + -0.0040846276096999645, + -0.0016399119049310684, + 0.02146771363914013, + 0.023893939331173897, + -0.00410643732175231, + 0.01252350490540266, + -0.008455811999738216, + 0.016738610342144966, + -0.00036928721237927675, + -0.00043851867667399347, + -0.01646268740296364, + -0.005515075288712978, + -0.0765024721622467, + 0.02326352894306183, + 0.022658783942461014, + 0.014867276884615421, + 0.03913738206028938, + 0.030725786462426186, + -0.000482511764857918, + -0.05763225257396698, + 0.040818504989147186, + 0.01918095164000988, + -0.014762180857360363, + 0.002011151984333992, + 0.03694191575050354, + -0.0012088812654837966, + 0.049744863063097, + 0.007292449474334717, + 0.004491650965064764, + -0.017969327047467232, + 0.0076468391343951225, + 0.020202361047267914, + 0.0018431841162964702, + 0.029126323759555817, + 0.012344717979431152, + -0.012504013255238533, + 0.01760978437960148, + -0.020517272874712944, + -0.00010747658234322444, + -0.01429225318133831, + -0.04913844168186188, + 0.034866735339164734, + -0.016653072088956833, + -0.021548470482230186, + 0.01914951018989086, + 0.013076833449304104, + 0.007256609853357077, + 0.011124898679554462, + 0.005895714741200209, + -0.02434697560966015, + -0.024845469743013382, + 0.014076869934797287, + -0.013264154084026814, + -0.056927185505628586, + 0.007209464441984892, + -0.013416294939815998, + 0.04190579801797867, + 0.00044020035420544446, + -0.005143878515809774, + -0.03979010879993439, + -0.06126100942492485, + 0.016588466241955757, + -0.0313122384250164, + -0.009136521257460117, + -0.004294091835618019, + -0.003021061886101961, + 0.018886398524045944, + -0.004917880520224571, + -0.0014111530035734177, + 0.016326067969202995, + 0.021390387788414955, + -0.041905488818883896, + -0.012030942365527153, + -0.01062843482941389, + -0.007911494933068752, + 0.04076584801077843, + 0.03257002681493759, + 0.016181303188204765, + 0.00021097264834679663, + 0.006776575464755297, + -0.01160284411162138, + -0.002320521278306842, + 0.022530466318130493, + -0.008159728720784187, + 0.01934042200446129, + 0.02455785684287548, + -0.020830443128943443, + 0.008183738216757774, + -0.09026404470205307, + -0.01433985773473978, + 0.012667330913245678, + 0.0117403669282794, + -0.04911823198199272, + 0.01826341077685356, + 0.000796781328972429, + -0.010787801817059517, + 0.05987490713596344, + 0.016319461166858673, + -0.03277040272951126, + 0.01106359250843525, + 0.01275473553687334, + -0.016795439645648003, + -0.020730074495077133, + 0.029764140024781227, + 0.011570478789508343, + 0.029539717361330986, + -0.001661845250055194, + 0.016433153301477432, + -0.012581738643348217, + -0.04977693408727646, + -0.011503548361361027, + 0.0026925094425678253, + -0.030162760987877846, + -0.057611264288425446, + -0.015657765790820122, + 0.01173997949808836, + 0.02363578788936138, + -0.02542944625020027, + -0.02258356288075447, + -0.008709587156772614, + 0.034951917827129364, + 0.00873415544629097, + -0.002694376278668642, + 0.013016462326049805, + -0.02745683118700981, + 0.04602978378534317, + 0.028613023459911346, + 0.00026105076540261507, + -0.04380151629447937, + -0.005944014992564917, + -0.0005703370552510023, + -0.008362957276403904, + -0.013101952150464058, + 0.022517910227179527, + 0.043083082884550095, + -0.043835122138261795, + -0.05353529751300812, + -0.01479034498333931, + -0.012060445733368397, + -0.008817158639431, + -0.056969188153743744, + -0.030104657635092735, + 0.017950905486941338, + -0.021140070632100105 + ] + }, + { + "name": "cat_football.png", + "file-path": "E:\\Pratham\\2025\\Harsh Sir\\Scratch Vision\\images\\sprites\\Soccer Ball.sprite3\\cat_football.png", + "embeddings": [ + 0.027552904561161995, + 0.04228115826845169, + -0.015912968665361404, + 0.055726099759340286, + -0.010717897675931454, + 0.01905987039208412, + 0.008879976347088814, + 0.026690984144806862, + 0.006498878821730614, + 0.031851671636104584, + 0.017406141385436058, + 0.012747246772050858, + 0.014397366903722286, + 0.020986704155802727, + 0.05763053521513939, + -0.016582395881414413, + -0.01777981035411358, + -0.007884485647082329, + 0.00520381610840559, + 0.03698326274752617, + -0.017674962058663368, + -0.012376861646771431, + 0.006049258168786764, + -0.022250475361943245, + 0.012340103276073933, + 0.03453890606760979, + 0.03310056030750275, + -0.010253742337226868, + -0.024132223799824715, + 0.0167701318860054, + 0.008649093098938465, + -0.0074826558120548725, + 0.010282235220074654, + -0.04621496424078941, + -0.0053724744357168674, + -0.0080339927226305, + 0.01234759297221899, + -0.003895135596394539, + -0.01464095339179039, + -0.040239736437797546, + -0.010088757611811161, + 0.015116244554519653, + 0.025686750188469887, + 0.003206575056537986, + -0.04147814214229584, + -0.018756471574306488, + -0.013237890787422657, + -0.09076520055532455, + -0.01983840949833393, + 0.0017718819435685873, + -0.022072216495871544, + 0.017516003921628, + 0.040420953184366226, + -0.006281509529799223, + -0.003743659006431699, + 0.022543834522366524, + -0.016909107565879822, + -0.010464156046509743, + 0.0068640331737697124, + -0.0310200322419405, + -0.01088494062423706, + -0.025833314284682274, + 0.01576337404549122, + -0.014024465344846249, + -0.012194592505693436, + -0.049910757690668106, + -0.017485937103629112, + -0.03003958985209465, + -0.028955085203051567, + 0.028750913217663765, + -0.0005177436978556216, + 0.004262418951839209, + 0.02256138063967228, + 0.005248482339084148, + -0.011966957710683346, + -0.026893967762589455, + -0.007247792091220617, + -0.02357284165918827, + 0.006867900490760803, + 0.006952607538551092, + -0.007776686921715736, + -0.012299210764467716, + 0.0018693833844736218, + -0.015854906290769577, + 0.05747019872069359, + -0.002171682193875313, + 0.00995666068047285, + 0.01037540566176176, + 0.010107049718499184, + 0.006593016907572746, + 0.04406581446528435, + -0.0035787406377494335, + -0.02836907096207142, + 0.022315632551908493, + 0.03983810171484947, + 0.016370447352528572, + 0.0186148714274168, + -0.022288408130407333, + 0.035134050995111465, + 0.0021436482202261686, + 0.050791796296834946, + 0.007716351188719273, + -0.000251508696237579, + 0.021401377394795418, + 0.020180679857730865, + 0.003334137611091137, + 0.016674041748046875, + -0.004434908274561167, + 0.01593625918030739, + 0.01662895828485489, + 0.027635633945465088, + -0.019878126680850983, + 0.017235107719898224, + 0.06576623022556305, + 0.02962462790310383, + 0.008995088748633862, + 0.017103085294365883, + 0.015491392463445663, + -0.01018193457275629, + 0.021403709426522255, + 0.016908694058656693, + -0.029535358771681786, + -0.027512725442647934, + 0.03907071053981781, + -0.04301599785685539, + -0.02571575902402401, + -0.005950475111603737, + 0.07466322928667068, + 0.0016188909066841006, + 0.03709925338625908, + 0.00701744994148612, + -0.0017551532946527004, + -0.003383052535355091, + -0.013024364598095417, + -0.058270689100027084, + 0.014030133374035358, + 0.0013036398449912667, + 0.005579751916229725, + -0.016336245462298393, + -0.002801035763695836, + -0.005776356440037489, + -0.0006769913015887141, + 0.00532467570155859, + 0.046886902302503586, + 0.013660911470651627, + -0.019879497587680817, + -0.008239274844527245, + -0.009921100921928883, + 0.008360699750483036, + 0.04060353338718414, + 0.009748955257236958, + -0.006680669263005257, + -0.02918809838593006, + 0.01994929648935795, + -0.033375419676303864, + 0.017744628712534904, + 0.027140771970152855, + -0.06306811422109604, + 0.009605936706066132, + -0.07262156903743744, + 0.0032479276414960623, + -0.0050205993466079235, + -0.00341393263079226, + -0.0029673415701836348, + -0.007301546633243561, + -0.026046300306916237, + -0.008755018003284931, + 0.0017687097424641252, + -0.014704925008118153, + -0.05362281948328018, + 0.002822400536388159, + 0.010012971237301826, + 0.014811373315751553, + -0.020492035895586014, + -0.052866093814373016, + -0.0408250093460083, + -0.010231299325823784, + -0.02478749305009842, + -0.009721707552671432, + 0.009262165986001492, + -0.00435582036152482, + -0.049824491143226624, + -0.027250805869698524, + 0.0023931909818202257, + -0.013977847062051296, + 0.029773956164717674, + 0.06755274534225464, + 0.00795579981058836, + 0.04071102663874626, + 0.02511080726981163, + 0.0030003890860825777, + -0.006364047527313232, + -0.04491167142987251, + -0.008823913522064686, + -0.04070238023996353, + 0.0034796572290360928, + 0.010189585387706757, + 0.008389236405491829, + 0.002492183819413185, + 0.02633788250386715, + 0.026231929659843445, + 0.013924089260399342, + -0.02800663374364376, + 0.012165485881268978, + 0.005440485198050737, + 0.02608257718384266, + -0.023549025878310204, + 0.00723537802696228, + -0.0275199543684721, + 0.015515136532485485, + -0.00567832263186574, + -0.05041063576936722, + 0.0199813824146986, + -0.026608649641275406, + 0.006404248531907797, + 0.029196200892329216, + -0.014187507331371307, + 0.041149042546749115, + 0.006374345161020756, + 0.056268155574798584, + -0.08639305084943771, + 0.0854564681649208, + 0.04214353486895561, + 0.02182893641293049, + 0.030333420261740685, + 0.037522125989198685, + -0.0017661324236541986, + -0.007241787388920784, + 0.053315192461013794, + -0.007382330019026995, + 0.037680596113204956, + 0.022466937080025673, + 0.03427852690219879, + -0.014810847118496895, + -0.06193101406097412, + -0.012975726276636124, + 0.004911715164780617, + 0.02093837596476078, + -0.034969475120306015, + 0.017994090914726257, + 0.008386011235415936, + -0.024606967344880104, + 0.014575483277440071, + 0.01356284599751234, + -0.034544993191957474, + -0.020191621035337448, + -0.0003592752036638558, + 0.02376493066549301, + 0.023084772750735283, + -0.007669920567423105, + -0.018986472859978676, + -0.0145828602835536, + -0.010600531473755836, + -0.07798607647418976, + 0.010420471429824829, + -0.05861049145460129, + -0.002434426685795188, + -0.014273232780396938, + -7.682995783397928e-05, + -0.029145823791623116, + -0.008605384267866611, + 0.0016684518195688725, + -0.008739265613257885, + -0.022870423272252083, + -0.022584576159715652, + 0.009296143427491188, + -0.030074717476963997, + -0.01182159036397934, + -0.048661183565855026, + 0.024512488394975662, + -0.022402895614504814, + -0.0004124187689740211, + 0.05041750147938728, + 0.02017001248896122, + -0.014538070186972618, + 0.008094827644526958, + -0.021068088710308075, + -0.05674217641353607, + 0.05543273687362671, + -0.0003058549191337079, + 0.009890705347061157, + -0.0006079478771425784, + 0.01249066460877657, + -0.006523665972054005, + 0.027566960081458092, + 0.015339151956140995, + -0.00963156670331955, + -0.012753945775330067, + -0.02511623315513134, + 0.007902747020125389, + 0.04832083731889725, + 0.01700122281908989, + 0.025543147698044777, + 0.030934108421206474, + 0.05903099849820137, + -0.013830292038619518, + 0.007253151852637529, + 0.04567129909992218, + -0.01953618973493576, + 0.026057351380586624, + -0.017068052664399147, + 0.008565455675125122, + 0.015885204076766968, + -0.04213133454322815, + 0.005680468399077654, + 0.010006438009440899, + -0.040225800126791, + 0.045605581253767014, + 0.00631161080673337, + 0.0034153303131461143, + 0.020361432805657387, + 0.032735928893089294, + -0.02695406787097454, + 0.0023317700251936913, + -0.007750748191028833, + 0.03823265805840492, + -0.05444883555173874, + -0.008913367986679077, + 0.006640100385993719, + 0.011128532700240612, + -0.0008620306616649032, + -0.0018996980506926775, + 0.006246556527912617, + -0.01748446375131607, + 0.007400299422442913, + 0.001638194196857512, + -0.06918758153915405, + -0.012121139094233513, + 0.0028283449355512857, + 0.0018009773921221495, + -0.012895200401544571, + -0.008330107666552067, + -0.027328284457325935, + 0.02578337863087654, + 0.019226262345910072, + 0.006800990551710129, + -0.00750042125582695, + -0.022383328527212143, + -0.009945318102836609, + -0.009009922854602337, + 0.0744161307811737, + -0.0063364822417497635, + 0.04927600547671318, + 0.009459988214075565, + 0.009965496137738228, + 0.01936146430671215, + -0.07257162034511566, + 0.018367689102888107, + 0.0016419690800830722, + -0.00018708515563048422, + -0.007340257056057453, + 0.01323650497943163, + 0.025394948199391365, + -0.027825897559523582, + 0.030701611191034317, + 0.020868021994829178, + 0.0038466767873615026, + 0.0496191643178463, + 0.00918627716600895, + -0.017109649255871773, + -0.019791413098573685, + 0.02066716179251671, + -0.03821332007646561, + -0.014995858073234558, + 0.019874995574355125, + 0.0022448680829256773, + -0.0021356462966650724, + -0.029201222583651543, + 0.01876833476126194, + 0.011426509357988834, + 0.0008081150008365512, + 0.001794296083971858, + 0.024883195757865906, + -0.001570228487253189, + -0.003507714020088315, + 0.01980658434331417, + -0.0031219690572470427, + -0.010246078483760357, + 0.014166000299155712, + -0.018967052921652794, + -0.004595512989908457, + 0.04882611334323883, + -0.07620732486248016, + 0.006991351023316383, + 0.020441001281142235, + 0.02988213673233986, + 0.02714286372065544, + -0.01681913621723652, + -0.005255110561847687, + -0.0023246826604008675, + -0.013639348559081554, + 0.00037093390710651875, + 0.001967804040759802, + 0.009725070558488369, + -0.011592910625040531, + -0.007870553992688656, + 0.042274393141269684, + 0.002957566874101758, + 0.061166033148765564, + -0.026565758511424065, + 0.001103694667108357, + -0.05302276834845543, + 0.028829630464315414, + -0.00990065187215805, + 0.023396318778395653, + 0.019799960777163506, + 0.019586050882935524, + -0.058255162090063095, + -0.020415160804986954, + 0.030694859102368355, + 0.0015537234721705317, + 0.0015787278534844518, + -0.034064844250679016, + 0.0055934591218829155, + 0.01486029289662838, + 0.03239109739661217, + -0.04368962347507477, + -0.007062561344355345, + 0.018295906484127045, + 0.011872666887938976, + -0.025442708283662796, + 0.011461357586085796, + -0.03811607509851456, + -0.005859666503965855, + -0.05623793974518776, + 0.009541189298033714, + 0.02083803527057171, + -0.004318810533732176, + 0.001882675802335143, + 0.015951544046401978, + 0.023711979389190674, + 0.03584279865026474, + 0.034844160079956055, + -0.008598634041845798, + -0.07954580336809158, + -0.022095613181591034, + -0.0371854305267334, + 0.010154264979064465, + 0.01313395332545042, + 0.03192739933729172, + -0.029106108471751213, + -0.00011229381925659254, + -0.011403586715459824, + 0.012910831719636917, + 0.011268051341176033, + -0.0070999241434037685, + -0.06308142840862274, + 0.008854977786540985, + 0.0242536012083292, + -0.02205698750913143, + -0.05160137638449669, + 0.0028599125798791647, + -0.029192576184868813, + 0.032680246978998184, + -0.016541862860322, + -0.024992771446704865, + -0.03766901046037674, + 0.3970429003238678, + 0.04495801404118538, + 0.01632482372224331, + 0.008579932153224945, + -0.02425755001604557, + -0.024806208908557892, + 0.023060904815793037, + 0.0318547785282135, + -0.016114475205540657, + -0.04938977584242821, + 0.013967406935989857, + -0.023869099095463753, + 0.0036885126028209925, + 0.07027924060821533, + 0.011118358932435513, + 0.015905344858765602, + -0.0016899616457521915, + -0.013345685787498951, + 0.01658196933567524, + 0.024151043966412544, + -0.05725404620170593, + -0.04698074981570244, + 0.0396430678665638, + -0.006796034052968025, + 0.03908996284008026, + -0.015381520614027977, + 0.011120792478322983, + 0.0006368536269292235, + 0.01803058385848999, + 0.03575953468680382, + 0.02647053264081478, + 0.005659092683345079, + 0.05493248999118805, + -0.029075853526592255, + -0.013134079985320568, + -0.004293778445571661, + 0.0042365919798612595, + 0.018560731783509254, + 0.015476721338927746, + 0.006185432896018028, + -0.05455822870135307, + -0.004945745225995779, + -0.01605265773832798, + 0.0031938788015395403, + 0.016214650124311447, + -0.022976107895374298, + -0.020649095997214317, + 0.048831842839717865, + 0.04365009814500809, + 0.029114743694663048, + -0.020836932584643364, + -0.02268608845770359, + 0.10339085012674332, + -0.04066287726163864, + -0.030949991196393967, + -0.008212108165025711, + 0.013183807954192162, + -0.00772842625156045, + -0.00633450411260128, + -0.009052855893969536, + 0.027396902441978455, + -0.030399443581700325, + 0.0032119324896484613, + 0.029175730422139168, + -0.03474336490035057, + 0.0053544114343822, + 0.01693754456937313, + 0.016144156455993652, + 0.0011218099389225245, + -0.0017576769459992647, + -0.020859457552433014, + -0.005807520356029272, + 0.0030788013245910406, + 0.06675930321216583, + 0.02380947954952717, + 0.005175987724214792, + 0.03017827495932579, + -0.010292382910847664, + 0.012734918855130672, + 0.015474448911845684, + -0.03751493617892265, + -0.041809190064668655, + 1.3235757251095492e-05, + 0.013195321895182133, + 0.0071694813668727875, + -0.03554733842611313, + -0.002418253570795059, + -0.018856454640626907, + -0.005290275439620018, + 0.028816303238272667, + -0.020287148654460907, + 0.009623118676245213, + 0.009850702248513699, + 0.008106552995741367, + 0.002533804392442107, + 0.006016520783305168, + 0.0785083994269371, + -0.021551089361310005, + 0.001965555362403393, + -0.012856855988502502, + -0.04479045793414116, + -0.02659635990858078, + -0.01673988066613674, + 0.004870582837611437, + 0.003280427074059844, + 0.01962978020310402, + 0.034681666642427444, + -0.01688743196427822, + 0.05005009472370148, + 0.0040205735713243484, + -0.01915711537003517, + 0.005932615604251623, + -0.0073605249635875225, + 0.016323857009410858, + 0.013514048419892788, + -0.0015998072922229767, + 0.006582219619303942, + -0.03202241659164429, + -0.037036217749118805, + -0.007975779473781586, + -0.0303666852414608, + 0.002628149464726448, + -0.004559539724141359, + -0.01605704054236412, + -0.007788235321640968, + 0.010678512044250965, + 0.1151614561676979, + 0.01788845844566822, + -0.0007857093587517738, + 0.012302708812057972, + 0.023969290778040886, + -0.08696448057889938, + -0.03805828094482422, + -0.05615659058094025, + 0.027615102007985115, + -0.005892209708690643, + -0.0009184397640638053, + 0.002683089580386877, + -0.035660114139318466, + 0.007539199199527502, + 0.0053027174435555935, + 0.031757574528455734, + -0.002848900854587555, + 0.0034314922522753477, + 0.01558717992156744, + 0.03150433674454689, + 0.004402683116495609, + 0.016112053766846657, + 0.014055824838578701, + 0.015562172047793865, + -0.010366227477788925, + 0.03469172492623329, + -0.019225554540753365, + 0.020375626161694527, + -0.02775319293141365, + -0.059123262763023376, + -0.010911599732935429, + 0.015448730438947678, + 0.021994587033987045, + 0.017843082547187805, + -0.005166998598724604, + -0.034164540469646454, + -0.0004175939247943461, + 0.01104766596108675, + -0.020053913816809654, + 0.039762452244758606, + 0.008659162558615208, + -0.022124340757727623, + 0.019045613706111908, + -0.06957241892814636, + -0.052891455590724945, + 0.003299916395917535, + 0.020925074815750122, + 0.019491858780384064, + -0.12321283668279648, + -0.04629581794142723, + -0.006962566636502743, + -0.0020384802483022213, + 0.0039587439969182014, + 0.035785648971796036, + 0.014263927936553955, + -0.03550507128238678, + 0.028259065002202988, + 0.015788117423653603, + -0.00036025396548211575, + 0.02071094699203968, + 0.04771891236305237, + 0.0077549731358885765, + 0.012625090777873993, + -0.020893163979053497, + -0.00856867153197527, + -1.3211693840275984e-05, + 0.051334816962480545, + 0.00045765057438984513, + 0.017384769394993782, + 0.003884805366396904, + -0.024121124297380447, + -0.012266717851161957, + -0.006968704517930746, + 0.010757089592516422, + -0.05350738391280174, + 0.023727301508188248, + 0.019082196056842804, + 0.03192524611949921, + -0.006196454167366028, + 0.008292291313409805, + 0.0007368726655840874, + -0.013098562136292458, + 0.004075137432664633, + -0.0024593467824161053, + -0.03715121001005173, + 0.015147512778639793, + 0.04068650305271149, + -0.006046648602932692, + -0.019162246957421303, + 0.016990434378385544, + 0.0015632716240361333, + -0.02342849038541317, + 0.029801730066537857, + -0.005388995632529259, + 0.01633348874747753, + -0.0032561253756284714, + 0.007406767923384905, + 0.003707624040544033, + -0.025014594197273254, + -0.026143589988350868, + 0.013566258363425732, + 0.028525305911898613, + 0.014075806364417076, + -0.015409544110298157, + 0.033760130405426025, + -0.048122018575668335, + -0.036993060261011124, + -0.007867864333093166, + -0.041697852313518524, + -0.0010802546748891473, + 0.020758695900440216, + 0.008738024160265923, + -0.0009177828906103969, + -0.004723722115159035, + 0.009427225217223167, + 0.007082020398229361, + 0.01442504208534956, + 0.008431525900959969, + -0.042807407677173615, + -0.0033070826902985573, + -0.011997339315712452, + -0.024155277758836746, + 0.00926969200372696, + -0.008081924170255661, + -0.015601325780153275, + 0.023925233632326126, + -0.035593997687101364, + -0.004503453616052866, + 0.017349136993288994, + -0.012132588773965836, + -0.0120330099016428, + 0.004340042360126972, + 0.00974983163177967, + -0.0227036215364933, + 0.021655606105923653, + -0.00025928442482836545, + -0.012203897349536419, + -0.02824719250202179, + -0.03865235298871994, + -0.021037118509411812, + -0.040879517793655396, + -0.010187419131398201, + 0.018474603071808815, + 0.0008939869585447013, + -0.03730769827961922, + 0.03283590078353882, + 0.0076865446753799915, + -0.04424893483519554, + -0.003419474232941866, + 0.015464000403881073, + 0.011065838858485222, + 0.020341923460364342, + -0.0290254857391119, + -0.006595897488296032, + 0.00953591987490654, + 0.004392125643789768, + -0.00012372042692732066, + -0.04903648421168327, + 0.0015002998989075422, + 0.005308989435434341, + -0.012498050928115845, + -0.009352189488708973, + 0.003707605181261897, + 0.009703538380563259, + 0.02519577369093895, + -0.02843480370938778, + -0.011768740601837635, + 0.002621549181640148, + 0.025649866089224815, + 0.023641880601644516, + -0.0796482115983963, + 0.017784401774406433, + -0.34925758838653564, + 0.021572351455688477, + 0.026252271607518196, + 0.006252041552215815, + -0.002762504853308201, + 0.0331568643450737, + 0.011562683619558811, + -0.00011669870582409203, + 0.025260018184781075, + 0.005641339812427759, + -0.007515949662774801, + 0.011755573563277721, + -0.07171700149774551, + 0.02880139835178852, + -0.004133063368499279, + -8.774129673838615e-05, + -0.019737761467695236, + -0.009883102029561996, + 0.008253892883658409, + -0.006566590163856745, + -0.026892615482211113, + 0.04041588678956032, + 0.014738034456968307, + 0.019329698756337166, + -0.016123048961162567, + -0.002526910975575447, + 0.020963212475180626, + -0.007293545641005039, + -0.024193815886974335, + 0.010632967576384544, + 0.036593589931726456, + 0.012540450319647789, + -0.022729573771357536, + -0.02804374322295189, + -0.04179384931921959, + 0.09949790686368942, + 0.017301032319664955, + -0.02183309942483902, + -0.03181248903274536, + -0.014138933271169662, + 0.00019694257935043424, + 0.015585615299642086, + 0.03814292699098587, + 0.009098630398511887, + 0.01469374168664217, + 0.016748197376728058, + -0.004643304739147425, + -0.0006677682395093143, + 0.002011387376114726, + -0.022271575406193733, + 0.015812663361430168, + 0.00937198381870985, + -0.0030996594578027725, + 0.005742889363318682, + -0.009052428416907787, + -0.015921834856271744, + -0.014969362877309322, + 0.007903863675892353, + 0.004932207986712456, + -0.01287754625082016, + -0.0013474930310621858, + 0.0064361076802015305, + 0.02700338326394558, + -0.025299202650785446, + -0.028619838878512383, + 0.014081072993576527, + 0.052360568195581436, + 0.011123059317469597, + 0.04029460623860359, + 0.0011116029927507043, + -0.002075196010991931, + -0.009131189435720444, + 0.011407029815018177, + -0.035893380641937256, + -0.028724931180477142, + -0.011394710280001163, + 0.05797463655471802, + -0.020209623500704765, + 0.04363744333386421, + -0.006749213207513094, + 0.012879502028226852, + 0.034782540053129196, + -0.034556660801172256, + -0.02822887897491455, + -0.013917154632508755, + -0.00027914223028346896, + 0.02755252830684185, + 0.0014453644398599863, + 0.0019065398955717683, + -0.0022857922594994307, + 0.02839573845267296, + 0.021031998097896576, + 0.025055155158042908, + -0.017823223024606705, + 0.014915856532752514, + -0.019567644223570824, + 0.009157845750451088, + 0.006400901358574629, + -0.029375815764069557, + 0.013117631897330284, + -0.004113129340112209, + 0.019805097952485085, + 0.04155049845576286, + 0.010550294071435928, + -0.03339941427111626, + 0.004725751467049122, + 0.024128511548042297, + 0.026795735582709312, + 0.007496385369449854, + 0.010543947108089924, + 0.023075317963957787, + -0.008046990260481834, + -0.01219051331281662, + -0.07101534307003021, + -0.10483770072460175, + 0.008396685123443604, + 0.009030187502503395, + -0.032975342124700546, + 0.0051651899702847, + -0.00021946807100903243, + -0.008597257547080517, + -0.03508983179926872, + 0.05737549811601639, + -0.050860513001680374, + 0.04097380489110947, + -0.005430142395198345, + 0.01850227266550064, + 0.01901807263493538, + -0.02268983982503414, + 0.015654431656003, + -0.018044112250208855, + 0.01116078533232212, + -0.008747306652367115, + 0.03043055720627308, + 0.0012756240321323276, + 0.006913267076015472, + 0.02512415498495102, + -0.028964858502149582, + -0.001783488318324089, + -0.027565181255340576, + 0.0031734586227685213, + 0.02616010047495365, + -0.0031493408605456352, + 0.013531427830457687, + -0.03157264366745949, + -0.006276039406657219, + 0.0035562128759920597, + 0.009109086357057095, + -0.03329436853528023, + 0.034299593418836594, + 0.016998399049043655, + 0.004522405564785004, + 0.0005323882214725018, + 0.007104580290615559, + -0.005218680948019028, + -0.009724593721330166, + 0.006759223528206348, + 0.014331292361021042, + -0.03597205877304077, + 0.007929036393761635, + 0.07662399858236313, + -0.00608443096280098, + 0.0403163805603981, + 0.01957748271524906, + -0.032505057752132416, + -0.014330866746604443, + 0.033153701573610306, + 0.003094551619142294, + -0.03215579316020012, + 0.00026322947815060616, + 0.038414593786001205, + 0.027731236070394516, + -0.0014191922964528203, + 0.028065240010619164, + 0.0196593776345253, + -0.012644018046557903, + 0.026709161698818207, + -0.0071570356376469135, + -0.024541856721043587, + -0.012130528688430786, + -0.004576939158141613, + -0.0013866458320990205, + -0.011796187609434128, + -0.00953871663659811, + -0.041972801089286804, + -0.03161953389644623, + 0.019511830061674118, + -0.02342156507074833, + 0.007188326213508844, + 0.013653554022312164, + 0.018543744459748268, + -0.006417089607566595, + -0.0020956452935934067, + -0.024543000385165215, + -0.005865475162863731, + -0.019490450620651245, + -0.026732539758086205, + 0.026203827932476997, + 0.009151864796876907, + 0.012535243295133114, + 0.025363566353917122, + 0.012485903687775135, + 0.04852234199643135, + -0.03065638802945614, + 0.0009827371686697006, + -0.0012634373269975185, + 0.04666881263256073, + -0.011242462322115898, + 0.002027340931817889, + 0.016937941312789917, + 0.006692285183817148, + 0.011247091926634312, + -0.005298103671520948, + 0.013968206010758877, + -0.013694017194211483, + -0.0046989088878035545, + 0.0018148234812542796, + -0.03768179565668106, + -0.07373784482479095, + 0.008249733597040176, + 0.018620656803250313, + -0.013096204027533531, + -0.019917121157050133, + -0.027686186134815216, + 0.0007021096535027027, + -0.02068217843770981, + 0.00655044661834836, + -0.0033980414737015963, + 0.009517433121800423, + 0.045982230454683304, + 0.02380436472594738, + -0.03125205636024475, + -0.010761408135294914, + 0.003940118011087179, + -0.026994075626134872, + 0.026320258155465126, + -0.010426385328173637, + 0.014530244283378124, + -0.05075230076909065, + -0.031160954385995865, + 0.01532987505197525, + 0.009780986234545708, + -0.01948167383670807, + -0.008966472931206226, + -0.008368038572371006, + 0.018608957529067993, + -0.029915573075413704, + -0.005054105538874865, + -0.009464059956371784, + 0.008793911896646023, + 0.004447522573173046, + -0.020860277116298676, + 0.01748020574450493, + -0.012638217769563198, + -0.02966173365712166, + 0.012612638995051384, + -0.01960955746471882, + -0.016461346298456192, + 0.05710098147392273, + -0.00030114056426100433, + 0.0017727509839460254, + -0.00846083089709282, + 0.01592075079679489, + 0.012356323190033436, + 0.024696288630366325, + -0.032880526036024094, + -0.01449920330196619, + 0.030914178118109703, + -0.012796862982213497 + ] + } +] \ No newline at end of file diff --git a/blocks/excel_contetn/hat_block.xlsx b/blocks/excel_contetn/hat_block.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..f009f687b0cf267472c9865a350d3d17773c0b31 Binary files /dev/null and b/blocks/excel_contetn/hat_block.xlsx differ diff --git a/blocks/excel_contetn/stack_block.xlsx b/blocks/excel_contetn/stack_block.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..982959de2701e18571537b12bfbccd2aaa42c3a6 Binary files /dev/null and b/blocks/excel_contetn/stack_block.xlsx differ diff --git a/blocks/hat_blocks.json b/blocks/hat_blocks.json new file mode 100644 index 0000000000000000000000000000000000000000..cfbf9ca2464fc14683d922487c66c2a927fcf0a5 --- /dev/null +++ b/blocks/hat_blocks.json @@ -0,0 +1,217 @@ +{ + "block_category": "Hat Blocks", + "description": "Hat blocks are characterized by a rounded top and a bump at the bottom. They initiate scripts, meaning they are the starting point for a sequence of interconnected blocks.", + "blocks": [ + { + "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": null, + "example_standalone": "when green flag clicked", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n go to x: (0) y: (0)\n say [Hello!] for (2) seconds\nend", + "explanation": "This script makes the sprite go to the center of the stage and then say 'Hello!' for 2 seconds when the green flag is clicked." + } + ] + }, + { + "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": [ + { + "name": "key", + "type": "dropdown", + "options": [ + "space", + "up arrow", + "down arrow", + "right arrow", + "left arrow", + "any", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9" + ] + } + ], + "example_standalone": "when [space v] key pressed", + "example_with_other_blocks": [ + { + "script": "when [space v] key pressed\n repeat (10)\n change y by (10)\n wait (0.1) seconds\n change y by (-10)\n end", + "explanation": "This script makes the sprite jump when the spacebar is pressed." + }, + { + "script": "when [right arrow v] key pressed\n point in direction (90)\n move (10) steps\nend", + "explanation": "This script moves the sprite right when the right arrow key is pressed." + } + ] + }, + { + "block_name": "when this sprite clicked", + "block_type": "Events", + "op_code": "event_whenthisspriteclicked", + "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": null, + "example_standalone": "when this sprite clicked", + "example_with_other_blocks": [ + { + "script": "when this sprite clicked\n say [Ouch!] for (1) seconds\n change [score v] by (-1)\nend", + "explanation": "This script makes the sprite say 'Ouch!' and decreases the score by 1 when the sprite is clicked." + } + ] + }, + { + "block_name": "when backdrop switches to ()", + "block_type": "Events", + "op_code": "event_whenbackdropswitchesto", + "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": [ + { + "name": "backdrop name", + "type": "dropdown", + "options": ["backdrop1", "backdrop2", "..."] + } + ], + "example_standalone": "when backdrop switches to [game over v]", + "example_with_other_blocks": [ + { + "script": "when backdrop switches to [game over v]\n stop [all v]\nend", + "explanation": "This script stops all running processes when the backdrop changes to 'game over'." + }, + { + "script": "when backdrop switches to [level completed v]\n stop [all v]\nend", + "explanation": "This script stops all running processes when the backdrop changes to 'level completed'." + } + ] + }, + { + "block_name": "when () > ()", + "block_type": "Events", + "op_code": "event_whengreaterthan", + "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": [ + { + "name": "value type", + "type": "dropdown", + "options": [ + "loudness", + "timer" + ] + }, + { + "name": "threshold", + "type": "number" + } + ], + "example_standalone": "when [loudness v] > (70)", + "example_with_other_blocks": [ + { + "script": "when [loudness v] > (70)\n start sound [scream v]\nend", + "explanation": "This script starts a 'scream' sound when the microphone loudness exceeds 70." + } + ] + }, + { + "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": [ + { + "name": "message name", + "type": "dropdown", + "options": ["message1", "message2", "new message..."] + } + ], + "example_standalone": "when I receive [start game v]", + "example_with_other_blocks": [ + { + "script": "when I receive [start game v]\n show\n go to x: (0) y: (0)\nend", + "explanation": "This script makes the sprite visible and moves it to the center of the stage when it receives the 'start game' broadcast." + }, + { + "script": "when I receive [game over v]\n set score to 0\n stop [all v]\nend", + "explanation": "This script stops all and resets the score on stage when it receives the 'game over' broadcast." + } + ] + }, + { + "block_name": "When I Start as a Clone", + "block_type": "Control", + "op_code": "control_start_as_clone", + "block_shape": "Hat Block", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": null, + "example_standalone": "When I Start as a Clone", + "example_with_other_blocks": [ + { + "script": "when I start as a clone\n go to x: (pick random -240 to 240) y: (pick random -180 to 180)\n show\n forever\n move (10) steps\n if on edge, bounce\n end\nend", + "explanation": "This script makes a newly created clone appear at a random position, become visible, and then continuously move 10 steps, bouncing if it hits an edge." + } + ] + }, + { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script. It allows users to define reusable sequences of code by specifying the block's name and any input parameters it will accept. This promotes modularity and abstraction in projects.", + "inputs": [ + { + "name": "PROCCONTAINER", + "type": "block_prototype" + } + ], + "example_standalone": "define jump (height)", + "example_with_other_blocks": [ + { + "script": "define jump (height)\n change y by (height)\n wait (0.5) seconds\n change y by (0 - (height))\nend\n\nwhen green flag clicked\n jump (50)\nend", + "explanation": "This script first defines a custom block named 'jump' that takes a numerical input 'height'. The definition outlines the actions for jumping up and then down. Later, 'jump (50)' is called to make the sprite jump 50 units." + } + ] + } + ] +} diff --git a/blocks/reporter_blocks.json b/blocks/reporter_blocks.json new file mode 100644 index 0000000000000000000000000000000000000000..0635724f6afa1a7a6b842f2dc2c3d80815742bf5 --- /dev/null +++ b/blocks/reporter_blocks.json @@ -0,0 +1,709 @@ +{ + "block_category": "Reporter Blocks", + "description": "Reporter blocks have rounded edges. Their purpose is to report values, which can be numbers or strings, and are designed to fit into input slots of other blocks.", + "blocks": [ + { + "block_name": "(x position)", + "block_type": "Motion", + "op_code": "motion_xposition", + "block_shape": "Reporter Block", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": null, + "example_standalone": "x position", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n say (x position) for (2) seconds\nend", + "explanation": "This script makes the sprite say its current X-coordinate for 2 seconds." + } + ] + }, + { + "block_name": "(y position)", + "block_type": "Motion", + "op_code": "motion_yposition", + "block_shape": "Reporter Block", + "functionality": "Reports the current Y coordinate of the sprite on the stage.[NOTE: not used in stage/backdrops]", + "inputs": null, + "example_standalone": "y position", + "example_with_other_blocks": [ + { + "script": "set [worms v] to (y position)", + "explanation": "This script assigns the sprite's current Y position to the 'worms' variable." + } + ] + }, + { + "block_name": "(direction)", + "block_type": "Motion", + "op_code": "motion_direction", + "block_shape": "Reporter Block", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": null, + "example_standalone": "direction", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n say (direction) for (2) seconds\nend", + "explanation": "This script makes the sprite say its current direction in degrees for 2 seconds." + } + ] + }, + { + "block_name": "(costume ())", + "block_type": "Looks", + "op_code": "looks_costumenumbername", + "block_shape": "Reporter Block", + "functionality": "Reports the current costume's number or name.", + "inputs": [ + { + "name": "NUMBER_NAME", + "type": "dropdown", + "options": [ + "number", + "name" + ] + } + ], + "example_standalone": "costume [number v]", + "example_with_other_blocks": [ + { + "script": "say join [I am costume ] (costume [name v])", + "explanation": "This script makes the sprite display its current costume name in a speech bubble." + } + ] + }, + { + "block_name": "(size)", + "block_type": "Looks", + "op_code": "looks_size", + "block_shape": "Reporter Block", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": null, + "example_standalone": "size", + "example_with_other_blocks": [ + { + "script": "set size to ( (size) + (10) )", + "explanation": "This script increases the sprite's size by 10% from its current size." + } + ] + }, + { + "block_name": "(backdrop ())", + "block_type": "Looks", + "op_code": "looks_backdropnumbername", + "block_shape": "Reporter Block", + "functionality": "Reports the current backdrop's number or name.", + "inputs": [ + { + "name": "NUMBER_NAME", + "type": "dropdown", + "options": [ + "number", + "name" + ] + } + ], + "example_standalone": "(backdrop [number v])", + "example_with_other_blocks": [ + { + "script": "say join [Current backdrop: ] (backdrop [name v]) for (2) seconds", + "explanation": "This script makes the sprite say the name of the current stage backdrop for 2 seconds." + } + ] + }, + { + "block_name": "(volume)", + "block_type": "Sound", + "op_code": "sound_volume", + "block_shape": "Reporter Block", + "functionality": "Reports the current volume level of the sprite.", + "inputs": null, + "example_standalone": "volume", + "example_with_other_blocks": [ + { + "script": "say join [Current volume: ] (volume)", + "explanation": "This script makes the sprite display its current volume level in a speech bubble." + } + ] + }, + { + "block_name": "(distance to ())", + "block_type": "Sensing", + "op_code": "sensing_distanceto", + "block_shape": "Reporter Block", + "functionality": "Reports the distance from the current sprite to the mouse-pointer or another specified sprite.", + "inputs": [ + { + "name": "target", + "type": "dropdown", + "options": ["mouse-pointer", "Sprite1", "Sprite2", "...", "_edge_"] + } + ], + "example_standalone": "distance to [mouse-pointer v]", + "example_with_other_blocks": [ + { + "script": "if <(distance to [Sprite2 v]) < (50)> then\n say [Too close!]\nend", + "explanation": "This script makes the sprite say 'Too close!' if it is less than 50 steps away from 'Sprite2'." + } + ] + }, + { + "block_name": "(answer)", + "block_type": "Sensing", + "op_code": "sensing_answer", + "block_shape": "Reporter Block", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": null, + "example_standalone": "answer", + "example_with_other_blocks": [ + { + "script": "ask [What is your name?] and wait\n say join [Hello ] (answer)", + "explanation": "This script prompts the user for their name and then uses the 'answer' block to incorporate their input into a greeting." + } + ] + }, + { + "block_name": "(mouse x)", + "block_type": "Sensing", + "op_code": "sensing_mousex", + "block_shape": "Reporter Block", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": null, + "example_standalone": "mouse x", + "example_with_other_blocks": [ + { + "script": "go to x: (mouse x) y: (mouse y)", + "explanation": "This script makes the sprite follow the mouse pointer's X and Y coordinates." + } + ] + }, + { + "block_name": "(mouse y)", + "block_type": "Sensing", + "op_code": "sensing_mousey", + "block_shape": "Reporter Block", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": null, + "example_standalone": "mouse y", + "example_with_other_blocks": [ + { + "script": "if <(mouse y) < (0)> then\n say [Below center]", + "explanation": "This script makes the sprite say 'Below center' if the mouse pointer is in the lower half of the stage." + } + ] + }, + { + "block_name": "(loudness)", + "block_type": "Sensing", + "op_code": "sensing_loudness", + "block_shape": "Reporter Block", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": null, + "example_standalone": "loudness", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n forever\n if <(loudness) > (30)> then\n start sound [pop v]\nend", + "explanation": "This script continuously checks the microphone loudness and plays a 'pop' sound if it exceeds 30." + } + ] + }, + { + "block_name": "(timer)", + "block_type": "Sensing", + "op_code": "sensing_timer", + "block_shape": "Reporter Block", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": null, + "example_standalone": "timer", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n reset timer\n wait (5) seconds\n say join [Time elapsed: ] (timer)", + "explanation": "This script resets the timer when the green flag is clicked, waits for 5 seconds, and then reports the elapsed time." + } + ] + }, + { + "block_name": "(() of ())", + "block_type": "Sensing", + "op_code": "sensing_of", + "block_shape": "Reporter Block", + "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.", + "inputs": [ + { + "name": "value to report", + "type": "dropdown", + "options": [ + "x position", + "y position", + "direction", + "costume #", + "costume name", + "size", + "volume", + "backdrop #", + "backdrop name" + ] + }, + { + "name": "sprite/stage", + "type": "dropdown", + "options": ["Stage", "Sprite1", "Sprite2", "...", "_edge_"] + } + ], + "example_standalone": "x position of [Sprite1 v]", + "example_with_other_blocks": [ + { + "script": "set [other sprite X v] to ( (x position) of [Sprite2 v] )", + "explanation": "This script sets the 'other sprite X' variable to the current X-position of 'Sprite2'." + } + ] + }, + { + "block_name": "(current ())", + "block_type": "Sensing", + "op_code": "sensing_current", + "block_shape": "Reporter Block", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": [ + { + "name": "time unit", + "type": "dropdown", + "options": [ + "year", + "month", + "date", + "day of week", + "hour", + "minute", + "second" + ] + } + ], + "example_standalone": "current [hour v]", + "example_with_other_blocks": [ + { + "script": "say join [The current hour is ] (current [hour v])", + "explanation": "This script makes the sprite say the current hour." + } + ] + }, + { + "block_name": "(days since 2000)", + "block_type": "Sensing", + "op_code": "sensing_dayssince2000", + "block_shape": "Reporter Block", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": null, + "example_standalone": "days since 2000", + "example_with_other_blocks": [ + { + "script": "say join [Days passed: ] (days since 2000)", + "explanation": "This script makes the sprite display the number of days that have passed since January 1, 2000." + } + ] + }, + { + "block_name": "(username)", + "block_type": "Sensing", + "op_code": "sensing_username", + "block_shape": "Reporter Block", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": null, + "example_standalone": "username", + "example_with_other_blocks": [ + { + "script": "say join [Hello, ] (username)", + "explanation": "This script makes the sprite greet the user by their Scratch username." + } + ] + }, + { + "block_name": "(() + ())", + "block_type": "operator", + "op_code": "operator_add", + "block_shape": "Reporter Block", + "functionality": "Adds two numerical values.", + "inputs": [ + { + "name": "number1", + "type": "number" + }, + { + "name": "number2", + "type": "number" + } + ], + "example_standalone": "(5) + (3)", + "example_with_other_blocks": [ + { + "script": "set [total v] to ( (number 1) + (number 2) )", + "explanation": "This script calculates the sum of 'number 1' and 'number 2' and stores the result in the 'total' variable." + } + ] + }, + { + "block_name": "(() - ())", + "block_type": "operator", + "op_code": "operator_subtract", + "block_shape": "Reporter Block", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": [ + { + "name": "number1", + "type": "number" + }, + { + "name": "number2", + "type": "number" + } + ], + "example_standalone": "((10) - (4))", + "example_with_other_blocks": [ + { + "script": "set [difference v] to ( (number 1) - (number 2) )", + "explanation": "This script calculates the subtraction of 'number 2' from 'number 1' and stores the result in the 'difference' variable." + } + ] + }, + { + "block_name": "(() * ())", + "block_type": "operator", + "op_code": "operator_multiply", + "block_shape": "Reporter Block", + "functionality": "Multiplies two numerical values.", + "inputs": [ + { + "name": "number1", + "type": "number" + }, + { + "name": "number2", + "type": "number" + } + ], + "example_standalone": "(6) * (7)", + "example_with_other_blocks": [ + { + "script": "set [area v] to ( (length) * (width) )", + "explanation": "This script calculates the area by multiplying 'length' and 'width' variables and stores it in the 'area' variable." + } + ] + }, + { + "block_name": "(() / ())", + "block_type": "operator", + "op_code": "operator_divide", + "block_shape": "Reporter Block", + "functionality": "Divides the first numerical value by the second.", + "inputs": [ + { + "name": "number1", + "type": "number" + }, + { + "name": "number2", + "type": "number" + } + ], + "example_standalone": "((20) / (5))", + "example_with_other_blocks": [ + { + "script": "set [average v] to ( (total score) / (number of students) )", + "explanation": "This script calculates the average by dividing 'total score' by 'number of students' and stores it in the 'average' variable." + } + ] + }, + { + "block_name": "(pick random () to ())", + "block_type": "operator", + "op_code": "operator_random", + "block_shape": "Reporter Block", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": [ + { + "name": "min", + "type": "number" + }, + { + "name": "max", + "type": "number" + } + ], + "example_standalone": "(pick random (1) to (10))", + "example_with_other_blocks": [ + { + "script": "go to x: (pick random -240 to 240) y: (pick random -180 to 180)", + "explanation": "This script moves the sprite to a random position on the stage." + } + ] + }, + { + "block_name": "(join ()())", + "block_type": "operator", + "op_code": "operator_join", + "block_shape": "Reporter Block", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": [ + { + "name": "string1", + "type": "string/number" + }, + { + "name": "string2", + "type": "string/number" + } + ], + "example_standalone": "(join [Hello ][World!])", + "example_with_other_blocks": [ + { + "script": "say (join [Hello ][World!])", + "explanation": "This script makes the sprite display 'Hello World!' in a speech bubble by joining two string literals." + } + ] + }, + { + "block_name": "letter () of ()", + "block_type": "operator", + "op_code": "operator_letterof", + "block_shape": "Reporter Block", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": [ + { + "name": "index", + "type": "number" + }, + { + "name": "text", + "type": "string" + } + ], + "example_standalone": "(letter (1) of [apple])", + "example_with_other_blocks": [ + { + "script": "say (letter (1) of [apple])", + "explanation": "This script makes the sprite display the first character of the string 'apple', which is 'a'." + } + ] + }, + { + "block_name": "(length of ())", + "block_type": "operator", + "op_code": "operator_length", + "block_shape": "Reporter Block", + "functionality": "Reports the total number of characters in a given string.", + "inputs": [ + { + "name": "text", + "type": "string" + } + ], + "example_standalone": "(length of [banana])", + "example_with_other_blocks": [ + { + "script": "say (length of [banana])", + "explanation": "This script makes the sprite display the length of the string 'banana', which is 6." + } + ] + }, + { + "block_name": "(() mod ())", + "block_type": "operator", + "op_code": "operator_mod", + "block_shape": "Reporter Block", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": [ + { + "name": "number1", + "type": "number" + }, + { + "name": "number2", + "type": "number" + } + ], + "example_standalone": "((10) mod (3))", + "example_with_other_blocks": [ + { + "script": "if <([number v] mod (2) = (0))> then\n say [Even number]", + "explanation": "This script checks if a 'number' variable is even by checking if its remainder when divided by 2 is 0." + } + ] + }, + { + "block_name": "(round ())", + "block_type": "operator", + "op_code": "operator_round", + "block_shape": "Reporter Block", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": [ + { + "name": "number", + "type": "number" + } + ], + "example_standalone": "(round (3.7))", + "example_with_other_blocks": [ + { + "script": "set [rounded score v] to (round (score))", + "explanation": "This script rounds the 'score' variable to the nearest whole number and stores it in 'rounded score'." + } + ] + }, + { + "block_name": "(() of ())", + "block_type": "operator", + "op_code": "operator_mathop", + "block_shape": "Reporter Block", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": [ + { + "name": "function type", + "type": "dropdown", + "options": [ + "abs", + "floor", + "ceiling", + "sqrt", + "sin", + "cos", + "tan", + "asin", + "acos", + "atan", + "ln", + "log", + "e ^", + "10 ^" + ] + }, + { + "name": "value", + "type": "number" + } + ], + "example_standalone": "([sqrt v] of (25))", + "example_with_other_blocks": [ + { + "script": "set [distance v] to ([sqrt v] of ( ( (x position) * (x position) ) + ( (y position) * (y position) ) ))", + "explanation": "This script calculates the distance from the origin (0,0) using the Pythagorean theorem and stores it in 'distance'." + } + ] + }, + { + "block_name": "[variable v]", + "block_type": "Data", + "op_code": "data_variable", + "block_shape": "Reporter Block", + "functionality": "Provides the current value stored in a variable.", + "inputs": [ + { + "name": "variable name", + "type": "dropdown", + "options": ["my variable", "score", "..."] + } + ], + "example_standalone": "[score v]", + "example_with_other_blocks": [ + { + "script": "say ([score v]) for (2) seconds", + "explanation": "This script makes the sprite say the current value of the 'score' variable for 2 seconds." + } + ] + }, + { + "block_name": "[list v]", + "block_type": "Data", + "op_code": "data_list", + "block_shape": "Reporter Block", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": [ + { + "name": "list name", + "type": "dropdown", + "options": ["my list", "list2", "..."] + } + ], + "example_standalone": "[my list v]", + "example_with_other_blocks": [ + { + "script": "say ([my list v]) ", + "explanation": "This script makes the sprite say all the contents of 'my list'." + } + ] + }, + { + "block_name": "(item (2) of [myList v])", + "block_type": "Data", + "op_code": "data_itemoflist", + "block_shape": "Reporter Block", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": [ + { + "name": "index/option", + "type": "number or dropdown", + "options": [ + "last", + "random" + ] + }, + { + "name": "list name", + "type": "dropdown", + "options": ["shopping list", "my list", "..."] + } + ], + "example_standalone": "item (1) of [shopping list v]", + "example_with_other_blocks": [ + { + "script": "say (item (2) of [myList v]) for 2 seconds ", + "explanation": "This script makes the sprite display the first item from the 'shopping list'." + } + ] + }, + { + "block_name": "(length of [myList v])", + "block_type": "Data", + "op_code": "data_lengthoflist", + "block_shape": "Reporter Block", + "functionality": "Provides the total number of items contained in a list.", + "inputs": [ + { + "name": "list name", + "type": "dropdown", + "options": ["my list", "shopping list", "..."] + } + ], + "example_standalone": "(length of [myList v])", + "example_with_other_blocks": [ + { + "script": "say join (length of [shopping list v]) [ items in the list.]", + "explanation": "This script makes the sprite display the total number of items currently in the 'shopping list'." + } + ] + }, + { + "block_name": "(item # of [Dog] in [myList v])", + "block_type": "Data", + "op_code": "data_itemnumoflist", + "block_shape": "Reporter Block", + "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.", + "inputs": [ + { + "name": "item", + "type": "string/number" + }, + { + "name": "list name", + "type": "dropdown", + "options": ["my list", "shopping list", "..."] + } + ], + "example_standalone": "(item # of [apple] in [shopping list v])", + "example_with_other_blocks": [ + { + "script": "if <(item # of [Dog] in [myList v])> (0)> then\n say join [Dog found at position ] (item # of [Dog] in [my list v])", + "explanation": "This script checks if 'banana' is in 'my list' and, if so, reports its position." + } + ] + } + ] +} \ No newline at end of file diff --git a/blocks/sprites/Batter.sprite3/592ee9ab2aeefe65cb4fb95fcd046f33.svg b/blocks/sprites/Batter.sprite3/592ee9ab2aeefe65cb4fb95fcd046f33.svg new file mode 100644 index 0000000000000000000000000000000000000000..796ae83e6e6b0a68446d0e15b51281e2040db528 --- /dev/null +++ b/blocks/sprites/Batter.sprite3/592ee9ab2aeefe65cb4fb95fcd046f33.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/Batter.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav b/blocks/sprites/Batter.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/blocks/sprites/Batter.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/blocks/sprites/Batter.sprite3/9d193bef6e3d6d8eba6d1470b8bf9351.svg b/blocks/sprites/Batter.sprite3/9d193bef6e3d6d8eba6d1470b8bf9351.svg new file mode 100644 index 0000000000000000000000000000000000000000..5cf5ffa34bec98cb7b80a096b3a46cb280b2fe21 --- /dev/null +++ b/blocks/sprites/Batter.sprite3/9d193bef6e3d6d8eba6d1470b8bf9351.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/Batter.sprite3/baseball_sprite_motion_1.png b/blocks/sprites/Batter.sprite3/baseball_sprite_motion_1.png new file mode 100644 index 0000000000000000000000000000000000000000..7cea4502774ee54ddaaa1511fdcc336a3dcdf81e Binary files /dev/null and b/blocks/sprites/Batter.sprite3/baseball_sprite_motion_1.png differ diff --git a/blocks/sprites/Batter.sprite3/bd4fc003528acfa847e45ff82f346eee.svg b/blocks/sprites/Batter.sprite3/bd4fc003528acfa847e45ff82f346eee.svg new file mode 100644 index 0000000000000000000000000000000000000000..42b439f490a8fbad897ac1fbd3c858b221b14499 --- /dev/null +++ b/blocks/sprites/Batter.sprite3/bd4fc003528acfa847e45ff82f346eee.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/Batter.sprite3/fdfde4bcbaca0f68e83fdf3f4ef0c660.svg b/blocks/sprites/Batter.sprite3/fdfde4bcbaca0f68e83fdf3f4ef0c660.svg new file mode 100644 index 0000000000000000000000000000000000000000..8b53a4c1e0dc4aed5ef1d913ec5e1adee23cc51e --- /dev/null +++ b/blocks/sprites/Batter.sprite3/fdfde4bcbaca0f68e83fdf3f4ef0c660.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/Batter.sprite3/sprite.json b/blocks/sprites/Batter.sprite3/sprite.json new file mode 100644 index 0000000000000000000000000000000000000000..40fd559a0aeb37f37b10d8a6f40875074e2fb960 --- /dev/null +++ b/blocks/sprites/Batter.sprite3/sprite.json @@ -0,0 +1,67 @@ +{ + "isStage": false, + "name": "Batter", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "batter-a", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "9d193bef6e3d6d8eba6d1470b8bf9351", + "md5ext": "9d193bef6e3d6d8eba6d1470b8bf9351.svg", + "rotationCenterX": 46, + "rotationCenterY": 80 + }, + { + "name": "batter-b", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "fdfde4bcbaca0f68e83fdf3f4ef0c660", + "md5ext": "fdfde4bcbaca0f68e83fdf3f4ef0c660.svg", + "rotationCenterX": 16, + "rotationCenterY": 67 + }, + { + "name": "batter-c", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "bd4fc003528acfa847e45ff82f346eee", + "md5ext": "bd4fc003528acfa847e45ff82f346eee.svg", + "rotationCenterX": 94, + "rotationCenterY": 66 + }, + { + "name": "batter-d", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "592ee9ab2aeefe65cb4fb95fcd046f33", + "md5ext": "592ee9ab2aeefe65cb4fb95fcd046f33.svg", + "rotationCenterX": 70, + "rotationCenterY": 102 + } + ], + "sounds": [ + { + "name": "pop", + "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", + "dataFormat": "wav", + "format": "", + "rate": 48000, + "sampleCount": 1123, + "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav" + } + ], + "volume": 100, + "visible": true, + "x": -12, + "y": 39, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" +} diff --git a/blocks/sprites/Bear.sprite3/6f303e972f33fcb7ef36d0d8012d0975.svg b/blocks/sprites/Bear.sprite3/6f303e972f33fcb7ef36d0d8012d0975.svg new file mode 100644 index 0000000000000000000000000000000000000000..760612e2d79708c0cc0aef8e89155700930c0dd0 --- /dev/null +++ b/blocks/sprites/Bear.sprite3/6f303e972f33fcb7ef36d0d8012d0975.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/blocks/sprites/Bear.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav b/blocks/sprites/Bear.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/blocks/sprites/Bear.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/blocks/sprites/Bear.sprite3/bear_motion_2.png b/blocks/sprites/Bear.sprite3/bear_motion_2.png new file mode 100644 index 0000000000000000000000000000000000000000..82c7e362d77e4545c3f37aa427e25f1f830363ac Binary files /dev/null and b/blocks/sprites/Bear.sprite3/bear_motion_2.png differ diff --git a/blocks/sprites/Bear.sprite3/deef1eaa96d550ae6fc11524a1935024.svg b/blocks/sprites/Bear.sprite3/deef1eaa96d550ae6fc11524a1935024.svg new file mode 100644 index 0000000000000000000000000000000000000000..b7b43887e6b5265674fe96a5d1356728dd16f043 --- /dev/null +++ b/blocks/sprites/Bear.sprite3/deef1eaa96d550ae6fc11524a1935024.svg @@ -0,0 +1,152 @@ + + + + + bear-a + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/Bear.sprite3/sprite.json b/blocks/sprites/Bear.sprite3/sprite.json new file mode 100644 index 0000000000000000000000000000000000000000..dc8ebeed3ab528fb47922bd0ef771c5d39b97782 --- /dev/null +++ b/blocks/sprites/Bear.sprite3/sprite.json @@ -0,0 +1 @@ +{"isStage":false,"name":"Bear","variables":{},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"name":"bear-a","bitmapResolution":1,"dataFormat":"svg","assetId":"deef1eaa96d550ae6fc11524a1935024","md5ext":"deef1eaa96d550ae6fc11524a1935024.svg","rotationCenterX":100,"rotationCenterY":90},{"name":"bear-b","bitmapResolution":1,"dataFormat":"svg","assetId":"6f303e972f33fcb7ef36d0d8012d0975","md5ext":"6f303e972f33fcb7ef36d0d8012d0975.svg","rotationCenterX":94,"rotationCenterY":190.66666666666666}],"sounds":[{"name":"pop","assetId":"83a9787d4cb6f3b7632b4ddfebf74367","dataFormat":"wav","format":"","rate":48000,"sampleCount":1123,"md5ext":"83a9787d4cb6f3b7632b4ddfebf74367.wav"}],"volume":100,"visible":true,"x":-4,"y":4,"size":100,"direction":90,"draggable":false,"rotationStyle":"all around"} \ No newline at end of file diff --git a/blocks/sprites/Beetle.sprite3/46d0dfd4ae7e9bfe3a6a2e35a4905eae.png b/blocks/sprites/Beetle.sprite3/46d0dfd4ae7e9bfe3a6a2e35a4905eae.png new file mode 100644 index 0000000000000000000000000000000000000000..c738364b9fcaa201da59054e72b438a34933b933 Binary files /dev/null and b/blocks/sprites/Beetle.sprite3/46d0dfd4ae7e9bfe3a6a2e35a4905eae.png differ diff --git a/blocks/sprites/Beetle.sprite3/46d0dfd4ae7e9bfe3a6a2e35a4905eae.svg b/blocks/sprites/Beetle.sprite3/46d0dfd4ae7e9bfe3a6a2e35a4905eae.svg new file mode 100644 index 0000000000000000000000000000000000000000..0388addcfe4383c3558175a2731be3385d4aead9 --- /dev/null +++ b/blocks/sprites/Beetle.sprite3/46d0dfd4ae7e9bfe3a6a2e35a4905eae.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/Beetle.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav b/blocks/sprites/Beetle.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/blocks/sprites/Beetle.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/blocks/sprites/Beetle.sprite3/sprite.json b/blocks/sprites/Beetle.sprite3/sprite.json new file mode 100644 index 0000000000000000000000000000000000000000..2a5b8aa46ec2a6e9e3fc1df3920e589ec7eba772 --- /dev/null +++ b/blocks/sprites/Beetle.sprite3/sprite.json @@ -0,0 +1 @@ +{"isStage":false,"name":"Beetle","variables":{},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"name":"beetle","bitmapResolution":1,"dataFormat":"svg","assetId":"46d0dfd4ae7e9bfe3a6a2e35a4905eae","md5ext":"46d0dfd4ae7e9bfe3a6a2e35a4905eae.svg","rotationCenterX":43,"rotationCenterY":38}],"sounds":[{"name":"pop","assetId":"83a9787d4cb6f3b7632b4ddfebf74367","dataFormat":"wav","format":"","rate":48000,"sampleCount":1123,"md5ext":"83a9787d4cb6f3b7632b4ddfebf74367.wav"}],"volume":100,"visible":true,"x":42,"y":-32,"size":100,"direction":90,"draggable":false,"rotationStyle":"all around"} \ No newline at end of file diff --git a/blocks/sprites/Centaur.sprite3/2373556e776cad3ba4d6ee04fc34550b.png b/blocks/sprites/Centaur.sprite3/2373556e776cad3ba4d6ee04fc34550b.png new file mode 100644 index 0000000000000000000000000000000000000000..1bc8b10c62d227535aeba0afef9c8d51d8be4630 Binary files /dev/null and b/blocks/sprites/Centaur.sprite3/2373556e776cad3ba4d6ee04fc34550b.png differ diff --git a/blocks/sprites/Centaur.sprite3/2373556e776cad3ba4d6ee04fc34550b.svg b/blocks/sprites/Centaur.sprite3/2373556e776cad3ba4d6ee04fc34550b.svg new file mode 100644 index 0000000000000000000000000000000000000000..7e201bbee69bfce5bee4940632a491ddec5bc2a6 --- /dev/null +++ b/blocks/sprites/Centaur.sprite3/2373556e776cad3ba4d6ee04fc34550b.svg @@ -0,0 +1,273 @@ + + + + + centaur-b + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/Centaur.sprite3/362d7440a57cab29914fecea621e50d4.wav b/blocks/sprites/Centaur.sprite3/362d7440a57cab29914fecea621e50d4.wav new file mode 100644 index 0000000000000000000000000000000000000000..0771639351c69bfeff85e6c169c433572120c722 Binary files /dev/null and b/blocks/sprites/Centaur.sprite3/362d7440a57cab29914fecea621e50d4.wav differ diff --git a/blocks/sprites/Centaur.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav b/blocks/sprites/Centaur.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/blocks/sprites/Centaur.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/blocks/sprites/Centaur.sprite3/c00ffa6c5dd0baf9f456b897ff974377.svg b/blocks/sprites/Centaur.sprite3/c00ffa6c5dd0baf9f456b897ff974377.svg new file mode 100644 index 0000000000000000000000000000000000000000..eb576eae09eea558b349fc3fc58fa91691328c13 --- /dev/null +++ b/blocks/sprites/Centaur.sprite3/c00ffa6c5dd0baf9f456b897ff974377.svg @@ -0,0 +1,282 @@ + + + + + centaur-d + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/Centaur.sprite3/d722329bd9373ad80625e5be6d52f3ed.svg b/blocks/sprites/Centaur.sprite3/d722329bd9373ad80625e5be6d52f3ed.svg new file mode 100644 index 0000000000000000000000000000000000000000..c48ca3c94984ad51ff9a6b4ffcd3d41d6e421941 --- /dev/null +++ b/blocks/sprites/Centaur.sprite3/d722329bd9373ad80625e5be6d52f3ed.svg @@ -0,0 +1,261 @@ + + + + + centaur-a + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/Centaur.sprite3/d7aa990538915b7ef1f496d7e8486ade.svg b/blocks/sprites/Centaur.sprite3/d7aa990538915b7ef1f496d7e8486ade.svg new file mode 100644 index 0000000000000000000000000000000000000000..f1c4a20e681a226abecb961ad4b9ec273bb45ef1 --- /dev/null +++ b/blocks/sprites/Centaur.sprite3/d7aa990538915b7ef1f496d7e8486ade.svg @@ -0,0 +1,257 @@ + + + + + centaur-c + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/Centaur.sprite3/sprite.json b/blocks/sprites/Centaur.sprite3/sprite.json new file mode 100644 index 0000000000000000000000000000000000000000..4c805ab16d0aaf9f0599b7f9b45c9a463bc476b8 --- /dev/null +++ b/blocks/sprites/Centaur.sprite3/sprite.json @@ -0,0 +1 @@ +{"isStage":false,"name":"Centaur","variables":{},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"name":"centaur-a","bitmapResolution":1,"dataFormat":"svg","assetId":"d722329bd9373ad80625e5be6d52f3ed","md5ext":"d722329bd9373ad80625e5be6d52f3ed.svg","rotationCenterX":110,"rotationCenterY":140},{"name":"centaur-b","bitmapResolution":1,"dataFormat":"svg","assetId":"2373556e776cad3ba4d6ee04fc34550b","md5ext":"2373556e776cad3ba4d6ee04fc34550b.svg","rotationCenterX":110,"rotationCenterY":140},{"name":"centaur-c","bitmapResolution":1,"dataFormat":"svg","assetId":"d7aa990538915b7ef1f496d7e8486ade","md5ext":"d7aa990538915b7ef1f496d7e8486ade.svg","rotationCenterX":110,"rotationCenterY":140},{"name":"centaur-d","bitmapResolution":1,"dataFormat":"svg","assetId":"c00ffa6c5dd0baf9f456b897ff974377","md5ext":"c00ffa6c5dd0baf9f456b897ff974377.svg","rotationCenterX":110,"rotationCenterY":140}],"sounds":[{"name":"pop","assetId":"83a9787d4cb6f3b7632b4ddfebf74367","dataFormat":"wav","format":"","rate":48000,"sampleCount":1123,"md5ext":"83a9787d4cb6f3b7632b4ddfebf74367.wav"},{"name":"snort","assetId":"362d7440a57cab29914fecea621e50d4","dataFormat":"wav","format":"adpcm","rate":22050,"sampleCount":17273,"md5ext":"362d7440a57cab29914fecea621e50d4.wav"}],"volume":100,"visible":true,"x":-94,"y":46,"size":100,"direction":90,"draggable":false,"rotationStyle":"all around"} \ No newline at end of file diff --git a/blocks/sprites/Crab.sprite3/49839aa1b0feed02a3c759db5f8dee71.svg b/blocks/sprites/Crab.sprite3/49839aa1b0feed02a3c759db5f8dee71.svg new file mode 100644 index 0000000000000000000000000000000000000000..ca19891501adc082b693044a06bf79815a99e42f --- /dev/null +++ b/blocks/sprites/Crab.sprite3/49839aa1b0feed02a3c759db5f8dee71.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/Crab.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav b/blocks/sprites/Crab.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/blocks/sprites/Crab.sprite3/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/blocks/sprites/Crab.sprite3/bear_element.png b/blocks/sprites/Crab.sprite3/bear_element.png new file mode 100644 index 0000000000000000000000000000000000000000..fdc5308eeb996af1a88d40fbda68df4ddaf35a67 Binary files /dev/null and b/blocks/sprites/Crab.sprite3/bear_element.png differ diff --git a/blocks/sprites/Crab.sprite3/f7cdd2acbc6d7559d33be8675059c79e.svg b/blocks/sprites/Crab.sprite3/f7cdd2acbc6d7559d33be8675059c79e.svg new file mode 100644 index 0000000000000000000000000000000000000000..86c6ec401638abf0321c77b684e12b568253ad14 --- /dev/null +++ b/blocks/sprites/Crab.sprite3/f7cdd2acbc6d7559d33be8675059c79e.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/Crab.sprite3/sprite.json b/blocks/sprites/Crab.sprite3/sprite.json new file mode 100644 index 0000000000000000000000000000000000000000..e868ad01ab2ee516221335788db4cbc3f82e9999 --- /dev/null +++ b/blocks/sprites/Crab.sprite3/sprite.json @@ -0,0 +1 @@ +{"isStage":false,"name":"Crab","variables":{},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"name":"crab-a","bitmapResolution":1,"dataFormat":"svg","assetId":"f7cdd2acbc6d7559d33be8675059c79e","md5ext":"f7cdd2acbc6d7559d33be8675059c79e.svg","rotationCenterX":75,"rotationCenterY":75},{"name":"crab-b","bitmapResolution":1,"dataFormat":"svg","assetId":"49839aa1b0feed02a3c759db5f8dee71","md5ext":"49839aa1b0feed02a3c759db5f8dee71.svg","rotationCenterX":75,"rotationCenterY":75}],"sounds":[{"name":"pop","assetId":"83a9787d4cb6f3b7632b4ddfebf74367","dataFormat":"wav","format":"","rate":48000,"sampleCount":1123,"md5ext":"83a9787d4cb6f3b7632b4ddfebf74367.wav"}],"volume":100,"visible":true,"x":-74,"y":-4,"size":100,"direction":90,"draggable":false,"rotationStyle":"all around"} \ No newline at end of file diff --git a/blocks/sprites/Soccer Ball.sprite3/1727f65b5f22d151685b8e5917456a60.wav b/blocks/sprites/Soccer Ball.sprite3/1727f65b5f22d151685b8e5917456a60.wav new file mode 100644 index 0000000000000000000000000000000000000000..f5be3a1606476bfbee8a4321f41de6a1a0ead520 Binary files /dev/null and b/blocks/sprites/Soccer Ball.sprite3/1727f65b5f22d151685b8e5917456a60.wav differ diff --git a/blocks/sprites/Soccer Ball.sprite3/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg b/blocks/sprites/Soccer Ball.sprite3/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg new file mode 100644 index 0000000000000000000000000000000000000000..3a3908a99eb18cdeaacf1222caec50828656871e --- /dev/null +++ b/blocks/sprites/Soccer Ball.sprite3/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg @@ -0,0 +1,29 @@ + + + + Slice 1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/Soccer Ball.sprite3/cat_football.png b/blocks/sprites/Soccer Ball.sprite3/cat_football.png new file mode 100644 index 0000000000000000000000000000000000000000..87ebad8ad8ce666a447e6db92278881ba476fe6c Binary files /dev/null and b/blocks/sprites/Soccer Ball.sprite3/cat_football.png differ diff --git a/blocks/sprites/Soccer Ball.sprite3/sprite.json b/blocks/sprites/Soccer Ball.sprite3/sprite.json new file mode 100644 index 0000000000000000000000000000000000000000..0b02539b3f4e4a59d108f975b1846f226caf0bd3 --- /dev/null +++ b/blocks/sprites/Soccer Ball.sprite3/sprite.json @@ -0,0 +1,40 @@ +{ + "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, + "visible": true, + "x": 41, + "y": -9, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" +} diff --git a/blocks/sprites/cat/0fb9be3e8397c983338cb71dc84d0b25.svg b/blocks/sprites/cat/0fb9be3e8397c983338cb71dc84d0b25.svg new file mode 100644 index 0000000000000000000000000000000000000000..5ff997fd11132a3505e71ce46f5e14e34dbb4430 --- /dev/null +++ b/blocks/sprites/cat/0fb9be3e8397c983338cb71dc84d0b25.svg @@ -0,0 +1,42 @@ + + + + costume2.1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/cat/83c36d806dc92327b9e7049a565c6bff.wav b/blocks/sprites/cat/83c36d806dc92327b9e7049a565c6bff.wav new file mode 100644 index 0000000000000000000000000000000000000000..45742d5ef6f09d05b0f0788cb055ffe54abfd9ad Binary files /dev/null and b/blocks/sprites/cat/83c36d806dc92327b9e7049a565c6bff.wav differ diff --git a/blocks/sprites/cat/bcf454acf82e4504149f7ffe07081dbc.svg b/blocks/sprites/cat/bcf454acf82e4504149f7ffe07081dbc.svg new file mode 100644 index 0000000000000000000000000000000000000000..03df23e29ad059d88e559d48bf5e2717870455f3 --- /dev/null +++ b/blocks/sprites/cat/bcf454acf82e4504149f7ffe07081dbc.svg @@ -0,0 +1,42 @@ + + + + costume1.1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blocks/sprites/cat/cat_motion_1.png b/blocks/sprites/cat/cat_motion_1.png new file mode 100644 index 0000000000000000000000000000000000000000..6062934a66b6eb74a14c6496cc05967e0efe4dc1 Binary files /dev/null and b/blocks/sprites/cat/cat_motion_1.png differ diff --git a/blocks/sprites/cat/sprite.json b/blocks/sprites/cat/sprite.json new file mode 100644 index 0000000000000000000000000000000000000000..fafb3638116fd5a808bd618516e12f3d982bca65 --- /dev/null +++ b/blocks/sprites/cat/sprite.json @@ -0,0 +1,49 @@ +{ + "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, + "visible": true, + "x": 0, + "y": 0, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" +} diff --git a/blocks/stack_blocks.json b/blocks/stack_blocks.json new file mode 100644 index 0000000000000000000000000000000000000000..034ae2cf9aea6dbaf31f76d72b49e6bd523bb00c --- /dev/null +++ b/blocks/stack_blocks.json @@ -0,0 +1,1321 @@ +{ + "block_category": "Stack Blocks", + "description": "Stack blocks are the most common block shape, featuring a notch at the top and a bump at the bottom. They perform the main commands within a script and can connect both above and below them.", + "blocks": [ + { + "block_name": "move () steps", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_movesteps", + "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.", + "inputs": [ + { + "name": "STEPS", + "type": "number" + } + ], + "example_standalone": "move () steps", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n go to x: (0) y: (0)\n point in direction (90)\n move (50) steps\nend", + "explanation": "This script first places the sprite at the center of the stage, points it to the right (90 degrees), and then moves it 50 steps in that direction." + } + ] + }, + { + "block_name": "turn right () degrees", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": [ + { + "name": "DEGREES", + "type": "number" + } + ], + "example_standalone": "turn (clockwise icon) (15) degrees", + "example_with_other_blocks": [ + { + "script": "when [right arrow v] key pressed\n turn (clockwise icon) (15) degrees\nend", + "explanation": "This script makes the sprite turn clockwise by 15 degrees every time the right arrow key is pressed." + }, + { + "script": "when green flag clicked\n forever\n turn (clockwise icon) (15) degrees\n wait (0.5) seconds\n end", + "explanation": "This script makes the sprite continuously spin clockwise by 15 degrees every half second." + } + ] + }, + { + "block_name": "turn left () degrees", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": [ + { + "name": "DEGREES", + "type": "number" + } + ], + "example_standalone": "turn (counter-clockwise icon) (15) degrees", + "example_with_other_blocks": [ + { + "script": "when [left arrow v] key pressed\n turn (counter-clockwise icon) (15) degrees\nend", + "explanation": "This script makes the sprite turn counter-clockwise by 15 degrees every time the left arrow key is pressed." + }, + { + "script": "when green flag clicked\n forever\n turn (counter-clockwise icon) (15) degrees\n wait (0.5) seconds\n end\nend", + "explanation": "This script makes the sprite continuously spin counter-clockwise by 15 degrees every half second." + } + ] + }, + { + "block_name": "go to ()", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": [ + { + "name": "TO", + "type": "dropdown", + "options": [ + "random position", + "mouse-pointer", + "sprite1", + "..." + ] + } + ], + "example_standalone": "go to [random position v]", + "example_with_other_blocks": [ + { + "script": "when this sprite clicked\n go to [mouse-pointer v]", + "explanation": "This script moves the sprite to the current position of the mouse pointer whenever the sprite is clicked." + }, + { + "script": "when this sprite clicked\n go to [sprite v]", + "explanation": "This script moves the sprite to the another sprite's position whenever the sprite is clicked." + } + ] + }, + { + "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": [ + { + "name": "X", + "type": "number" + }, + { + "name": "Y", + "type": "number" + } + ], + "example_standalone": "go to x: (0) y: (0)", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n go to x: (120) y: (0)\n say [Ready to start! v] for (1) seconds\nend", + "explanation": "This script positions the sprite at the center of the stage at the beginning of the project and then makes it say 'Ready to start!'." + } + ] + }, + { + "block_name": "glide () secs to ()", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": [ + { + "name": "SECS", + "type": "number" + }, + { + "name": "TO", + "type": "dropdown", + "options": [ + "random position", + "mouse-pointer", + "sprite1", + "sprite2", + "..." + ] + } + ], + "example_standalone": "glide (1) secs to ([random position v])", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n glide (1) secs to ([mouse-pointer v])\nend", + "explanation": "This script makes the sprite glide smoothly to the mouse pointer's position over 1 second when the green flag is clicked." + } + ] + }, + { + "block_name": "glide () secs to x: () y: ()", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": [ + { + "name": "SECS", + "type": "number" + }, + { + "name": "X", + "type": "number" + }, + { + "name": "Y", + "type": "number" + } + ], + "example_standalone": "glide (1) secs to x: (100) y: (50)", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n glide (2) secs to x: (150) y: (-100)\n glide (2) secs to x: (-150) y: (100)\nend", + "explanation": "This script makes the sprite glide to two different points on the stage, taking 2 seconds for each movement." + } + ] + }, + { + "block_name": "point in direction ()", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": [ + { + "name": "DIRECTION", + "type": "number" + } + ], + "example_standalone": "point in direction (90)", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n point in direction (0)\n move (100) steps\nend", + "explanation": "This script makes the sprite point upwards (0 degrees) and then move 100 steps in that direction." + } + ] + }, + { + "block_name": "point towards ()", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": [ + { + "name": "TOWARDS", + "type": "dropdown", + "options": [ + "mouse-pointer", + "sprite1", + "..." + ] + } + ], + "example_standalone": "point towards [mouse-pointer v]", + "example_with_other_blocks": [ + { + "script": "when this sprite clicked\n point towards [mouse-pointer v]\n move (10) steps\nend", + "explanation": "When the sprite is clicked, it will point towards the mouse pointer and then move 10 steps in that direction." + }, + { + "script": "when green flag clicked\n forever\n point towards [mouse-pointer v]\n move (5) steps\n end\nend", + "explanation": "This script makes the sprite continuously follow the mouse pointer." + } + ] + }, + { + "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": [ + { + "name": "DX", + "type": "number" + } + ], + "example_standalone": "change x by (10)", + "example_with_other_blocks": [ + { + "script": "when [right arrow v] key pressed\n change x by (10)\nend", + "explanation": "This script moves the sprite 10 steps to the right when the right arrow key is pressed." + } + ] + }, + { + "block_name": "set x to ()", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": [ + { + "name": "X", + "type": "number" + } + ], + "example_standalone": "set x to (0)", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n set x to (0)\n set y to (0)\nend", + "explanation": "This script centers the sprite horizontally at the start of the project." + } + ] + }, + { + "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": [ + { + "name": "DY", + "type": "number" + } + ], + "example_standalone": "change y by (10)", + "example_with_other_blocks": [ + { + "script": "when [up arrow v] key pressed\n change y by (10)\nend", + "explanation": "This script moves the sprite 10 steps up when the up arrow key is pressed." + } + ] + }, + { + "block_name": "set y to ()", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": [ + { + "name": "Y", + "type": "number" + } + ], + "example_standalone": "set y to (0)", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n set x to (0)\n set y to (0)\nend", + "explanation": "This script centers the sprite vertically at the start of the project." + } + ] + }, + { + "block_name": "if on edge, bounce", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": null, + "example_standalone": "if on edge, bounce", + "example_with_other_blocks": [ + { + "script": "when I receive [start moving v]\n repeat (50)\n move (5) steps\n if on edge, bounce\n end\nend", + "explanation": "Upon receiving the 'start moving' broadcast, the sprite will move 5 steps repeatedly for 50 times, bouncing off edges if it touches them." + }, + { + "script": "when green flag clicked\n forever\n move (10) steps\n if on edge, bounce\n end\nend", + "explanation": "This script makes the sprite move continuously and bounce off the edges of the stage." + } + ] + }, + { + "block_name": "set rotation style ()", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": [ + { + "name": "STYLE", + "type": "dropdown", + "options": [ + "left-right", + "don't rotate", + "all around" + ] + } + ], + "example_standalone": "set rotation style [left-right v]", + "example_with_other_blocks": [ + { + "script": "when backdrop switches to [game level 1 v]\n set rotation style [all around v]\nend", + "explanation": "When the backdrop changes to 'game level 1', the sprite's rotation style will be set to 'all around', allowing it to rotate freely." + }, + { + "script": "when green flag clicked\n set rotation style [left-right v]\n forever\n move (10) steps\n if on edge, bounce\n end \nend", + "explanation": "This script makes the sprite move horizontally and flip its costume when it hits an edge, instead of rotating." + } + ] + }, + { + "block_name": "say () for () seconds", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": [ + { + "name": "MESSAGE", + "type": "string" + }, + { + "name": "SECS", + "type": "number" + } + ], + "example_standalone": "say [Hello!] for (2) seconds", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n say [Grr] for (3) seconds\n say [Have you seen my honey? v] for (3) seconds\nend", + "explanation": "This script makes the sprite display two sequential speech bubbles with different messages and durations. First, it says 'Grr' for 3 seconds, then 'Have you seen my honey?' for another 3 seconds." + } + ] + }, + { + "block_name": "say ()", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": [ + { + "name": "MESSAGE", + "type": "string" + } + ], + "example_standalone": "say [Hello! v]", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n say [Welcome to my game! v]\n wait (2) seconds\n say [] \nend", + "explanation": "This script makes the sprite say 'Welcome to my game!' for 2 seconds, then clears the speech bubble." + } + ] + }, + { + "block_name": "think () for () seconds", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": [ + { + "name": "MESSAGE", + "type": "string" + }, + { + "name": "SECS", + "type": "number" + } + ], + "example_standalone": "think [Hmm... v] for (2) seconds", + "example_with_other_blocks": [ + { + "script": "when this sprite clicked\n think [What should I do? v] for (2) seconds\nend", + "explanation": "This script makes the sprite display a thought bubble saying 'What should I do?' for 2 seconds when clicked." + } + ] + }, + { + "block_name": "think ()", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": [ + { + "name": "MESSAGE", + "type": "string" + } + ], + "example_standalone": "think [Got it! v]", + "example_with_other_blocks": [ + { + "script": "when I receive [correct answer v]\n think [That's right! v]\n wait (1) seconds\n think [good v] \nend", + "explanation": "This script makes the sprite think 'That's right!' for 1 second when a 'correct answer' broadcast is received, then clears the thought bubble." + } + ] + }, + { + "block_name": "switch costume to ()", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": [ + { + "name": "COSTUME", + "type": "dropdown/number" + } + ], + "example_standalone": "switch costume to [costume1 v]", + "example_with_other_blocks": [ + { + "script": "when I receive [explosion v]\n repeat (5)\n next costume\n end\n hide[costume1 v] \nend", + "explanation": "This script animates an explosion by rapidly switching costumes, then hides the sprite. [3]" + } + ] + }, + { + "block_name": "next costume", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_nextcostume", + "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.", + "inputs": null, + "example_standalone": "next costume", + "example_with_other_blocks": [ + { + "script": "when [space v] key pressed\n repeat (3)\n next costume\n wait (0.1) seconds\n end \nend", + "explanation": "When the space key is pressed, the sprite will cycle through its next three costumes with a short delay between each change." + }, + { + "script": "when green flag clicked\n forever\n next costume\n wait (0.2) seconds\n end \nend", + "explanation": "This script continuously animates the sprite by switching to the next costume every 0.2 seconds, creating a walking or flying effect." + } + ] + }, + { + "block_name": "switch backdrop to ()", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": [ + { + "name": "BACKDROP", + "type": "dropdown/number" + } + ], + "example_standalone": "switch backdrop to [backdrop1 v]", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n switch backdrop to [start screen v]\nend ", + "explanation": "This script sets the stage to a 'start screen' backdrop when the project begins." + } + ] + }, + { + "block_name": "switch backdrop to () and wait", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": [ + { + "name": "BACKDROP", + "type": "dropdown/number" + } + ], + "example_standalone": "switch backdrop to [game over v] and wait", + "example_with_other_blocks": [ + { + "script": "broadcast [game over v]\n switch backdrop to [game over v] and wait\n stop [all v] \nend", + "explanation": "This script broadcasts a 'game over' message, then changes the backdrop to 'game over' and waits for any associated scripts to finish before stopping all processes." + } + ] + }, + { + "block_name": "next backdrop", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_nextbackdrop", + "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.", + "inputs": null, + "example_standalone": "next backdrop", + "example_with_other_blocks": [ + { + "script": "when [space v] key pressed\n next backdrop\nend", + "explanation": "This script changes the stage to the next backdrop in the list each time the space key is pressed." + } + ] + }, + { + "block_name": "change size by ()", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": [ + { + "name": "CHANGE", + "type": "number" + } + ], + "example_standalone": "change size by (10)", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n repeat (10)\n change size by (5)\n wait (0.1) seconds\n end \nend ", + "explanation": "This script makes the sprite gradually grow larger over 10 steps, with a short pause between each size change." + } + ] + }, + { + "block_name": "set size to () %", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": [ + { + "name": "SIZE", + "type": "number" + } + ], + "example_standalone": "set size to (100) %", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n set size to (50) %\n wait (1) seconds\n set size to (100) %\nend ", + "explanation": "This script makes the sprite shrink to half its original size at the start, waits for 1 second, then returns to its original size." + } + ] + }, + { + "block_name": "change () effect by ()", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": [ + { + "name": "EFFECT", + "type": "dropdown", + "options": [ + "color", + "fisheye", + "whirl", + "pixelate", + "mosaic", + "brightness", + "ghost" + ] + }, + { + "name": "CHANGE", + "type": "number" + } + ], + "example_standalone": "change [color v] effect by (25)", + "example_with_other_blocks": [ + { + "script": "when loudness > (10)\n change [fisheye v] effect by (5)\nend", + "explanation": "When the loudness detected by the microphone is greater than 10, the sprite's fisheye effect will increase by 5." + }, + { + "script": "when green flag clicked\n forever\n change [color v] effect by (5)\n wait (0.1) seconds\n end \nend", + "explanation": "This script makes the sprite continuously cycle through different colors." + } + ] + }, + { + "block_name": "set () effect to ()", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": [ + { + "name": "EFFECT", + "type": "dropdown", + "options": [ + "color", + "fisheye", + "whirl", + "pixelate", + "mosaic", + "brightness", + "ghost" + ] + }, + { + "name": "VALUE", + "type": "number" + } + ], + "example_standalone": "set [ghost v] effect to (50)", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n set [ghost v] effect to (75)\nend", + "explanation": "This script makes the sprite 75% transparent at the start of the project." + } + ] + }, + { + "block_name": "clear graphic effects", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": null, + "example_standalone": "clear graphic effects", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n change [color v] effect by (50)\n wait (2) seconds\n clear graphic effects\nend", + "explanation": "This script changes the sprite's color effect, waits 2 seconds, then resets all graphic effects." + } + ] + }, + { + "block_name": "show", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": null, + "example_standalone": "show", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n hide[start game v]\nwhen I receive [start game v]\n show [start game v] \nend", + "explanation": "This script hides the sprite at the beginning of the project and makes it visible when a 'start game' broadcast is received." + } + ] + }, + { + "block_name": "hide", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": null, + "example_standalone": "hide", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n hide \nend", + "explanation": "This script hides the sprite from the stage when the green flag is clicked." + } + ] + }, + { + "block_name": "go to () layer", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": [ + { + "name": "FRONT_BACK", + "type": "dropdown", + "options": [ + "front", + "back" + ] + } + ], + "example_standalone": "go to [front v] layer", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n go to [front v] layer\nend", + "explanation": "This script ensures the sprite is always visible on top of other sprites at the start of the project." + } + ] + }, + { + "block_name": "go () layers", + "block_type": "Looks", + "block_shape": "Stack Block", + "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": [ + { + "name": "FORWARD_BACKWARD", + "type": "dropdown", + "options": [ + "forward", + "backward" + ] + }, + { + "name": "NUM", + "type": "number" + } + ], + "example_standalone": "go [forward v] (1) layers", + "example_with_other_blocks": [ + { + "script": "when this sprite clicked go [forward v] (1) layers\nend", + "explanation": "This script brings the clicked sprite one layer closer to the front." + } + ] + }, + { + "block_name": "play sound () until done", + "block_type": "Sound", + "block_shape": "Stack Block", + "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": [ + { + "name": "sound name", + "type": "dropdown" + } + ], + "example_standalone": "play sound [Meow v] until done", + "example_with_other_blocks": [ + { + "script": "when backdrop switches to [winning screen v]\n play sound [fanfare v] until done\n say [You won!] for (2) seconds\nend", + "explanation": "When the backdrop changes to the 'winning screen', a 'fanfare' sound will play until it finishes, and then the sprite will say 'You won!' for 2 seconds." + }, + { + "script": "forever\n play sound [Music v] until done \nend", + "explanation": "This script creates a continuous loop for background music, playing the 'Music' sound repeatedly." + } + ] + }, + { + "block_name": "start sound ()", + "block_type": "Sound", + "block_shape": "Stack Block", + "op_code": "sound_start", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": [ + { + "name": "sound name", + "type": "dropdown" + } + ], + "example_standalone": "start sound [Pop v]", + "example_with_other_blocks": [ + { + "script": "when this sprite clicked\n start sound [Pop v]\n change [score v] by (1)\nend", + "explanation": "This script plays a 'Pop' sound and increments the score simultaneously when the sprite is clicked." + } + ] + }, + { + "block_name": "stop all sounds", + "block_type": "Sound", + "block_shape": "Stack Block", + "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": null, + "example_standalone": "stop all sounds", + "example_with_other_blocks": [ + { + "script": "when I receive [game over v]\n stop all sounds\nend", + "explanation": "This script stops any sounds currently playing when the 'game over' broadcast is received." + } + ] + }, + { + "block_name": "change volume by ()", + "block_type": "Sound", + "block_shape": "Stack Block", + "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": [ + { + "name": "change", + "type": "number" + } + ], + "example_standalone": "change volume by (-10)", + "example_with_other_blocks": [ + { + "script": "when [down arrow v] key pressed\n change volume by (-5)\nend", + "explanation": "This script decreases the project's volume by 5 when the down arrow key is pressed." + } + ] + }, + { + "block_name": "set volume to () %", + "block_type": "Sound", + "block_shape": "Stack Block", + "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": [ + { + "name": "percentage", + "type": "number" + } + ], + "example_standalone": "set volume to (100) %", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n set volume to (50) %\nend", + "explanation": "This script sets the project's volume to 50% when the green flag is clicked." + } + ] + }, + { + "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": [ + { + "name": "message name", + "type": "string/dropdown" + } + ], + "example_standalone": "broadcast [start game v]", + "example_with_other_blocks": [ + { + "script": "if then\n broadcast [jump v]\nend", + "explanation": "This script sends a 'jump' message to other scripts or sprites when the space key is pressed." + } + ] + }, + { + "block_name": "broadcast () and wait", + "block_type": "Events", + "block_shape": "Stack Block", + "op_code": "event_broadcastandwait", + "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.", + "inputs": [ + { + "name": "message name", + "type": "string/dropdown" + } + ], + "example_standalone": "broadcast [initialize sprites v] and wait", + "example_with_other_blocks": [ + { + "script": "broadcast [initialize sprites v] and wait\n say [Game Started!] for (2) seconds", + "explanation": "This script ensures all sprite initialization routines complete before displaying 'Game Started!' for 2 seconds." + } + ] + }, + { + "block_name": "wait () seconds", + "block_type": "Control", + "block_shape": "Stack Block", + "op_code": "control_wait", + "functionality": "Pauses the script for a specified duration.", + "inputs": [ + { + "name": "seconds", + "type": "number" + } + ], + "example_standalone": "wait (1) seconds", + "example_with_other_blocks": [ + { + "script": "say [Hello!] for (1) seconds\n wait (0.5) seconds\n say [Goodbye!] for (1) seconds", + "explanation": "This script creates a timed dialogue sequence, pausing for 0.5 seconds between two speech bubbles." + } + ] + }, + { + "block_name": "wait until <>", + "block_type": "Control", + "block_shape": "Stack Block", + "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": [ + { + "name": "condition", + "type": "boolean" + } + ], + "example_standalone": "wait until ", + "example_with_other_blocks": [ + { + "script": "wait until \n start sound [pop v]\nend", + "explanation": "This script pauses until the space key is pressed, then plays a 'pop' sound." + } + ] + }, + { + "block_name": "stop ()", + "block_type": "Control", + "block_shape": "Stack Block", + "op_code": "control_stop", + "functionality": "Stops all scripts, this script, or other scripts in the sprite. Becomes a Cap Block if 'all' or 'this script' is selected in the dropdown menu.", + "inputs": [ + { + "name": "option", + "type": "dropdown", + "options": [ + "all", + "this script", + "other scripts in sprite" + ] + } + ], + "example_standalone": "stop [all v]", + "example_with_other_blocks": [ + { + "script": "if then\n stop [all v]\nend", + "explanation": "This script stops the entire project if the 'score' variable becomes 0." + } + ] + }, + { + "block_name": "create clone of ()", + "block_type": "Control", + "block_shape": "Stack Block", + "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": [ + { + "name": "sprite_name", + "type": "dropdown", + "options": [ + "myself", + "sprite1", + "..." + ] + } + ], + "example_standalone": "create clone of [myself v]", + "example_with_other_blocks": [ + { + "script": "when I start as a clone\n show\n go to random position\n wait (2) seconds\n delete this clone\nend", + "explanation": "When a clone is created, it will show itself, go to a random position, wait for 2 seconds, and then delete itself." + } + ] + }, + { + "block_name": "delete this clone", + "block_type": "Control", + "block_shape": "Stack Block", + "op_code": "control_delete_this_clone", + "functionality": "Deletes the clone that is currently running the script.", + "inputs": null, + "example_standalone": "delete this clone", + "example_with_other_blocks": [ + { + "script": "when I start as a clone\n wait (5) seconds\n delete this clone\nend", + "explanation": "This script makes each clone wait for 5 seconds after it's created, then deletes itself." + } + ] + }, + { + "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": [ + { + "name": "variable name", + "type": "dropdown" + }, + { + "name": "value", + "type": "any" + } + ], + "example_standalone": "set [score v] to (0)", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n set [score v] to (0)\n set [player name v] to [Guest]\nend", + "explanation": "This script initializes the 'score' variable to 0 and the 'player name' variable to 'Guest' when the project starts." + } + ] + }, + { + "block_name": "change [my variable v] by ()", + "block_type": "Data", + "block_shape": "Stack Block", + "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": [ + { + "name": "variable name", + "type": "dropdown" + }, + { + "name": "value", + "type": "number" + } + ], + "example_standalone": "change [score v] by (1)", + "example_with_other_blocks": [ + { + "script": "when this sprite clicked\n change [score v] by (1)\nend", + "explanation": "This script increments the 'score' variable by 1 each time the sprite is clicked." + } + ] + }, + { + "block_name": "add () to [my list v]", + "block_type": "Data", + "block_shape": "Stack Block", + "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": [ + { + "name": "item", + "type": "any" + }, + { + "name": "list name", + "type": "dropdown" + } + ], + "example_standalone": "add [apple] to [shopping list v]", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n add [apple] to [shopping list v]\n add [banana] to [shopping list v]\nend", + "explanation": "This script adds 'apple' and 'banana' as new items to the 'shopping list' when the project starts." + } + ] + }, + { + "block_name": "delete () of [my list v]", + "block_type": "Data", + "block_shape": "Stack Block", + "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": [ + { + "name": "index/option", + "type": "number/dropdown", + "options": [ + "all", + "last", + "random" + ] + }, + { + "name": "list name", + "type": "dropdown" + } + ], + "example_standalone": "delete (1) of [my list v]", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n delete (all) of [my list v]\nend", + "explanation": "This script clears all items from 'my list' when the green flag is clicked." + } + ] + }, + { + "block_name": "insert () at () of [my list v]", + "block_type": "Data", + "block_shape": "Stack Block", + "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": [ + { + "name": "item", + "type": "any" + }, + { + "name": "index", + "type": "number" + }, + { + "name": "list name", + "type": "dropdown" + } + ], + "example_standalone": "insert [orange] at (2) of [fruits v]", + "example_with_other_blocks": [ + { + "script": "insert [orange] at (2) of [fruits v]", + "explanation": "This script inserts 'orange' as the second item in the 'fruits' list, shifting subsequent items." + } + ] + }, + { + "block_name": "replace item () of [my list v] with ()", + "block_type": "Data", + "block_shape": "Stack Block", + "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": [ + { + "name": "index", + "type": "number" + }, + { + "name": "list name", + "type": "dropdown" + }, + { + "name": "new item", + "type": "any" + } + ], + "example_standalone": "replace item (1) of [colors v] with [blue]", + "example_with_other_blocks": [ + { + "script": "replace item (1) of [colors v] with [blue]", + "explanation": "This script changes the first item in the 'colors' list to 'blue'." + } + ] + }, + { + "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": [ + { + "name": "variable name", + "type": "dropdown" + } + ], + "example_standalone": "show variable [score v]", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n show variable [score v]\nend", + "explanation": "This script displays the 'score' variable on the stage when the project starts." + } + ] + }, + { + "block_name": "hide variable [my variable v]", + "block_type": "Data", + "block_shape": "Stack Block", + "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": [ + { + "name": "variable name", + "type": "dropdown" + } + ], + "example_standalone": "hide variable [score v]", + "example_with_other_blocks": [ + { + "script": "when I receive [game over v]\n hide variable [score v]\nend", + "explanation": "This script hides the 'score' variable when the 'game over' broadcast is received." + } + ] + }, + { + "block_name": "show list [my list v]", + "block_type": "Data", + "block_shape": "Stack Block", + "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": [ + { + "name": "list name", + "type": "dropdown" + } + ], + "example_standalone": "show list [shopping list v]", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n show list [shopping list v]\nend", + "explanation": "This script displays the 'shopping list' on the stage when the project starts." + } + ] + }, + { + "block_name": "hide list [my list v]", + "block_type": "Data", + "block_shape": "Stack Block", + "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": [ + { + "name": "list name", + "type": "dropdown" + } + ], + "example_standalone": "hide list [shopping list v]", + "example_with_other_blocks": [ + { + "script": "when I receive [game over v]\n hide list [shopping list v]\nend", + "explanation": "This script hides the 'shopping list' when the 'game over' broadcast is received." + } + ] + }, + { + "block_name": "Ask () and Wait", + "block_type": "Sensing", + "block_shape": "Stack Block", + "op_code": "sensing_askandwait", + "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.", + "inputs": [ + { + "name": "question", + "type": "text" + } + ], + "example_standalone": "ask [What is your name? v] and wait", + "example_with_other_blocks": [ + { + "script": "ask [What is your name? v] and wait\n say join [Hello v] (answer) for (2) seconds \nend", + "explanation": "This script prompts the user for their name, waits for input, then greets them using the provided answer." + } + ] + }, + { + "block_name": "Reset Timer", + "block_type": "Sensing", + "block_shape": "Stack Block", + "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": null, + "example_standalone": "reset timer", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n reset timer\n wait (5) seconds\n say timer for (2) seconds\nend", + "explanation": "This script resets the timer at the start, waits for 5 seconds, then says the current timer value." + } + ] + }, + { + "block_name": "set drag mode [draggable v]", + "block_type": "Sensing", + "block_shape": "Stack Block", + "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": null, + "fields": { + "DRAG_MODE": { + "type": "dropdown", + "options": [ + "draggable", + "not draggable" + ] + } + }, + "example_standalone": "set drag mode [draggable v]", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n set drag mode [not draggable v]\nend when this sprite clicked\n set drag mode [draggable v] \nend", + "explanation": "This script makes the sprite not draggable when the project starts, but allows it to be dragged once it's clicked." + } + ] + }, + { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block. This block allows users to call and reuse custom code sequences by simply dragging and dropping it into their scripts, optionally providing required input values.", + "inputs": [ + { + "name": "argument_name_1", + "type": "any" + }, + { + "name": "argument_name_2", + "type": "any" + } + ], + "example_standalone": "jump (50)", + "example_with_other_blocks": [ + { + "script": "when green flag clicked\n go to x: (0) y: (0)\n jump (50)\n wait (1) seconds\n say [I jumped!] for (2) seconds", + "explanation": "This script moves the sprite to a starting position, then calls the 'jump' custom block with an input of 50 (assuming 'jump' is a custom block that moves the sprite up and down). After the jump, the sprite says 'I jumped!'." + }, + { + "script": "when green flag clicked\n hide\n forever\n create clone of [myself v]\n wait (1) seconds\n end", + "explanation": "This script continuously creates new clones of the current sprite every second after the original sprite hides itself." + } + ] + } + ] +} \ No newline at end of file diff --git a/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg b/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg new file mode 100644 index 0000000000000000000000000000000000000000..3a3908a99eb18cdeaacf1222caec50828656871e --- /dev/null +++ b/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg @@ -0,0 +1,29 @@ + + + + Slice 1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/83a9787d4cb6f3b7632b4ddfebf74367.wav b/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/83c36d806dc92327b9e7049a565c6bff.wav b/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/83c36d806dc92327b9e7049a565c6bff.wav new file mode 100644 index 0000000000000000000000000000000000000000..45742d5ef6f09d05b0f0788cb055ffe54abfd9ad Binary files /dev/null and b/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/83c36d806dc92327b9e7049a565c6bff.wav differ diff --git a/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/bcf454acf82e4504149f7ffe07081dbc.svg b/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/bcf454acf82e4504149f7ffe07081dbc.svg new file mode 100644 index 0000000000000000000000000000000000000000..03df23e29ad059d88e559d48bf5e2717870455f3 --- /dev/null +++ b/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/bcf454acf82e4504149f7ffe07081dbc.svg @@ -0,0 +1,42 @@ + + + + costume1.1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/e7c147730f19d284bcd7b3f00af19bb6.svg b/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/e7c147730f19d284bcd7b3f00af19bb6.svg new file mode 100644 index 0000000000000000000000000000000000000000..0326edc8bdd7da45b33bac907c15ae1c0d1026c1 --- /dev/null +++ b/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/e7c147730f19d284bcd7b3f00af19bb6.svg @@ -0,0 +1,19 @@ + + + + blue sky + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/project.json b/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/project.json new file mode 100644 index 0000000000000000000000000000000000000000..5b7c5415624acba340c66ce9486aca9a0052cc92 --- /dev/null +++ b/generated_projects/0581565f-1c42-407c-aa3a-faa81d96e6ae/project.json @@ -0,0 +1,122 @@ +{ + "targets": [ + { + "isStage": true, + "name": "Stage", + "objName": "Stage", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Blue sky", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "e7c147730f19d284bcd7b3f00af19bb6", + "md5ext": "e7c147730f19d284bcd7b3f00af19bb6.svg", + "rotationCenterX": 240, + "rotationCenterY": 180 + } + ], + "sounds": [ + { + "name": "pop", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", + "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav" + } + ], + "volume": 100, + "layerOrder": 0, + "tempo": 60, + "videoTransparency": 50, + "videoState": "on", + "textToSpeechLanguage": null + }, + { + "isStage": false, + "name": "Cat", + "objName": "Cat", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Cat", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "bcf454acf82e4504149f7ffe07081dbc", + "md5ext": "bcf454acf82e4504149f7ffe07081dbc.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [ + { + "name": "meow", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83c36d806dc92327b9e7049a565c6bff", + "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav" + } + ], + "volume": 100, + "layerOrder": 2, + "visible": true, + "x": 0, + "y": 0, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + }, + { + "isStage": false, + "name": "ball", + "objName": "ball", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "ball", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "5d973d7a3a8be3f3bd6e1cd0f73c32b5", + "md5ext": "5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [], + "volume": 100, + "layerOrder": 3, + "visible": true, + "x": 0, + "y": 0, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + } + ], + "monitors": [], + "extensions": [], + "meta": { + "semver": "3.0.0", + "vm": "11.1.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" + } +} \ No newline at end of file diff --git a/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg b/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg new file mode 100644 index 0000000000000000000000000000000000000000..3a3908a99eb18cdeaacf1222caec50828656871e --- /dev/null +++ b/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg @@ -0,0 +1,29 @@ + + + + Slice 1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/83a9787d4cb6f3b7632b4ddfebf74367.wav b/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/83c36d806dc92327b9e7049a565c6bff.wav b/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/83c36d806dc92327b9e7049a565c6bff.wav new file mode 100644 index 0000000000000000000000000000000000000000..45742d5ef6f09d05b0f0788cb055ffe54abfd9ad Binary files /dev/null and b/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/83c36d806dc92327b9e7049a565c6bff.wav differ diff --git a/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/bcf454acf82e4504149f7ffe07081dbc.svg b/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/bcf454acf82e4504149f7ffe07081dbc.svg new file mode 100644 index 0000000000000000000000000000000000000000..03df23e29ad059d88e559d48bf5e2717870455f3 --- /dev/null +++ b/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/bcf454acf82e4504149f7ffe07081dbc.svg @@ -0,0 +1,42 @@ + + + + costume1.1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/e7c147730f19d284bcd7b3f00af19bb6.svg b/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/e7c147730f19d284bcd7b3f00af19bb6.svg new file mode 100644 index 0000000000000000000000000000000000000000..0326edc8bdd7da45b33bac907c15ae1c0d1026c1 --- /dev/null +++ b/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/e7c147730f19d284bcd7b3f00af19bb6.svg @@ -0,0 +1,19 @@ + + + + blue sky + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/project.json b/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/project.json new file mode 100644 index 0000000000000000000000000000000000000000..5b7c5415624acba340c66ce9486aca9a0052cc92 --- /dev/null +++ b/generated_projects/22afaea6-1437-4fdb-8452-ffa8036c7024/project.json @@ -0,0 +1,122 @@ +{ + "targets": [ + { + "isStage": true, + "name": "Stage", + "objName": "Stage", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Blue sky", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "e7c147730f19d284bcd7b3f00af19bb6", + "md5ext": "e7c147730f19d284bcd7b3f00af19bb6.svg", + "rotationCenterX": 240, + "rotationCenterY": 180 + } + ], + "sounds": [ + { + "name": "pop", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", + "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav" + } + ], + "volume": 100, + "layerOrder": 0, + "tempo": 60, + "videoTransparency": 50, + "videoState": "on", + "textToSpeechLanguage": null + }, + { + "isStage": false, + "name": "Cat", + "objName": "Cat", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Cat", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "bcf454acf82e4504149f7ffe07081dbc", + "md5ext": "bcf454acf82e4504149f7ffe07081dbc.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [ + { + "name": "meow", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83c36d806dc92327b9e7049a565c6bff", + "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav" + } + ], + "volume": 100, + "layerOrder": 2, + "visible": true, + "x": 0, + "y": 0, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + }, + { + "isStage": false, + "name": "ball", + "objName": "ball", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "ball", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "5d973d7a3a8be3f3bd6e1cd0f73c32b5", + "md5ext": "5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [], + "volume": 100, + "layerOrder": 3, + "visible": true, + "x": 0, + "y": 0, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + } + ], + "monitors": [], + "extensions": [], + "meta": { + "semver": "3.0.0", + "vm": "11.1.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" + } +} \ No newline at end of file diff --git a/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg b/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg new file mode 100644 index 0000000000000000000000000000000000000000..3a3908a99eb18cdeaacf1222caec50828656871e --- /dev/null +++ b/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg @@ -0,0 +1,29 @@ + + + + Slice 1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/83a9787d4cb6f3b7632b4ddfebf74367.wav b/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/83c36d806dc92327b9e7049a565c6bff.wav b/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/83c36d806dc92327b9e7049a565c6bff.wav new file mode 100644 index 0000000000000000000000000000000000000000..45742d5ef6f09d05b0f0788cb055ffe54abfd9ad Binary files /dev/null and b/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/83c36d806dc92327b9e7049a565c6bff.wav differ diff --git a/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/bcf454acf82e4504149f7ffe07081dbc.svg b/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/bcf454acf82e4504149f7ffe07081dbc.svg new file mode 100644 index 0000000000000000000000000000000000000000..03df23e29ad059d88e559d48bf5e2717870455f3 --- /dev/null +++ b/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/bcf454acf82e4504149f7ffe07081dbc.svg @@ -0,0 +1,42 @@ + + + + costume1.1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/e7c147730f19d284bcd7b3f00af19bb6.svg b/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/e7c147730f19d284bcd7b3f00af19bb6.svg new file mode 100644 index 0000000000000000000000000000000000000000..0326edc8bdd7da45b33bac907c15ae1c0d1026c1 --- /dev/null +++ b/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/e7c147730f19d284bcd7b3f00af19bb6.svg @@ -0,0 +1,19 @@ + + + + blue sky + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/project.json b/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/project.json new file mode 100644 index 0000000000000000000000000000000000000000..5b7c5415624acba340c66ce9486aca9a0052cc92 --- /dev/null +++ b/generated_projects/5b92bebe-637c-48ff-94a2-76dfe6b8245a/project.json @@ -0,0 +1,122 @@ +{ + "targets": [ + { + "isStage": true, + "name": "Stage", + "objName": "Stage", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Blue sky", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "e7c147730f19d284bcd7b3f00af19bb6", + "md5ext": "e7c147730f19d284bcd7b3f00af19bb6.svg", + "rotationCenterX": 240, + "rotationCenterY": 180 + } + ], + "sounds": [ + { + "name": "pop", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", + "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav" + } + ], + "volume": 100, + "layerOrder": 0, + "tempo": 60, + "videoTransparency": 50, + "videoState": "on", + "textToSpeechLanguage": null + }, + { + "isStage": false, + "name": "Cat", + "objName": "Cat", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Cat", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "bcf454acf82e4504149f7ffe07081dbc", + "md5ext": "bcf454acf82e4504149f7ffe07081dbc.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [ + { + "name": "meow", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83c36d806dc92327b9e7049a565c6bff", + "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav" + } + ], + "volume": 100, + "layerOrder": 2, + "visible": true, + "x": 0, + "y": 0, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + }, + { + "isStage": false, + "name": "ball", + "objName": "ball", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "ball", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "5d973d7a3a8be3f3bd6e1cd0f73c32b5", + "md5ext": "5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [], + "volume": 100, + "layerOrder": 3, + "visible": true, + "x": 0, + "y": 0, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + } + ], + "monitors": [], + "extensions": [], + "meta": { + "semver": "3.0.0", + "vm": "11.1.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" + } +} \ No newline at end of file diff --git a/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020.sb3 b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020.sb3 new file mode 100644 index 0000000000000000000000000000000000000000..18ead62e5ac7632743f2657b98d7770f16b5b3f8 Binary files /dev/null and b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020.sb3 differ diff --git a/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg new file mode 100644 index 0000000000000000000000000000000000000000..3a3908a99eb18cdeaacf1222caec50828656871e --- /dev/null +++ b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg @@ -0,0 +1,29 @@ + + + + Slice 1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/83a9787d4cb6f3b7632b4ddfebf74367.wav b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/83c36d806dc92327b9e7049a565c6bff.wav b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/83c36d806dc92327b9e7049a565c6bff.wav new file mode 100644 index 0000000000000000000000000000000000000000..45742d5ef6f09d05b0f0788cb055ffe54abfd9ad Binary files /dev/null and b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/83c36d806dc92327b9e7049a565c6bff.wav differ diff --git a/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/bcf454acf82e4504149f7ffe07081dbc.svg b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/bcf454acf82e4504149f7ffe07081dbc.svg new file mode 100644 index 0000000000000000000000000000000000000000..03df23e29ad059d88e559d48bf5e2717870455f3 --- /dev/null +++ b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/bcf454acf82e4504149f7ffe07081dbc.svg @@ -0,0 +1,42 @@ + + + + costume1.1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/e7c147730f19d284bcd7b3f00af19bb6.svg b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/e7c147730f19d284bcd7b3f00af19bb6.svg new file mode 100644 index 0000000000000000000000000000000000000000..0326edc8bdd7da45b33bac907c15ae1c0d1026c1 --- /dev/null +++ b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/e7c147730f19d284bcd7b3f00af19bb6.svg @@ -0,0 +1,19 @@ + + + + blue sky + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/project.json b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/project.json new file mode 100644 index 0000000000000000000000000000000000000000..08990e9cf8a9cf58ca0ba05a9a88b4a55e054c5a --- /dev/null +++ b/generated_projects/8f9aca5d-60b6-4ca3-b9c9-d69410033020/project.json @@ -0,0 +1,609 @@ +{ + "targets": [ + { + "isStage": true, + "name": "Stage", + "objName": "Stage", + "variables": { + "score_470bd82b": [ + "score", + "0" + ], + "\u2601 High Score_ccac98b5": [ + "\u2601 High Score", + "0", + true + ], + "lives_966dc602": [ + "lives", + "3" + ], + "speed_0a370303": [ + "speed", + "0" + ], + "distance_4afc231e": [ + "distance", + "0" + ] + }, + "lists": { + "recent_scores_c7e84343": [ + "recent_scores", + [] + ], + "obstacle_positions_0f75a667": [ + "obstacle_positions", + [] + ] + }, + "broadcasts": { + "Game Over_d58b8435": "Game Over", + "Game Start_bcc43148": "Game Start", + "Reset Game_235417e8": "Reset Game", + "Level Up_b611e9d2": "Level Up" + }, + "blocks": { + "7Qci0=F(x7SxMXYKTG-$": { + "opcode": "event_whenflagclicked", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "uF#bhmq9cj0fDUSCi88e", + "x": 0, + "y": 0 + }, + "uF#bhmq9cj0fDUSCi88e": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": { + "VARIABLE": [ + "score", + "score_470bd82b" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "7Qci0=F(x7SxMXYKTG-$", + "next": "o#e{40XdpOd@oZ1YpTA~" + }, + "o#e{40XdpOd@oZ1YpTA~": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "3" + ] + ] + }, + "fields": { + "VARIABLE": [ + "lives", + "lives" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "uF#bhmq9cj0fDUSCi88e", + "next": "jG6VuX_#=sK0YFX1d3%J" + }, + "jG6VuX_#=sK0YFX1d3%J": { + "opcode": "data_showvariable", + "inputs": {}, + "fields": { + "VARIABLE": [ + "score", + "score_470bd82b" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "o#e{40XdpOd@oZ1YpTA~", + "next": "-{=pH%C7P[=LqCRYgV$M" + }, + "-{=pH%C7P[=LqCRYgV$M": { + "opcode": "data_showvariable", + "inputs": {}, + "fields": { + "VARIABLE": [ + "lives", + "lives" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "jG6VuX_#=sK0YFX1d3%J", + "next": "_m[3d}3&qZ+2sosCnO=U" + }, + "_m[3d}3&qZ+2sosCnO=U": { + "opcode": "event_broadcast", + "inputs": { + "BROADCAST_INPUT": [ + 1, + [ + 11, + "Game Start", + "Game Start_bcc43148" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "-{=pH%C7P[=LqCRYgV$M", + "next": null + }, + "xpB)d^mjQO]77CAT8d{O": { + "opcode": "event_whenbroadcastreceived", + "inputs": {}, + "fields": { + "BROADCAST_OPTION": [ + "Game Over ", + "Game Over_d58b8435" + ] + }, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "&l)Q*Nsjrw+&zvJCbWjF", + "x": 0, + "y": 150 + }, + "&l)Q*Nsjrw+&zvJCbWjF": { + "opcode": "event_broadcast", + "inputs": { + "BROADCAST_INPUT": [ + 1, + [ + 11, + "Reset Game", + "Reset Game_235417e8" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "xpB)d^mjQO]77CAT8d{O", + "next": "vLH&+4b(G{(gXl}WZVeD" + }, + "vLH&+4b(G{(gXl}WZVeD": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": { + "VARIABLE": [ + "score", + "score_470bd82b" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "&l)Q*Nsjrw+&zvJCbWjF", + "next": "b[ir3JD}gbE#mMHGjcsn" + }, + "b[ir3JD}gbE#mMHGjcsn": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "3" + ] + ] + }, + "fields": { + "VARIABLE": [ + "lives", + "lives" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "vLH&+4b(G{(gXl}WZVeD", + "next": null + } + }, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Blue sky", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "e7c147730f19d284bcd7b3f00af19bb6", + "md5ext": "e7c147730f19d284bcd7b3f00af19bb6.svg", + "rotationCenterX": 240, + "rotationCenterY": 180 + } + ], + "sounds": [ + { + "name": "pop", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", + "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav" + } + ], + "volume": 100, + "layerOrder": 0, + "tempo": 60, + "videoTransparency": 50, + "videoState": "on", + "textToSpeechLanguage": null + }, + { + "isStage": false, + "name": "Cat", + "objName": "Cat", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": { + "{PdW6r7d=19qq]5]5vcz": { + "opcode": "event_whenflagclicked", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "Z9y_$hjX}N@Ab2]JoZH(", + "x": 0, + "y": 0 + }, + "Z9y_$hjX}N@Ab2]JoZH(": { + "opcode": "motion_gotoxy", + "inputs": { + "X": [ + 1, + [ + 4, + "0" + ] + ], + "Y": [ + 1, + [ + 4, + "-100" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "{PdW6r7d=19qq]5]5vcz", + "next": "z8_)+lXYUO!9VmMX@7jg" + }, + "z8_)+lXYUO!9VmMX@7jg": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": { + "VARIABLE": [ + "speed", + "speed" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "Z9y_$hjX}N@Ab2]JoZH(", + "next": null + }, + "7P3Ib{rY!1nj{dZ{3P^o": { + "opcode": "event_whenkeypressed", + "inputs": {}, + "fields": { + "KEY_OPTION": [ + "right arrow ", + null + ] + }, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "u~ee#Fefpymx#R%XAXKc", + "x": 0, + "y": 150 + }, + "u~ee#Fefpymx#R%XAXKc": { + "opcode": "motion_changexby", + "inputs": { + "DX": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "7P3Ib{rY!1nj{dZ{3P^o", + "next": null + }, + "h57F3RhRYARHen~IoQ1G": { + "opcode": "event_whenkeypressed", + "inputs": {}, + "fields": { + "KEY_OPTION": [ + "left arrow ", + null + ] + }, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "#4RgkgOwIJQu$@&h3C_5", + "x": 0, + "y": 300 + }, + "#4RgkgOwIJQu$@&h3C_5": { + "opcode": "motion_changexby", + "inputs": { + "DX": [ + 1, + [ + 4, + "-10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "h57F3RhRYARHen~IoQ1G", + "next": null + }, + "&wR5FT-cO([vd6CX&@6i": { + "opcode": "event_whenkeypressed", + "inputs": {}, + "fields": { + "KEY_OPTION": [ + "space ", + null + ] + }, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "I4-tqOY5Oy&Q^Aug%52A", + "x": 0, + "y": 450 + }, + "I4-tqOY5Oy&Q^Aug%52A": { + "opcode": "control_if", + "inputs": { + "CONDITION": [ + 2, + "operator_equals_1" + ], + "SUBSTACK": [ + 2, + "7o(g3-R-$+~r*d=d@coZ" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "&wR5FT-cO([vd6CX&@6i", + "next": null + }, + "7o(g3-R-$+~r*d=d@coZ": { + "opcode": "control_forever", + "inputs": { + "SUBSTACK": [ + 2, + "&EBjG{Rm~2*PXoaUOZ-J" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "I4-tqOY5Oy&Q^Aug%52A", + "next": null + }, + "&EBjG{Rm~2*PXoaUOZ-J": { + "opcode": "motion_changeyby", + "inputs": { + "DY": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "7o(g3-R-$+~r*d=d@coZ", + "next": "control_wait_1" + }, + "cNl0ryVZV^OYDALBMavq": { + "opcode": "motion_changeyby", + "inputs": { + "DY": [ + 1, + [ + 4, + "-10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "control_wait_1", + "next": "control_wait_2" + } + }, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Cat", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "bcf454acf82e4504149f7ffe07081dbc", + "md5ext": "bcf454acf82e4504149f7ffe07081dbc.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [ + { + "name": "meow", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83c36d806dc92327b9e7049a565c6bff", + "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav" + } + ], + "volume": 100, + "layerOrder": 2, + "visible": true, + "x": 0, + "y": -100, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + }, + { + "isStage": false, + "name": "ball", + "objName": "ball", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "ball", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "5d973d7a3a8be3f3bd6e1cd0f73c32b5", + "md5ext": "5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [], + "volume": 100, + "layerOrder": 3, + "visible": true, + "x": 0, + "y": 100, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + } + ], + "monitors": [ + { + "id": "monitor_123e4567", + "opcode": "data_variable", + "mode": "default", + "params": { + "VARIABLE": "score" + }, + "spriteName": "Stage", + "value": "0", + "visible": true, + "x": 5, + "y": 5, + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + }, + { + "id": "monitor_8f4e3210", + "opcode": "data_variable", + "mode": "default", + "params": { + "VARIABLE": "\u2601 High Score" + }, + "spriteName": "Stage", + "value": "0", + "visible": true, + "x": 325, + "y": 5, + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + }, + { + "id": "monitor_4d5e921f", + "opcode": "looks_backdropnumbername", + "mode": "default", + "params": { + "NUMBER_NAME": "number" + }, + "spriteName": "Stage", + "value": "1", + "visible": false, + "x": 5, + "y": 55, + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + }, + { + "id": "monitor_8f901234", + "opcode": "looks_costumenumbername", + "mode": "default", + "params": { + "NUMBER_NAME": "number" + }, + "spriteName": "Stage", + "value": "1", + "visible": false, + "x": 325, + "y": 55, + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + } + ], + "extensions": [], + "meta": { + "semver": "3.0.0", + "vm": "11.1.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" + } +} \ No newline at end of file diff --git a/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg b/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg new file mode 100644 index 0000000000000000000000000000000000000000..3a3908a99eb18cdeaacf1222caec50828656871e --- /dev/null +++ b/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg @@ -0,0 +1,29 @@ + + + + Slice 1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/83a9787d4cb6f3b7632b4ddfebf74367.wav b/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/83c36d806dc92327b9e7049a565c6bff.wav b/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/83c36d806dc92327b9e7049a565c6bff.wav new file mode 100644 index 0000000000000000000000000000000000000000..45742d5ef6f09d05b0f0788cb055ffe54abfd9ad Binary files /dev/null and b/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/83c36d806dc92327b9e7049a565c6bff.wav differ diff --git a/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/bcf454acf82e4504149f7ffe07081dbc.svg b/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/bcf454acf82e4504149f7ffe07081dbc.svg new file mode 100644 index 0000000000000000000000000000000000000000..03df23e29ad059d88e559d48bf5e2717870455f3 --- /dev/null +++ b/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/bcf454acf82e4504149f7ffe07081dbc.svg @@ -0,0 +1,42 @@ + + + + costume1.1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/e7c147730f19d284bcd7b3f00af19bb6.svg b/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/e7c147730f19d284bcd7b3f00af19bb6.svg new file mode 100644 index 0000000000000000000000000000000000000000..0326edc8bdd7da45b33bac907c15ae1c0d1026c1 --- /dev/null +++ b/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/e7c147730f19d284bcd7b3f00af19bb6.svg @@ -0,0 +1,19 @@ + + + + blue sky + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/project.json b/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/project.json new file mode 100644 index 0000000000000000000000000000000000000000..9b51bedee5a004927808c8d274a26201b4d87ebe --- /dev/null +++ b/generated_projects/91fd8375-563f-4bbc-a531-8ed5e8ff73cd/project.json @@ -0,0 +1,625 @@ +{ + "targets": [ + { + "isStage": true, + "name": "Stage", + "objName": "Stage", + "variables": { + "score_ae103e41": [ + "score", + "0" + ], + "\u2601 High Score_9a181c08": [ + "\u2601 High Score", + "0", + true + ], + "lives_ae9cb3c9": [ + "lives", + "3" + ], + "speed_add52876": [ + "speed", + "1" + ], + "jump_height_038177c8": [ + "jump_height", + "10" + ] + }, + "lists": { + "recent_scores_402c5bca": [ + "recent_scores", + [] + ], + "power_ups_8809d8c6": [ + "power_ups", + [] + ], + "obstacles_2f507374": [ + "obstacles", + [] + ] + }, + "broadcasts": { + "Game Over_6350a3f3": "Game Over", + "Game Start_4f341cfb": "Game Start", + "Reset Game_5468ad5f": "Reset Game", + "Level Up_0db289bf": "Level Up" + }, + "blocks": { + "kW+Mqd%D3Ql$FwoLtETC": { + "opcode": "event_whenflagclicked", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "{v=mfg8ui+]+r2b4(w7W", + "x": 0, + "y": 0 + }, + "{v=mfg8ui+]+r2b4(w7W": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": { + "VARIABLE": [ + "score", + "score" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "kW+Mqd%D3Ql$FwoLtETC", + "next": "#EHyvIS~[*HcnfIK}KXM" + }, + "#EHyvIS~[*HcnfIK}KXM": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "3" + ] + ] + }, + "fields": { + "VARIABLE": [ + "lives", + "lives" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "kW+Mqd%D3Ql$FwoLtETC", + "next": "gHKiy^jyRmT+FN[DyFmG" + }, + "gHKiy^jyRmT+FN[DyFmG": { + "opcode": "event_broadcast", + "inputs": { + "BROADCAST_INPUT": [ + 1, + [ + 11, + "Game Start", + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "kW+Mqd%D3Ql$FwoLtETC", + "next": null + }, + "ViwQbX&z}G0M-+JYJulF": { + "opcode": "event_whenbroadcastreceived", + "inputs": {}, + "fields": { + "BROADCAST_OPTION": [ + "Game Over ", + null + ] + }, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "u)gVoVFmg*C-hvhr-cjw", + "x": 0, + "y": 150 + }, + "u)gVoVFmg*C-hvhr-cjw": { + "opcode": "control_if", + "inputs": { + "CONDITION": [ + 2, + "6lvZUnc7IJ+rb=a+IaWN" + ], + "SUBSTACK": [ + 2, + "[aHs~&QA2Cshy~Y~bC5k" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "ViwQbX&z}G0M-+JYJulF", + "next": "VP4jWAB9nD{V5&=)yj7R" + }, + "6lvZUnc7IJ+rb=a+IaWN": { + "opcode": "operator_gt", + "inputs": { + "OPERAND1": [ + 3, + "data_variable_1", + [ + 10, + "" + ] + ], + "OPERAND2": [ + 3, + "data_variable_2", + [ + 10, + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "u)gVoVFmg*C-hvhr-cjw", + "next": null + }, + "[aHs~&QA2Cshy~Y~bC5k": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 3, + "data_variable_3", + [ + 10, + "" + ] + ] + }, + "fields": { + "VARIABLE": [ + "High Score", + "High Score" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "u)gVoVFmg*C-hvhr-cjw", + "next": null + }, + "VP4jWAB9nD{V5&=)yj7R": { + "opcode": "event_broadcast", + "inputs": { + "BROADCAST_INPUT": [ + 1, + [ + 11, + "Reset Game", + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "ViwQbX&z}G0M-+JYJulF", + "next": null + }, + "7p*JVe[*d[GlqtaNE-T7": { + "opcode": "event_whenbroadcastreceived", + "inputs": {}, + "fields": { + "BROADCAST_OPTION": [ + "Reset Game ", + null + ] + }, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "AtUt#FOb1o[CDif}${eY", + "x": 0, + "y": 300 + }, + "AtUt#FOb1o[CDif}${eY": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": { + "VARIABLE": [ + "score", + "score" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "7p*JVe[*d[GlqtaNE-T7", + "next": "wtuu@pRxyqpP2Qi})Y*i" + }, + "wtuu@pRxyqpP2Qi})Y*i": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "3" + ] + ] + }, + "fields": { + "VARIABLE": [ + "lives", + "lives" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "7p*JVe[*d[GlqtaNE-T7", + "next": null + } + }, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Blue sky", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "e7c147730f19d284bcd7b3f00af19bb6", + "md5ext": "e7c147730f19d284bcd7b3f00af19bb6.svg", + "rotationCenterX": 240, + "rotationCenterY": 180 + } + ], + "sounds": [ + { + "name": "button", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", + "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav" + } + ], + "volume": 100, + "layerOrder": 0, + "tempo": 60, + "videoTransparency": 50, + "videoState": "on", + "textToSpeechLanguage": null + }, + { + "isStage": false, + "name": "Cat", + "objName": "Cat", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": { + "1(BdEK^fa!!gKE4JjI()": { + "opcode": "event_whenflagclicked", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "mZG-2$M)c8GO+fy9h&zp", + "x": 0, + "y": 0 + }, + "mZG-2$M)c8GO+fy9h&zp": { + "opcode": "motion_gotoxy", + "inputs": { + "X": [ + 1, + [ + 4, + "0" + ] + ], + "Y": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "1(BdEK^fa!!gKE4JjI()", + "next": null + }, + "gFAT=T$7A!}pW@K1xSoM": { + "opcode": "event_whenkeypressed", + "inputs": {}, + "fields": { + "KEY_OPTION": [ + "space ", + null + ] + }, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "RnoMyK2Q)z2BtG{H7JaN", + "x": 0, + "y": 150 + }, + "RnoMyK2Q)z2BtG{H7JaN": { + "opcode": "control_if", + "inputs": { + "CONDITION": [ + 2, + "operator_equals_1" + ], + "SUBSTACK": [ + 2, + "iK[jP8L8z8)]b}i^GkDn" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "gFAT=T$7A!}pW@K1xSoM", + "next": null + }, + "iK[jP8L8z8)]b}i^GkDn": { + "opcode": "control_repeat", + "inputs": { + "TIMES": [ + 1, + [ + 4, + "10" + ] + ], + "SUBSTACK": [ + 2, + "zuJL+DUR9#&x-l-M[B$A" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "RnoMyK2Q)z2BtG{H7JaN", + "next": null + }, + "zuJL+DUR9#&x-l-M[B$A": { + "opcode": "motion_changeyby", + "inputs": { + "DY": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "iK[jP8L8z8)]b}i^GkDn", + "next": "6MUuIFY93ib8}B8@@lub" + }, + "6MUuIFY93ib8}B8@@lub": { + "opcode": "control_wait", + "inputs": { + "DURATION": [ + 1, + [ + 4, + "0.1" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "iK[jP8L8z8)]b}i^GkDn", + "next": null + }, + "!Yu%#hRei4p6#ZnC#v8a": { + "opcode": "motion_yposition", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "operator_equals_1", + "next": null + }, + "PpMp0}5eX2t3s[0HXtf7": { + "opcode": "event_whenthisspriteclicked", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "QZCr_CO+sl-lNhbKVV_a", + "x": 0, + "y": 300 + }, + "QZCr_CO+sl-lNhbKVV_a": { + "opcode": "event_broadcast", + "inputs": { + "BROADCAST_INPUT": [ + 1, + [ + 11, + "Game Over", + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "PpMp0}5eX2t3s[0HXtf7", + "next": null + } + }, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Cat", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "bcf454acf82e4504149f7ffe07081dbc", + "md5ext": "bcf454acf82e4504149f7ffe07081dbc.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [ + { + "name": "Meow", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83c36d806dc92327b9e7049a565c6bff", + "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav" + } + ], + "volume": 100, + "layerOrder": 2, + "visible": true, + "x": 0, + "y": -130, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + }, + { + "isStage": false, + "name": "Ball", + "objName": "Ball", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Ball", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "5d973d7a3a8be3f3bd6e1cd0f73c32b5", + "md5ext": "5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [], + "volume": 100, + "layerOrder": 3, + "visible": true, + "x": 0, + "y": 0, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + } + ], + "monitors": [ + { + "id": "monitor_1234567890", + "opcode": "data_variable", + "spriteName": "Stage", + "params": { + "VARIABLE": "score" + }, + "value": "0", + "x": 5, + "y": 5, + "visible": true, + "mode": "default", + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + }, + { + "id": "monitor_9876543210", + "opcode": "data_variable", + "spriteName": "Stage", + "params": { + "VARIABLE": "\u2601 High Score" + }, + "value": "0", + "x": 325, + "y": 5, + "visible": true, + "mode": "default", + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + }, + { + "id": "monitor_abcdefghijk", + "opcode": "data_variable", + "spriteName": "Stage", + "params": { + "VARIABLE": "lives" + }, + "value": "3", + "x": 5, + "y": 55, + "visible": true, + "mode": "default", + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + }, + { + "id": "monitor_abcdefghijklm", + "opcode": "looks_costumenumbername", + "spriteName": "Stage", + "params": { + "NUMBER_NAME": "number" + }, + "value": "1", + "x": 325, + "y": 55, + "visible": false, + "mode": "default", + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + } + ], + "extensions": [], + "meta": { + "semver": "3.0.0", + "vm": "11.1.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" + } +} \ No newline at end of file diff --git a/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048.sb3 b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048.sb3 new file mode 100644 index 0000000000000000000000000000000000000000..623766fb7671ab8716e75e1f124b0a055773a6d8 Binary files /dev/null and b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048.sb3 differ diff --git a/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg new file mode 100644 index 0000000000000000000000000000000000000000..3a3908a99eb18cdeaacf1222caec50828656871e --- /dev/null +++ b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg @@ -0,0 +1,29 @@ + + + + Slice 1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/83a9787d4cb6f3b7632b4ddfebf74367.wav b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/83c36d806dc92327b9e7049a565c6bff.wav b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/83c36d806dc92327b9e7049a565c6bff.wav new file mode 100644 index 0000000000000000000000000000000000000000..45742d5ef6f09d05b0f0788cb055ffe54abfd9ad Binary files /dev/null and b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/83c36d806dc92327b9e7049a565c6bff.wav differ diff --git a/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/bcf454acf82e4504149f7ffe07081dbc.svg b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/bcf454acf82e4504149f7ffe07081dbc.svg new file mode 100644 index 0000000000000000000000000000000000000000..03df23e29ad059d88e559d48bf5e2717870455f3 --- /dev/null +++ b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/bcf454acf82e4504149f7ffe07081dbc.svg @@ -0,0 +1,42 @@ + + + + costume1.1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/e7c147730f19d284bcd7b3f00af19bb6.svg b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/e7c147730f19d284bcd7b3f00af19bb6.svg new file mode 100644 index 0000000000000000000000000000000000000000..0326edc8bdd7da45b33bac907c15ae1c0d1026c1 --- /dev/null +++ b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/e7c147730f19d284bcd7b3f00af19bb6.svg @@ -0,0 +1,19 @@ + + + + blue sky + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/project.json b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/project.json new file mode 100644 index 0000000000000000000000000000000000000000..175f4dd3e266614cb91477090e6f6e6198b67128 --- /dev/null +++ b/generated_projects/b4abeb10-d2de-4b83-8715-cb2a0eeeb048/project.json @@ -0,0 +1,522 @@ +{ + "targets": [ + { + "isStage": true, + "name": "Stage", + "objName": "Stage", + "variables": { + "score_2c9060d0": [ + "score", + "0" + ], + "\u2601 High Score_c92bfa58": [ + "\u2601 High Score", + "0", + true + ], + "speed_00c4a9f2": [ + "speed", + "0" + ], + "jump_height_d5401f1e": [ + "jump_height", + "0" + ], + "obstacle_speed_361d7263": [ + "obstacle_speed", + "0" + ] + }, + "lists": { + "recent_scores_c34193a7": [ + "recent_scores", + [] + ], + "obstacle_positions_81758246": [ + "obstacle_positions", + [] + ], + "power_ups_5b8047e2": [ + "power_ups", + [] + ] + }, + "broadcasts": { + "Game Over_de9405f2": "Game Over", + "Game Start_f8419639": "Game Start", + "Jump_d96c0049": "Jump", + "Obstacle Created_ca2b358e": "Obstacle Created" + }, + "blocks": { + "9o99fD-lz{WhW[@_N~H(": { + "opcode": "event_whenflagclicked", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "parent": null, + "next": ")pH-{0*ccC]e&MD&^-{+", + "x": 0, + "y": 0 + }, + ")pH-{0*ccC]e&MD&^-{+": { + "opcode": "looks_switchbackdropto", + "inputs": { + "BACKDROP": [ + 1, + [ + 11, + "blue sky", + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "9o99fD-lz{WhW[@_N~H(", + "next": "435A92C7U1lrCo)a1Y5[" + }, + "(h7FKjM!*6JX73LFZ$J!": { + "opcode": "looks_backdrops", + "inputs": {}, + "fields": {}, + "shadow": true, + "topLevel": false, + "parent": ")pH-{0*ccC]e&MD&^-{+", + "next": null + }, + "435A92C7U1lrCo)a1Y5[": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": { + "VARIABLE": [ + "score", + "score" + ] + }, + "shadow": false, + "topLevel": false, + "parent": ")pH-{0*ccC]e&MD&^-{+", + "next": "aN+gqX*s*N2))~v8Mflw" + }, + "aN+gqX*s*N2))~v8Mflw": { + "opcode": "data_showvariable", + "inputs": {}, + "fields": { + "VARIABLE": [ + "score", + "score" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "435A92C7U1lrCo)a1Y5[", + "next": "}_6--kTG(&fMW_}&vZTO" + }, + "}_6--kTG(&fMW_}&vZTO": { + "opcode": "event_broadcast", + "inputs": { + "BROADCAST_INPUT": [ + 1, + [ + 11, + "Game Start", + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "aN+gqX*s*N2))~v8Mflw", + "next": null + }, + "9%#rp=FXc96IT0no@vcr": { + "opcode": "event_whenbroadcastreceived", + "inputs": {}, + "fields": { + "BROADCAST_OPTION": [ + "Game Over ", + null + ] + }, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "ya%f7WjR@qz9HM4~MP@C", + "x": 0, + "y": 150 + }, + "ya%f7WjR@qz9HM4~MP@C": { + "opcode": "event_broadcast", + "inputs": { + "BROADCAST_INPUT": [ + 1, + [ + 11, + "Game Over", + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "9%#rp=FXc96IT0no@vcr", + "next": "N(vVX*lZcUmiWcl=neMl" + }, + "N(vVX*lZcUmiWcl=neMl": { + "opcode": "looks_switchbackdropto", + "inputs": { + "BACKDROP": [ + 1, + [ + 11, + "Game Over", + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "ya%f7WjR@qz9HM4~MP@C", + "next": null + }, + "{QWkuvi*%y@)1Ig=HTNb": { + "opcode": "looks_backdrops", + "inputs": {}, + "fields": {}, + "shadow": true, + "topLevel": false, + "parent": "N(vVX*lZcUmiWcl=neMl", + "next": null + } + }, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Blue sky", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "e7c147730f19d284bcd7b3f00af19bb6", + "md5ext": "e7c147730f19d284bcd7b3f00af19bb6.svg", + "rotationCenterX": 240, + "rotationCenterY": 180 + } + ], + "sounds": [ + { + "name": "pop", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", + "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav" + } + ], + "volume": 100, + "layerOrder": 0, + "tempo": 60, + "videoTransparency": 50, + "videoState": "on", + "textToSpeechLanguage": null + }, + { + "isStage": false, + "name": "Cat", + "objName": "Cat", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": { + "_=sWdovM@oC@kll3%$7e": { + "opcode": "event_whenflagclicked", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "4kvgnE!k-A=b8Al_BYO)", + "x": 0, + "y": 0 + }, + "4kvgnE!k-A=b8Al_BYO)": { + "opcode": "motion_gotoxy", + "inputs": { + "X": [ + 1, + [ + 4, + "0" + ] + ], + "Y": [ + 1, + [ + 4, + "-120" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "_=sWdovM@oC@kll3%$7e", + "next": ")svf{nSdp{mwj6P[}t!Y" + }, + ")svf{nSdp{mwj6P[}t!Y": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": { + "VARIABLE": [ + "speed", + "speed" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "4kvgnE!k-A=b8Al_BYO)", + "next": null + }, + "@s5E!$C$Z-Kf8H79&2O4": { + "opcode": "event_whenkeypressed", + "inputs": {}, + "fields": { + "KEY_OPTION": [ + "space ", + null + ] + }, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "n2cM0=pP#(~d}WGe{)2*", + "x": 0, + "y": 150 + }, + "n2cM0=pP#(~d}WGe{)2*": { + "opcode": "control_if", + "inputs": { + "CONDITION": [ + 2, + "operator_equals_1" + ], + "SUBSTACK": [ + 2, + "Zg=bU]4Da+yu6o(Hcl@]" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "@s5E!$C$Z-Kf8H79&2O4", + "next": null + }, + "Zg=bU]4Da+yu6o(Hcl@]": { + "opcode": "control_forever", + "inputs": { + "SUBSTACK": [ + 2, + "J3r^O@Mdj#s#o&p&=9-y" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "n2cM0=pP#(~d}WGe{)2*", + "next": null + }, + "QQ5E&v)ox1quBit@43yd": { + "opcode": "control_wait", + "inputs": { + "DURATION": [ + 1, + [ + 4, + "0.1" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "J3r^O@Mdj#s#o&p&=9-y", + "next": "z%-3=aL_Tq_N+gQs~)5}" + }, + "J3r^O@Mdj#s#o&p&=9-y": { + "opcode": "motion_changeyby", + "inputs": { + "DY": [ + 1, + [ + 4, + "10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "Zg=bU]4Da+yu6o(Hcl@]", + "next": "QQ5E&v)ox1quBit@43yd" + }, + "z%-3=aL_Tq_N+gQs~)5}": { + "opcode": "motion_changeyby", + "inputs": { + "DY": [ + 1, + [ + 4, + "-10" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "QQ5E&v)ox1quBit@43yd", + "next": null + } + }, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Cat", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "bcf454acf82e4504149f7ffe07081dbc", + "md5ext": "bcf454acf82e4504149f7ffe07081dbc.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [ + { + "name": "meow", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83c36d806dc92327b9e7049a565c6bff", + "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav" + } + ], + "volume": 100, + "layerOrder": 2, + "visible": true, + "x": 0, + "y": -120, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + }, + { + "isStage": false, + "name": "ball", + "objName": "ball", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "ball", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "5d973d7a3a8be3f3bd6e1cd0f73c32b5", + "md5ext": "5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [], + "volume": 100, + "layerOrder": 3, + "visible": true, + "x": 130, + "y": 0, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + } + ], + "monitors": [ + { + "id": "monitor_1234567890", + "opcode": "data_variable", + "mode": "default", + "params": { + "VARIABLE": "score" + }, + "spriteName": "Stage", + "value": "0", + "visible": true, + "x": 5, + "y": 5, + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + }, + { + "id": "monitor_9876543210", + "opcode": "data_variable", + "mode": "default", + "params": { + "VARIABLE": "\u2601 High Score" + }, + "spriteName": "Stage", + "value": "0", + "visible": true, + "x": 325, + "y": 5, + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + }, + { + "id": "monitor_abcdefghijk", + "opcode": "looks_costumenumbername", + "mode": "default", + "params": { + "NUMBER_NAME": "number" + }, + "spriteName": "Stage", + "value": "1", + "visible": false, + "x": 5, + "y": 55, + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + } + ], + "extensions": [], + "meta": { + "semver": "3.0.0", + "vm": "11.1.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" + } +} \ No newline at end of file diff --git a/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0.sb3 b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0.sb3 new file mode 100644 index 0000000000000000000000000000000000000000..c4208b763e3e728cf85b1c804b47e374592b5755 Binary files /dev/null and b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0.sb3 differ diff --git a/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg new file mode 100644 index 0000000000000000000000000000000000000000..3a3908a99eb18cdeaacf1222caec50828656871e --- /dev/null +++ b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg @@ -0,0 +1,29 @@ + + + + Slice 1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/83a9787d4cb6f3b7632b4ddfebf74367.wav b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/83c36d806dc92327b9e7049a565c6bff.wav b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/83c36d806dc92327b9e7049a565c6bff.wav new file mode 100644 index 0000000000000000000000000000000000000000..45742d5ef6f09d05b0f0788cb055ffe54abfd9ad Binary files /dev/null and b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/83c36d806dc92327b9e7049a565c6bff.wav differ diff --git a/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/bcf454acf82e4504149f7ffe07081dbc.svg b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/bcf454acf82e4504149f7ffe07081dbc.svg new file mode 100644 index 0000000000000000000000000000000000000000..03df23e29ad059d88e559d48bf5e2717870455f3 --- /dev/null +++ b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/bcf454acf82e4504149f7ffe07081dbc.svg @@ -0,0 +1,42 @@ + + + + costume1.1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/e7c147730f19d284bcd7b3f00af19bb6.svg b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/e7c147730f19d284bcd7b3f00af19bb6.svg new file mode 100644 index 0000000000000000000000000000000000000000..0326edc8bdd7da45b33bac907c15ae1c0d1026c1 --- /dev/null +++ b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/e7c147730f19d284bcd7b3f00af19bb6.svg @@ -0,0 +1,19 @@ + + + + blue sky + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/project.json b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/project.json new file mode 100644 index 0000000000000000000000000000000000000000..e868268165c1da27c246d33441bbb7fb8ba59632 --- /dev/null +++ b/generated_projects/f9e2fddb-5c0c-44e4-b5e0-09b5914202e0/project.json @@ -0,0 +1,483 @@ +{ + "targets": [ + { + "isStage": true, + "name": "Stage", + "objName": "Stage", + "variables": { + "score_024c3763": [ + "score", + "0" + ], + "\u2601 High Score_b22705f5": [ + "\u2601 High Score", + "0", + true + ], + "speed_a92a0476": [ + "speed", + "0" + ], + "lives_10d3fa54": [ + "lives", + "3" + ], + "level_0988b57b": [ + "level", + "1" + ] + }, + "lists": { + "recent_scores_5797d07b": [ + "recent_scores", + [] + ], + "obstacle_sizes_b6423236": [ + "obstacle_sizes", + [] + ], + "obstacle_speeds_4db4e284": [ + "obstacle_speeds", + [] + ] + }, + "broadcasts": { + "Game Over_875b3a7d": "Game Over", + "Game Start_54418de0": "Game Start", + "Jump_2870b1e1": "Jump", + "Obstacle Appears_060e6453": "Obstacle Appears" + }, + "blocks": { + "9^lJ9r4N4{M5AJNpM[i[": { + "opcode": "event_whenflagclicked", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "!w@QEvK9s5U-^_H!kGq[", + "x": 0, + "y": 0 + }, + "!w@QEvK9s5U-^_H!kGq[": { + "opcode": "looks_switchbackdropto", + "inputs": { + "BACKDROP": [ + 1, + [ + 11, + "blue sky", + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "9^lJ9r4N4{M5AJNpM[i[", + "next": "B8u{5)A-lAXI(XBG{K$i" + }, + "A8^B5vi=zs{eH^NUGIRA": { + "opcode": "looks_backdrops", + "inputs": {}, + "fields": {}, + "shadow": true, + "topLevel": false, + "parent": "!w@QEvK9s5U-^_H!kGq[", + "next": null + }, + "B8u{5)A-lAXI(XBG{K$i": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": { + "VARIABLE": [ + "score", + "score" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "!w@QEvK9s5U-^_H!kGq[", + "next": "p}_&3&NX*0kmb0Hz#mPA" + }, + "p}_&3&NX*0kmb0Hz#mPA": { + "opcode": "event_broadcast", + "inputs": { + "BROADCAST_INPUT": [ + 1, + [ + 11, + "Game Start", + "" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "B8u{5)A-lAXI(XBG{K$i", + "next": null + } + }, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Blue sky", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "e7c147730f19d284bcd7b3f00af19bb6", + "md5ext": "e7c147730f19d284bcd7b3f00af19bb6.svg", + "rotationCenterX": 240, + "rotationCenterY": 180 + } + ], + "sounds": [ + { + "name": "pop", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", + "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav" + } + ], + "volume": 100, + "layerOrder": 0, + "tempo": 60, + "videoTransparency": 50, + "videoState": "on", + "textToSpeechLanguage": null + }, + { + "isStage": false, + "name": "Cat", + "objName": "Cat", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": { + "s_SW_vL7T#y!oe]L8isH": { + "opcode": "event_whenflagclicked", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "z&6IC@$q!}gK(qpQs7!#", + "x": 0, + "y": 0 + }, + "z&6IC@$q!}gK(qpQs7!#": { + "opcode": "motion_gotoxy", + "inputs": { + "X": [ + 1, + [ + 4, + "0" + ] + ], + "Y": [ + 1, + [ + 4, + "-120" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "s_SW_vL7T#y!oe]L8isH", + "next": "~nNRaob_BM^D3Gg^zD3h" + }, + "~nNRaob_BM^D3Gg^zD3h": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "3" + ] + ] + }, + "fields": { + "VARIABLE": [ + "lives", + "lives" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "z&6IC@$q!}gK(qpQs7!#", + "next": "dA5D@vOf)xz&5wT=A*hG" + }, + "dA5D@vOf)xz&5wT=A*hG": { + "opcode": "data_showvariable", + "inputs": {}, + "fields": { + "VARIABLE": [ + "lives", + "lives" + ] + }, + "shadow": false, + "topLevel": false, + "parent": "~nNRaob_BM^D3Gg^zD3h", + "next": null + }, + "e5=^ZbAdStdg1rg+XoMH": { + "opcode": "event_whenkeypressed", + "inputs": {}, + "fields": { + "KEY_OPTION": [ + "space ", + null + ] + }, + "shadow": false, + "topLevel": true, + "parent": null, + "next": "Y9![AH4mXt*lgEZWj2J$", + "x": 0, + "y": 150 + }, + "Y9![AH4mXt*lgEZWj2J$": { + "opcode": "control_if", + "inputs": { + "CONDITION": [ + 2, + "operator_equals_1" + ], + "SUBSTACK": [ + 2, + "%PslWrUx(DSeRWanYvs{" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "e5=^ZbAdStdg1rg+XoMH", + "next": null + }, + "%PslWrUx(DSeRWanYvs{": { + "opcode": "control_repeat", + "inputs": { + "TIMES": [ + 1, + [ + 4, + "10" + ] + ], + "SUBSTACK": [ + 2, + "XX+TgRjy#$DVeV6Bn*^$" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "Y9![AH4mXt*lgEZWj2J$", + "next": null + }, + "XX+TgRjy#$DVeV6Bn*^$": { + "opcode": "motion_changeyby", + "inputs": { + "DY": [ + 1, + [ + 4, + "20" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "%PslWrUx(DSeRWanYvs{", + "next": "control_wait_1" + }, + "kNK@t2uUw(XW9#O7+j@Q": { + "opcode": "motion_changeyby", + "inputs": { + "DY": [ + 1, + [ + 4, + "-20" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "parent": "control_wait_1", + "next": "control_wait_2" + } + }, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "Cat", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "bcf454acf82e4504149f7ffe07081dbc", + "md5ext": "bcf454acf82e4504149f7ffe07081dbc.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [ + { + "name": "meow", + "dataFormat": "wav", + "rate": 44100, + "sampleCount": 0, + "assetId": "83c36d806dc92327b9e7049a565c6bff", + "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav" + } + ], + "volume": 100, + "layerOrder": 2, + "visible": true, + "x": 0, + "y": -120, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + }, + { + "isStage": false, + "name": "ball", + "objName": "ball", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "ball", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "5d973d7a3a8be3f3bd6e1cd0f73c32b5", + "md5ext": "5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg", + "rotationCenterX": 0, + "rotationCenterY": 0 + } + ], + "sounds": [], + "volume": 100, + "layerOrder": 3, + "visible": true, + "x": 130, + "y": 0, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + } + ], + "monitors": [ + { + "id": "monitor_1234567890", + "opcode": "data_variable", + "spriteName": "Stage", + "params": { + "VARIABLE": "score" + }, + "value": "0", + "x": 5, + "y": 5, + "visible": true, + "mode": "default", + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + }, + { + "id": "monitor_8d55f432-e421-4daf-85f7-325145f210d2", + "opcode": "data_variable", + "spriteName": "Stage", + "params": { + "VARIABLE": "\u2601 High Score" + }, + "value": "0", + "x": 325, + "y": 5, + "visible": true, + "mode": "default", + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + }, + { + "id": "monitor_4e5d6f7a8b9c", + "opcode": "data_variable", + "spriteName": "Stage", + "params": { + "VARIABLE": "speed" + }, + "value": "0", + "x": 5, + "y": 55, + "visible": true, + "mode": "default", + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + }, + { + "id": "monitor_ffffffffffffffff", + "opcode": "looks_backdropnumbername", + "spriteName": "Stage", + "params": { + "NUMBER_NAME": "number" + }, + "value": "1", + "x": 325, + "y": 55, + "visible": false, + "mode": "default", + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + }, + { + "id": "monitor_fffffffffffff", + "opcode": "looks_costumenumbername", + "spriteName": "Stage", + "params": { + "NUMBER_NAME": "number" + }, + "value": "1", + "x": 5, + "y": 105, + "visible": false, + "mode": "default", + "sliderMin": 0, + "sliderMax": 100, + "isDiscrete": true + } + ], + "extensions": [], + "meta": { + "semver": "3.0.0", + "vm": "11.1.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" + } +} \ No newline at end of file diff --git a/generated_projects/test_1.sb3 b/generated_projects/test_1.sb3 new file mode 100644 index 0000000000000000000000000000000000000000..8f2190036239e649fb6140cffe003e0197a01646 Binary files /dev/null and b/generated_projects/test_1.sb3 differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..efa3b050e5c364238dbef68f6fa0d77c2e4cc25b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,151 @@ +gradio +requests +accelerate +agent +aiohappyeyeballs +aiohttp +aiosignal +annotated-types +anyio +arxiv +asttokens +attrs +beautifulsoup4 +bidict +blinker +bs4 +certifi +charset-normalizer +click +colorama +comm +dataclasses-json +debugpy +decorator +distro +dnspython +duckduckgo_search +duckduckgo-search +eventlet +executing +faiss-cpu +ffmpeg-python +filelock +Flask +Flask-SocketIO +frozenlist +fsspec +greenlet +groq +gunicorn +h11 +httpcore +httpx +httpx-sse +huggingface-hub +idna +ipykernel +ipython +ipython_pygments_lexers +itsdangerous +jedi +Jinja2 +joblib +jsonpatch +jsonpointer +jupyter_client +jupyter_core +langchain +langchain-community +langchain-core +langchain_experimental +langchain-groq +langchain-mistralai +langchain-pinecone +langchain-text-splitters +langgraph +langgraph-checkpoint +langgraph-prebuilt +langgraph-sdk +langsmith +lxml +markdown2 +MarkupSafe +marshmallow +matplotlib-inline +mpmath +multidict +mypy_extensions +nest-asyncio +networkx +numexpr +numpy +orjson +ormsgpack +pandas +packaging +parso +pillow +platformdirs +playwright +primp +prompt_toolkit +propcache +psutil +pure_eval +pydantic +pydantic-settings +pydantic_core +pinecone +pinecone-client +pyee +Pygments +PyMuPDF +PyPDF2 +pytesseract +python-dateutil +python-dotenv +python-engineio +python-socketio +PyYAML +pyzmq +regex +requests +requests-toolbelt +safetensors +scikit-learn +scipy +sentence-transformers +setuptools +simple-websocket +six +sniffio +soupsieve +SQLAlchemy +stack-data +sympy +tenacity +threadpoolctl +tokenizers +torch +tornado +tqdm +traitlets +transformers +typing-inspect +typing-inspection +typing_extensions +urllib3 +wcwidth +Werkzeug +wsproto +wikipedia +xxhash +yarl +zstandard +youtube-transcript-api +pytube +newspaper3k +SpeechRecognition +python-docx +lxml_html_clean \ No newline at end of file diff --git a/static/assets/backdrops/e7c147730f19d284bcd7b3f00af19bb6.svg b/static/assets/backdrops/e7c147730f19d284bcd7b3f00af19bb6.svg new file mode 100644 index 0000000000000000000000000000000000000000..0326edc8bdd7da45b33bac907c15ae1c0d1026c1 --- /dev/null +++ b/static/assets/backdrops/e7c147730f19d284bcd7b3f00af19bb6.svg @@ -0,0 +1,19 @@ + + + + blue sky + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/assets/backdrops/ee99a4b49761fe861db0753d43918c68.svg b/static/assets/backdrops/ee99a4b49761fe861db0753d43918c68.svg new file mode 100644 index 0000000000000000000000000000000000000000..0f164e20a6cba69da457b5ad359684a2be0e4875 --- /dev/null +++ b/static/assets/backdrops/ee99a4b49761fe861db0753d43918c68.svg @@ -0,0 +1 @@ +Game Over \ No newline at end of file diff --git a/static/assets/backdrops/ef9973bcff6d4cbc558e946028ec7d23.png b/static/assets/backdrops/ef9973bcff6d4cbc558e946028ec7d23.png new file mode 100644 index 0000000000000000000000000000000000000000..0f3dacdde1a33a9f00ff9cfe06c7e5c16b6dae75 --- /dev/null +++ b/static/assets/backdrops/ef9973bcff6d4cbc558e946028ec7d23.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f38afeaf3d4edab9a2162227c8d4b9df0bdda753adf7a98d6cea90931ceb1bab +size 120024 diff --git a/static/assets/sounds/1727f65b5f22d151685b8e5917456a60.wav b/static/assets/sounds/1727f65b5f22d151685b8e5917456a60.wav new file mode 100644 index 0000000000000000000000000000000000000000..f5be3a1606476bfbee8a4321f41de6a1a0ead520 Binary files /dev/null and b/static/assets/sounds/1727f65b5f22d151685b8e5917456a60.wav differ diff --git a/static/assets/sounds/83a9787d4cb6f3b7632b4ddfebf74367.wav b/static/assets/sounds/83a9787d4cb6f3b7632b4ddfebf74367.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc3b2724a9c7cfef378eeb65499d44236ad2add8 Binary files /dev/null and b/static/assets/sounds/83a9787d4cb6f3b7632b4ddfebf74367.wav differ diff --git a/static/assets/sounds/83c36d806dc92327b9e7049a565c6bff.wav b/static/assets/sounds/83c36d806dc92327b9e7049a565c6bff.wav new file mode 100644 index 0000000000000000000000000000000000000000..45742d5ef6f09d05b0f0788cb055ffe54abfd9ad Binary files /dev/null and b/static/assets/sounds/83c36d806dc92327b9e7049a565c6bff.wav differ diff --git a/static/assets/sprites/0fb9be3e8397c983338cb71dc84d0b25.svg b/static/assets/sprites/0fb9be3e8397c983338cb71dc84d0b25.svg new file mode 100644 index 0000000000000000000000000000000000000000..5ff997fd11132a3505e71ce46f5e14e34dbb4430 --- /dev/null +++ b/static/assets/sprites/0fb9be3e8397c983338cb71dc84d0b25.svg @@ -0,0 +1,42 @@ + + + + costume2.1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/assets/sprites/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg b/static/assets/sprites/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg new file mode 100644 index 0000000000000000000000000000000000000000..3a3908a99eb18cdeaacf1222caec50828656871e --- /dev/null +++ b/static/assets/sprites/5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg @@ -0,0 +1,29 @@ + + + + Slice 1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/assets/sprites/bcf454acf82e4504149f7ffe07081dbc.svg b/static/assets/sprites/bcf454acf82e4504149f7ffe07081dbc.svg new file mode 100644 index 0000000000000000000000000000000000000000..03df23e29ad059d88e559d48bf5e2717870455f3 --- /dev/null +++ b/static/assets/sprites/bcf454acf82e4504149f7ffe07081dbc.svg @@ -0,0 +1,42 @@ + + + + costume1.1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/app_index.html b/templates/app_index.html new file mode 100644 index 0000000000000000000000000000000000000000..770a29ab649e70f5f50cb1f4687f99ceff2c6f2f --- /dev/null +++ b/templates/app_index.html @@ -0,0 +1,93 @@ + + + + + + Upload PDF for Sprite Extraction + + + + +

Upload PDF for Image Extraction and Captioning

+
+ + +
+ +
+
+ + + + + \ No newline at end of file diff --git a/templates/index4.html b/templates/index4.html new file mode 100644 index 0000000000000000000000000000000000000000..796fe02b34c88649902ea32a3c87057edabd23db --- /dev/null +++ b/templates/index4.html @@ -0,0 +1,227 @@ + + + + + + Scratch Game JSON Generator + + + +

Scratch Game JSON Generator

+ +
+
+ +

Backdrops

+
+ + +

Sprites

+
+
+ + + +

Output JSON:

+
Waiting for input...
+ + + + \ No newline at end of file diff --git a/uploads/databases/employee.db b/uploads/databases/employee.db new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/uploads/documents/LLM_based_QA_chatbot_builder.pdf b/uploads/documents/LLM_based_QA_chatbot_builder.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8a134504e0acff8a78e78f1a0c56671c6041b307 --- /dev/null +++ b/uploads/documents/LLM_based_QA_chatbot_builder.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:633e305a9a7fcdc69769e77d96db1fa715ab387c9f8745ebe77fa75590772848 +size 1887485 diff --git a/uploads/images/how-ML-pipeline-Work.png b/uploads/images/how-ML-pipeline-Work.png new file mode 100644 index 0000000000000000000000000000000000000000..fdb2dfba26d782ec6228703f6366fdd73ddeab56 Binary files /dev/null and b/uploads/images/how-ML-pipeline-Work.png differ diff --git a/utils/__pycache__/block_relation_builder.cpython-312.pyc b/utils/__pycache__/block_relation_builder.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..830683c2a86e983f69223d65fd959e6d1163c2d4 --- /dev/null +++ b/utils/__pycache__/block_relation_builder.cpython-312.pyc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c686438dd391592343c2837c7eb952141313d9a15e99bc26f3783b1385a9f91 +size 111900 diff --git a/utils/action_node.py b/utils/action_node.py new file mode 100644 index 0000000000000000000000000000000000000000..2ceb5afe86d992bb0d657fa39b3faa47f3e26edc --- /dev/null +++ b/utils/action_node.py @@ -0,0 +1,452 @@ +import json +import uuid +import logging +from typing import Dict, Any, List + +# Assume GameState is a TypedDict or similar for clarity +# from typing import TypedDict +# class GameState(TypedDict): +# description: str +# project_json: Dict[str, Any] +# action_plan: Dict[str, Any] +# sprite_initial_positions: Dict[str, Any] + +# Placeholder for actual GameState in your application +GameState = Dict[str, Any] + +logger = logging.getLogger(__name__) + +# --- Mock Agent for demonstration --- +class MockAgent: + def invoke(self, payload: Dict[str, Any]) -> Dict[str, Any]: + """ + Mocks an LLM agent invocation. In a real scenario, this would call your + actual LLM API (e.g., through LangChain, LlamaIndex, etc.). + """ + user_message = payload["messages"][-1]["content"] + print(f"\n--- Mock Agent Received Prompt (partial) ---\n{user_message[:500]}...\n------------------------------------------") + + # Simplified mock responses for demonstration purposes + # In a real scenario, the LLM would generate actual Scratch block JSON + if "Propose a high-level action flow" in user_message: + return { + "messages": [{ + "content": json.dumps({ + "action_overall_flow": { + "Sprite1": { + "description": "Basic movement and interaction", + "plans": [ + { + "event": "when flag clicked", + "logic": "forever loop: move 10 steps, if touching Edge then turn 15 degrees" + }, + { + "event": "when space key pressed", + "logic": "say Hello! for 2 seconds" + } + ] + }, + "Ball": { + "description": "Simple bouncing behavior", + "plans": [ + { + "event": "when flag clicked", + "logic": "move 5 steps, if on edge bounce" + } + ] + } + } + }) + }] + } + elif "You are an AI assistant generating Scratch 3.0 block JSON" in user_message: + # This mock response is highly simplified. A real LLM would generate + # valid Scratch blocks based on the provided relevant catalog and plan. + # We're just demonstrating the *mechanism* of filtering the catalog. + if "Sprite1" in user_message: + return { + "messages": [{ + "content": json.dumps({ + f"block_id_{generate_block_id()}": { + "opcode": "event_whenflagclicked", + "next": f"block_id_{generate_block_id()}_forever", + "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, "x": 100, "y": 100 + }, + f"block_id_{generate_block_id()}_forever": { + "opcode": "control_forever", + "next": None, + "parent": f"block_id_{generate_block_id()}", + "inputs": { + "SUBSTACK": [2, f"block_id_{generate_block_id()}_move"] + }, "fields": {}, "shadow": False, "topLevel": False + }, + f"block_id_{generate_block_id()}_move": { + "opcode": "motion_movesteps", + "next": f"block_id_{generate_block_id()}_if", + "parent": f"block_id_{generate_block_id()}_forever", + "inputs": { + "STEPS": [1, [4, "10"]] + }, "fields": {}, "shadow": False, "topLevel": False + }, + f"block_id_{generate_block_id()}_if": { + "opcode": "control_if", + "next": None, + "parent": f"block_id_{generate_block_id()}_forever", + "inputs": { + "CONDITION": [2, f"block_id_{generate_block_id()}_touching"], + "SUBSTACK": [2, f"block_id_{generate_block_id()}_turn"] + }, "fields": {}, "shadow": False, "topLevel": False + }, + f"block_id_{generate_block_id()}_touching": { + "opcode": "sensing_touchingobject", + "next": None, + "parent": f"block_id_{generate_block_id()}_if", + "inputs": { + "TOUCHINGOBJECTMENU": [1, f"block_id_{generate_block_id()}_touching_menu"] + }, "fields": {}, "shadow": False, "topLevel": False + }, + f"block_id_{generate_block_id()}_touching_menu": { + "opcode": "sensing_touchingobjectmenu", + "next": None, + "parent": f"block_id_{generate_block_id()}_touching", + "inputs": {}, + "fields": {"TOUCHINGOBJECTMENU": ["_edge_", None]}, + "shadow": True, "topLevel": False + }, + f"block_id_{generate_block_id()}_turn": { + "opcode": "motion_turnright", + "next": None, + "parent": f"block_id_{generate_block_id()}_if", + "inputs": { + "DEGREES": [1, [4, "15"]] + }, "fields": {}, "shadow": False, "topLevel": False + }, + f"block_id_{generate_block_id()}_say": { + "opcode": "looks_sayforsecs", + "next": None, + "parent": None, # This block would typically be part of a separate script + "inputs": { + "MESSAGE": [1, [10, "Hello!"]], + "SECS": [1, [4, "2"]] + }, "fields": {}, "shadow": False, "topLevel": True, "x": 300, "y": 100 + } + }) + }] + } + elif "Ball" in user_message: + return { + "messages": [{ + "content": json.dumps({ + f"block_id_{generate_block_id()}": { + "opcode": "event_whenflagclicked", + "next": f"block_id_{generate_block_id()}_moveball", + "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, "x": 100, "y": 100 + }, + f"block_id_{generate_block_id()}_moveball": { + "opcode": "motion_movesteps", + "next": f"block_id_{generate_block_id()}_edgebounce", + "parent": f"block_id_{generate_block_id()}", + "inputs": { + "STEPS": [1, [4, "5"]] + }, "fields": {}, "shadow": False, "topLevel": False + }, + f"block_id_{generate_block_id()}_edgebounce": { + "opcode": "motion_ifonedgebounce", + "next": None, + "parent": f"block_id_{generate_block_id()}_moveball", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + } + }) + }] + } + return {"messages": [{"content": "[]"}]} # Default empty response + +agent = MockAgent() + +# Helper function to generate a unique block ID +def generate_block_id(): + return str(uuid.uuid4())[:10].replace('-', '') # Shorten for readability, ensure uniqueness + +# Placeholder for your extract_json_from_llm_response function +def extract_json_from_llm_response(response_string): + try: + # Assuming the LLM response is ONLY the JSON string within triple backticks + json_match = response_string.strip().replace("```json", "").replace("```", "").strip() + return json.loads(json_match) + except json.JSONDecodeError as e: + logger.error(f"Failed to decode JSON from LLM response: {e}") + logger.error(f"Raw response: {response_string}") + raise ValueError("Invalid JSON response from LLM") + +# --- GLOBAL CATALOG OF ALL SCRATCH BLOCKS --- +# This is where you would load your block_content.json +# For demonstration, I'm using your provided snippets and adding some common ones. +# In a real application, you'd load this once at startup. +ALL_SCRATCH_BLOCKS_CATALOG = { + "motion_movesteps": { + "opcode": "motion_movesteps", "next": None, "parent": None, + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, "x": 464, "y": -416 + }, + "motion_turnright": { + "opcode": "motion_turnright", "next": None, "parent": None, + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True, "x": 467, "y": -316 + }, + "motion_ifonedgebounce": { + "opcode": "motion_ifonedgebounce", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, "x": 467, "y": -316 + }, + "event_whenflagclicked": { + "opcode": "event_whenflagclicked", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, "x": 10, "y": 10 + }, + "event_whenkeypressed": { + "opcode": "event_whenkeypressed", "next": None, "parent": None, + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": False, "topLevel": True, "x": 10, "y": 10 + }, + "control_forever": { + "opcode": "control_forever", "next": None, "parent": None, + "inputs": {"SUBSTACK": [2, "some_id"]}, "fields": {}, "shadow": False, "topLevel": True, "x": 10, "y": 10 + }, + "control_if": { + "opcode": "control_if", "next": None, "parent": None, + "inputs": {"CONDITION": [2, "some_id"], "SUBSTACK": [2, "some_id_2"]}, "fields": {}, "shadow": False, "topLevel": True, "x": 10, "y": 10 + }, + "looks_sayforsecs": { + "opcode": "looks_sayforsecs", "next": None, "parent": None, + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True, "x": 10, "y": 10 + }, + "looks_say": { + "opcode": "looks_say", "next": None, "parent": None, + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True, "x": 10, "y": 10 + }, + "sensing_touchingobject": { + "opcode": "sensing_touchingobject", "next": None, "parent": None, + "inputs": {"TOUCHINGOBJECTMENU": [1, "some_id"]}, "fields": {}, "shadow": False, "topLevel": True, "x": 10, "y": 10 + }, + "sensing_touchingobjectmenu": { + "opcode": "sensing_touchingobjectmenu", "next": None, "parent": None, + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": True, "x": 10, "y": 10 + }, + # Add more blocks from your block_content.json here... +} + +# --- Heuristic-based block selection --- +def get_relevant_blocks_for_plan(action_plan: Dict[str, Any], all_blocks_catalog: Dict[str, Any]) -> Dict[str, Any]: + """ + Analyzes the natural language action plan and selects relevant Scratch blocks + from the comprehensive catalog. This is a heuristic approach and might need + to be refined based on your specific use cases and LLM capabilities. + """ + relevant_opcodes = set() + + # Always include common event blocks + relevant_opcodes.add("event_whenflagclicked") + relevant_opcodes.add("event_whenkeypressed") # Could be more specific if key is mentioned + + # Keyword to opcode mapping (can be expanded) + keyword_map = { + "move": "motion_movesteps", + "steps": "motion_movesteps", + "turn": "motion_turnright", + "rotate": "motion_turnright", + "bounce": "motion_ifonedgebounce", + "edge": "motion_ifonedgebounce", + "forever": "control_forever", + "loop": "control_forever", + "if": "control_if", + "condition": "control_if", + "say": "looks_say", + "hello": "looks_say", # Simple example, might need more context + "touching": "sensing_touchingobject", + "mouse pointer": "sensing_touchingobjectmenu", + "edge": "sensing_touchingobjectmenu", # For touching edge + } + + # Iterate through the action plan to find keywords + for sprite_name, sprite_actions in action_plan.get("action_overall_flow", {}).items(): + for plan in sprite_actions.get("plans", []): + event_logic = plan.get("event", "").lower() + " " + plan.get("logic", "").lower() + + # Check for direct opcode matches (if the LLM somehow outputs opcodes in its plan) + for opcode in all_blocks_catalog.keys(): + if opcode in event_logic: + relevant_opcodes.add(opcode) + + # Check for keywords + for keyword, opcode in keyword_map.items(): + if keyword in event_logic: + relevant_opcodes.add(opcode) + # Add associated shadow blocks if known + if opcode == "sensing_touchingobject": + relevant_opcodes.add("sensing_touchingobjectmenu") + if opcode == "event_whenkeypressed": + relevant_opcodes.add("event_whenkeypressed") # It's already there but good to be explicit + + # Construct the filtered catalog + relevant_blocks_catalog = { + opcode: all_blocks_catalog[opcode] + for opcode in relevant_opcodes if opcode in all_blocks_catalog + } + return relevant_blocks_catalog + +# --- New Action Planning Node --- +def plan_sprite_actions(state: GameState): + logger.info("--- Running PlanSpriteActionsNode ---") + + planning_prompt = ( + f"You are an AI assistant tasked with planning Scratch 3.0 block code for a game. " + f"The game description is: '{state['description']}'.\n\n" + 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" + f"Initial positions: {json.dumps(state.get('sprite_initial_positions', {}), indent=2)}\n\n" + f"Consider the main actions and interactions required for each sprite. " + f"Think step-by-step about what each sprite needs to *do*, *when* it needs to do it (events), " + f"and if any actions need to *repeat* or depend on *conditions*.\n\n" + f"Propose a high-level action flow for each sprite in the following JSON format. " + f"Do NOT generate Scratch block JSON yet. Only describe the logic using natural language or simplified pseudo-code.\n\n" + f"Example format:\n" + f"```json\n" + f"{{\n" + f" \"action_overall_flow\": {{\n" + f" \"Sprite1\": {{\n" + f" \"description\": \"Main character actions\",\n" + f" \"plans\": [\n" + f" {{\n" + f" \"event\": \"when flag clicked\",\n" + f" \"logic\": \"forever loop: move 10 steps, if on edge bounce\"\n" + f" }},\n" + f" {{\n" + f" \"event\": \"when space key pressed\",\n" + f" \"logic\": \"change y by 10, wait 0.1 seconds, change y by -10\"\n" + f" }}\n" + f" ]\n" + f" }},\n" + f" \"Ball\": {{\n" + f" \"description\": \"Projectile movement\",\n" + f" \"plans\": [\n" + f" {{\n" + f" \"event\": \"when I start as a clone\",\n" + f" \"logic\": \"glide 1 sec to random position, if touching Sprite1 then stop this script\"\n" + f" }}\n" + f" ]\n" + f" }}\n" + f" }}\n" + f"}}\n" + f"```\n\n" + f"Return ONLY the JSON object for the action overall flow." + ) + + try: + response = agent.invoke({"messages": [{"role": "user", "content": planning_prompt}]}) + raw_response = response["messages"][-1].content + print("Raw response from LLM [PlanSpriteActionsNode]:", raw_response) + action_plan = extract_json_from_llm_response(raw_response) + logger.info("Sprite action plan generated by PlanSpriteActionsNode.") + return {"action_plan": action_plan} + except Exception as e: + logger.error(f"Error in PlanSpriteActionsNode: {e}") + raise + +# --- Updated Action Node Builder (to consume the plan and build blocks) --- +def build_action_nodes(state: GameState): + logger.info("--- Running ActionNodeBuilder ---") + + action_plan = state.get("action_plan", {}) + if not action_plan: + raise ValueError("No action plan found in state. Run PlanSpriteActionsNode first.") + + # Convert the Scratch project JSON to a mutable Python object + project_json = state["project_json"] + targets = project_json["targets"] + + # We need a way to map sprite names to their actual target objects in project_json + sprite_map = {target["name"]: target for target in targets if not target["isStage"]} + + # --- NEW: Get only the relevant blocks for the entire action plan --- + relevant_scratch_blocks_catalog = get_relevant_blocks_for_plan(action_plan, ALL_SCRATCH_BLOCKS_CATALOG) + logger.info(f"Filtered {len(relevant_scratch_blocks_catalog)} relevant blocks out of {len(ALL_SCRATCH_BLOCKS_CATALOG)} total.") + + + # Iterate through the planned actions for each sprite + for sprite_name, sprite_actions in action_plan.get("action_overall_flow", {}).items(): + if sprite_name in sprite_map: + current_sprite_target = sprite_map[sprite_name] + # Ensure 'blocks' field exists for the sprite + if "blocks" not in current_sprite_target: + current_sprite_target["blocks"] = {} + + # Generate block JSON based on the detailed action plan for this sprite + # This is where the LLM's role becomes crucial: translating logic to blocks + llm_block_generation_prompt = ( + f"You are an AI assistant generating Scratch 3.0 block JSON based on a provided plan. " + f"The current sprite is '{sprite_name}'.\n" + f"Its planned actions are:\n" + f"```json\n{json.dumps(sprite_actions, indent=2)}\n```\n\n" + f"Here is a **curated catalog of only the most relevant Scratch 3.0 blocks** for this plan:\n" + f"```json\n{json.dumps(relevant_scratch_blocks_catalog, indent=2)}\n```\n\n" + f"Current Scratch project JSON (for context, specifically this sprite's existing blocks if any):\n" + f"```json\n{json.dumps(current_sprite_target, indent=2)}\n```\n\n" + f"**Instructions:**\n" + f"1. For each planned event and its associated logic, generate the corresponding Scratch 3.0 block JSON.\n" + f"2. **Generate unique block IDs** for every new block. Use a format like 'block_id_abcdef12'.\n" # Updated ID format hint + f"3. Properly link blocks using `next` and `parent` fields to form execution stacks. Hat blocks (`topLevel: true`, `parent: null`).\n" + 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" + 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" + 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" + 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" + f"8. Return ONLY the **updated 'blocks' dictionary** for this specific sprite. Do NOT return the full project JSON. ONLY the `blocks` dictionary." + ) + + try: + response = agent.invoke({"messages": [{"role": "user", "content": llm_block_generation_prompt}]}) + raw_response = response["messages"][-1].content + print(f"Raw response from LLM [ActionNodeBuilder - {sprite_name}]:", raw_response) + generated_blocks = extract_json_from_llm_response(raw_response) + current_sprite_target["blocks"].update(generated_blocks) # Merge new blocks + logger.info(f"Action blocks added for sprite '{sprite_name}' by ActionNodeBuilder.") + except Exception as e: + logger.error(f"Error generating blocks for sprite '{sprite_name}': {e}") + # Depending on robustness needed, you might continue or re-raise + raise + + return {"project_json": project_json} + +# --- Example Usage (to demonstrate the flow) --- +if __name__ == "__main__": + # Initialize a mock game state + initial_game_state = { + "description": "A simple game where a sprite moves and says hello.", + "project_json": { + "targets": [ + {"isStage": True, "name": "Stage", "blocks": {}}, + {"isStage": False, "name": "Sprite1", "blocks": {}}, + {"isStage": False, "name": "Ball", "blocks": {}} + ] + }, + "sprite_initial_positions": {} + } + + # Step 1: Plan Sprite Actions + try: + state_after_planning = plan_sprite_actions(initial_game_state) + initial_game_state.update(state_after_planning) + print("\n--- Game State After Planning ---") + print(json.dumps(initial_game_state, indent=2)) + except Exception as e: + print(f"Planning failed: {e}") + exit() + + # Step 2: Build Action Nodes (Generate Blocks) + try: + state_after_building = build_action_nodes(initial_game_state) + initial_game_state.update(state_after_building) + print("\n--- Game State After Building Blocks ---") + # Print only the blocks for a specific sprite to keep output manageable + for target in initial_game_state["project_json"]["targets"]: + if not target["isStage"]: + print(f"\nBlocks for {target['name']}:") + print(json.dumps(target.get('blocks', {}), indent=2)) + + except Exception as e: + print(f"Building blocks failed: {e}") \ No newline at end of file diff --git a/utils/action_plan_sample.txt b/utils/action_plan_sample.txt new file mode 100644 index 0000000000000000000000000000000000000000..c7ed075065ca7a8d7cf26c9adce6fd384267f40f --- /dev/null +++ b/utils/action_plan_sample.txt @@ -0,0 +1,1577 @@ +First sample: + +{ + 'Stage': { + 'description': 'Background and global game state management, including broadcasts, rewards, and score.', + 'plans': [ + { + 'event': 'event_whenflagclicked', + '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]', + 'motion': [ + + ], + 'control': [ + + ], + 'operator': [ + + ], + 'sensing': [ + + ], + 'looks': [ + 'looks_switchbackdropto' + ], + 'sounds': [ + + ], + 'events': [ + 'event_broadcast' + ], + 'data': [ + 'data_setvariableto', + 'data_showvariable' + ], + 'opcode_counts': [ + { + 'opcode': 'event_whenflagclicked', + 'count': 1 + }, + { + 'opcode': 'looks_switchbackdropto', + 'count': 1 + }, + { + 'opcode': 'data_setvariableto', + 'count': 1 + }, + { + 'opcode': 'data_showvariable', + 'count': 1 + }, + { + 'opcode': 'event_broadcast', + 'count': 1 + } + ] + }, + { + 'event': 'event_whenbroadcastreceived', + '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]', + 'motion': [ + + ], + 'control': [ + 'control_if' + ], + 'operator': [ + 'operator_gt' + ], + 'sensing': [ + + ], + 'looks': [ + 'looks_switchbackdropto' + ], + 'sounds': [ + + ], + 'events': [ + + ], + 'data': [ + 'data_setvariableto' + ], + 'opcode_counts': [ + { + 'opcode': 'event_whenbroadcastreceived', + 'count': 1 + }, + { + 'opcode': 'control_if', + 'count': 1 + }, + { + 'opcode': 'operator_gt', + 'count': 1 + }, + { + 'opcode': 'data_setvariableto', + 'count': 1 + }, + { + 'opcode': 'looks_switchbackdropto', + 'count': 1 + } + ] + }, + { + 'event': 'event_whenbroadcastreceived', + 'logic': 'when I receive [Level Up v]\n change [level v] by 1\n set [ballSpeed v] to (ballSpeed * 1.1)', + 'motion': [ + + ], + 'control': [ + + ], + 'operator': [ + 'operator_multiply' + ], + 'sensing': [ + + ], + 'looks': [ + + ], + 'sounds': [ + + ], + 'events': [ + + ], + 'data': [ + 'data_changevariableby', + 'data_setvariableto' + ], + 'opcode_counts': [ + { + 'opcode': 'event_whenbroadcastreceived', + 'count': 1 + }, + { + 'opcode': 'data_changevariableby', + 'count': 1 + }, + { + 'opcode': 'data_setvariableto', + 'count': 1 + }, + { + 'opcode': 'operator_multiply', + 'count': 1 + } + ] + } + ] + }, + 'Cat': { + 'description': 'Main character (cat) actions', + 'plans': [ + { + 'event': 'event_whenflagclicked', + 'logic': 'when green flag clicked\n go to x: 0 y: -100\n forever\n if then\n jump\n end\n move 5 steps\n if then\n broadcast [Game Over v]\n end\n end', + 'motion': [ + 'motion_movesteps', + 'motion_gotoxy' + ], + 'control': [ + 'control_forever', + 'control_if' + ], + 'operator': [ + + ], + 'sensing': [ + 'sensing_keypressed', + 'sensing_touchingobject' + ], + 'looks': [ + + ], + 'sounds': [ + + ], + 'events': [ + 'event_broadcast' + ], + 'data': [ + + ], + 'opcode_counts': [ + { + 'opcode': 'event_whenflagclicked', + 'count': 1 + }, + { + 'opcode': 'motion_gotoxy', + 'count': 1 + }, + { + 'opcode': 'control_forever', + 'count': 1 + }, + { + 'opcode': 'control_if', + 'count': 2 + }, + { + 'opcode': 'sensing_keypressed', + 'count': 1 + }, + { + 'opcode': 'sensing_touchingobject', + 'count': 1 + }, + { + 'opcode': 'motion_movesteps', + 'count': 1 + }, + { + 'opcode': 'event_broadcast', + 'count': 1 + } + ] + }, + { + 'event': 'event_whenkeypressed', + 'logic': 'when [space v] key pressed\n change y by 10\n wait 0.2 seconds\n change y by -10', + 'motion': [ + 'motion_changeyby' + ], + 'control': [ + 'control_wait' + ], + 'operator': [ + + ], + 'sensing': [ + + ], + 'looks': [ + + ], + 'sounds': [ + + ], + 'events': [ + + ], + 'data': [ + + ], + 'opcode_counts': [ + { + 'opcode': 'event_whenkeypressed', + 'count': 1 + }, + { + 'opcode': 'motion_changeyby', + 'count': 2 + }, + { + 'opcode': 'control_wait', + 'count': 1 + } + ] + } + ] + }, + 'Ball': { + 'description': 'Obstacle movement and interaction', + 'plans': [ + { + 'event': 'event_whenflagclicked', + '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 then\n broadcast [Game Over v]\n end\n end', + 'motion': [ + 'motion_glidesecstoxy', + 'motion_xposition' + ], + 'control': [ + 'control_forever' + ], + 'operator': [ + 'operator_random' + ], + 'sensing': [ + 'sensing_touchingobject' + ], + 'looks': [ + + ], + 'sounds': [ + + ], + 'events': [ + 'event_broadcast' + ], + 'data': [ + + ], + 'opcode_counts': [ + { + 'opcode': 'event_whenflagclicked', + 'count': 1 + }, + { + 'opcode': 'motion_gotoxy', + 'count': 1 + }, + { + 'opcode': 'motion_glidesecstoxy', + 'count': 1 + }, + { + 'opcode': 'operator_random', + 'count': 1 + }, + { + 'opcode': 'control_forever', + 'count': 1 + }, + { + 'opcode': 'sensing_touchingobject', + 'count': 1 + }, + { + 'opcode': 'event_broadcast', + 'count': 1 + } + ] + } + ] + } +} + +second sample + +[Refined Action Plan]: { + "Stage": { + "description": "Background and global game state management, including broadcasts, rewards, and score.", + "plans": [ + { + "event": "event_whenflagclicked", + "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]", + "control": [], + "operator": [], + "sensing": [], + "looks": [], + "sounds": [], + "events": [ + "event_broadcast" + ], + "data": [ + "data_setvariableto" + ] + }, + { + "event": "event_whenbroadcastreceived", + "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]", + "control": [ + "control_if" + ], + "operator": [ + "operator_gt" + ], + "sensing": [], + "looks": [], + "sounds": [], + "events": [ + "event_broadcast" + ], + "data": [ + "data_setvariableto" + ] + }, + { + "event": "event_whenbroadcastreceived", + "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", + "control": [], + "operator": [], + "sensing": [], + "looks": [], + "sounds": [], + "events": [ + "event_broadcast" + ], + "data": [ + "data_setvariableto" + ] + } + ] + }, + "Cat": { + "description": "Main character (cat) actions", + "plans": [ + { + "event": "event_whenflagclicked", + "logic": "when green flag clicked\n go to x: 0 y: -130\n forever\n if then\n jump\n end\n move 5 steps\n end", + "motion": [ + "motion_movesteps", + "motion_setx", + "motion_sety" + ], + "control": [ + "control_forever", + "control_if" + ], + "operator": [], + "sensing": [ + "sensing_keypressed" + ], + "looks": [], + "sounds": [], + "events": [], + "data": [] + }, + { + "event": "event_whenkeypressed", + "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", + "control": [ + "control_repeat", + "control_if" + ], + "operator": [], + "sensing": [], + "looks": [], + "sounds": [], + "events": [], + "data": [] + } + ] + }, + "Ball": { + "description": "Obstacle movement and interaction", + "plans": [ + { + "event": "event_whenflagclicked", + "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 + set x to 240\n end\n if then\n broadcast [Game Over v]\n end\n end", + "motion": [ + "motion_gotoxy", + "motion_glidesecstoxy", + "motion_xposition", + "motion_setx" + ], + "control": [ + "control_forever", + "control_if" + ], + "operator": [ + "operator_lt" + ], + "sensing": [ + "sensing_istouching" + ], + "looks": [], + "sounds": [], + "events": [ + "event_broadcast" + ], + "data": [] + } + ] + } +} + + +sample 3: +{ + 'Stage': { + 'description': 'Background and global game state management, including broadcasts, rewards, and score.', + 'plans': [ + { + 'event': 'event_whenflagclicked', + '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]', + 'motion': [ + + ], + 'control': [ + + ], + 'operator': [ + + ], + 'sensing': [ + + ], + 'looks': [ + 'looks_switchbackdropto' + ], + 'sounds': [ + + ], + 'events': [ + 'event_broadcast' + ], + 'data': [ + 'data_setvariableto', + 'data_showvariable' + ], + 'opcode_counts': [ + { + 'opcode': 'event_whenflagclicked', + 'count': 1 + }, + { + 'opcode': 'data_setvariableto', + 'count': 3 + }, + { + 'opcode': 'looks_switchbackdropto', + 'count': 1 + }, + { + 'opcode': 'event_broadcast', + 'count': 1 + } + ] + }, + { + 'event': 'event_whenbroadcastreceived', + '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]', + 'motion': [ + + ], + 'control': [ + 'control_if' + ], + 'operator': [ + 'operator_gt' + ], + 'sensing': [ + + ], + 'looks': [ + 'looks_switchbackdropto' + ], + 'sounds': [ + + ], + 'events': [ + + ], + 'data': [ + 'data_setvariableto' + ], + 'opcode_counts': [ + { + 'opcode': 'event_whenbroadcastreceived', + 'count': 1 + }, + { + 'opcode': 'control_if', + 'count': 1 + }, + { + 'opcode': 'operator_gt', + 'count': 1 + }, + { + 'opcode': 'data_setvariableto', + 'count': 1 + }, + { + 'opcode': 'looks_switchbackdropto', + 'count': 1 + } + ] + } + ] + }, + 'Cat': { + 'description': 'Main character (cat) actions', + 'plans': [ + { + 'event': 'event_whenflagclicked', + 'logic': 'when green flag clicked\n go to x: 0 y: -130\n set [shield v] to 0', + 'motion': [ + 'motion_gotoxy' + ], + 'control': [ + + ], + 'operator': [ + + ], + 'sensing': [ + + ], + 'looks': [ + + ], + 'sounds': [ + + ], + 'events': [ + + ], + 'data': [ + + ], + 'opcode_counts': [ + { + 'opcode': 'event_whenflagclicked', + 'count': 1 + }, + { + 'opcode': 'motion_gotoxy', + 'count': 1 + }, + { + 'opcode': 'data_setvariableto', + 'count': 1 + } + ] + }, + { + 'event': 'event_whenkeypressed', + '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', + 'motion': [ + 'motion_changeyby' + ], + 'control': [ + 'control_repeat', + 'control_wait', + 'control_if' + ], + 'operator': [ + + ], + 'sensing': [ + + ], + 'looks': [ + + ], + 'sounds': [ + + ], + 'events': [ + + ], + 'data': [ + + ], + 'opcode_counts': [ + { + 'opcode': 'event_whenkeypressed', + 'count': 1 + }, + { + 'opcode': 'control_if', + 'count': 1 + }, + { + 'opcode': 'control_repeat', + 'count': 1 + }, + { + 'opcode': 'control_wait', + 'count': 1 + }, + { + 'opcode': 'motion_changeyby', + 'count': 2 + } + ] + }, + { + 'event': 'event_whenbroadcastreceived', + 'logic': 'when I receive [Power-Up v]\n if <(Power-Up) = [Shield v]> then\n set [shield v] to 1\n end', + 'motion': [ + + ], + 'control': [ + + ], + 'operator': [ + + ], + 'sensing': [ + + ], + 'looks': [ + + ], + 'sounds': [ + + ], + 'events': [ + + ], + 'data': [ + + ], + 'opcode_counts': [ + { + 'opcode': 'event_whenbroadcastreceived', + 'count': 1 + }, + { + 'opcode': 'control_if', + 'count': 1 + }, + { + 'opcode': 'operator_eq', + 'count': 1 + }, + { + 'opcode': 'data_setvariableto', + 'count': 1 + } + ] + } + ] + }, + 'Ball': { + 'description': 'Obstacle movement and interaction', + 'plans': [ + { + 'event': 'event_whenflagclicked', + '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 then\n broadcast [Game Over v]\n end\n end', + 'motion': [ + 'motion_changexby', + 'motion_setx' + ], + 'control': [ + 'control_forever', + 'control_if' + ], + 'operator': [ + + ], + 'sensing': [ + 'sensing_touchingobject' + ], + 'looks': [ + + ], + 'sounds': [ + + ], + 'events': [ + 'event_broadcast' + ], + 'data': [ + + ], + 'opcode_counts': [ + { + 'opcode': 'event_whenflagclicked', + 'count': 1 + }, + { + 'opcode': 'motion_gotoxy', + 'count': 1 + }, + { + 'opcode': 'motion_changexby', + 'count': 1 + }, + { + 'opcode': 'motion_setx', + 'count': 2 + }, + { + 'opcode': 'control_forever', + 'count': 1 + }, + { + 'opcode': 'control_if', + 'count': 2 + }, + { + 'opcode': 'sensing_touchingobject', + 'count': 1 + }, + { + 'opcode': 'event_broadcast', + 'count': 1 + } + ] + } + ] + } +} + + +refinement_prompt = f""" +You are an expert in Scratch 3.0 game development, specializing in understanding block relationships (stacked, nested). +Review the following plan for '{target_name}' triggered by '{event}'. + +Game Description: +{game_description} + +--- Scratch 3.0 Block Reference --- + ### Hat Blocks + Description: {hat_description} + Blocks: + {hat_opcodes_functionalities} + + ### Boolean Blocks + Description: {boolean_description} + Blocks: + {boolean_opcodes_functionalities} + + ### C Blocks + Description: {c_description} + Blocks: + {c_opcodes_functionalities} + + ### Cap Blocks + Description: {cap_description} + Blocks: + {cap_opcodes_functionalities} + + ### Reporter Blocks + Description: {reporter_description} + Blocks: + {reporter_opcodes_functionalities} + + ### Stack Blocks + Description: {stack_description} + Blocks: + {stack_opcodes_functionalities} +----------------------------------- +Current Plan Details: +- Event (Hat Block Opcode): {event} +- Overall plans made on {target_name} are in the list: {overall_plans_in_sprite} +- Associated Opcodes by Category: {json.dumps(opcodes, indent=2)} +- Reference code, functionality, and logic script to check: {combined_blocks} +- Current Logic Description to Update if required: "{original_logic}" + +Your task is to: +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. + +2. **Structural requirements**: + - **Numeric values** `(e.g., 0, 5, 0.2, -130)` **must** be in parentheses: `(0)`, `(5)`, `(0.2)`, `(-130)`. + - **AlphaNumeric values** `(e.g., hello, say 5, 4, hi!)` **must** be in parentheses: `(hello)`, `(say 5)`, `(4)`, `(hi!)`. + - **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])`. + - **Dropdown options** must be in the form `[option v]` (e.g., `[Game Start v]`, `[blue sky v]`). example use `when [space v] key pressed`. + - **Reporter blocks** used as inputs must be double‑wrapped: `((x position))`, `((y position))`. example use `if <((y position)) = (-130)> then` or `(((x position)) * (1))`. + - **Boolean blocks** in conditions must be inside `< >`, including nested ones: `>`, `< and >`,`< or >`. + - **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]?>`. + - **Operator expressions** must use explicit Scratch operator blocks, e.g.: + ``` + (([ballSpeed v]) * (1.1)) + ``` + - **Every hat block script must end** with a final `end` on its own line. + +3. **Pseudo‑code formatting**: + - Represent each block or nested block on its own line. + - Indent nested blocks by 4 spaces under their parent (`forever`, `if`, etc.). + - No comments or explanatory text—just the block sequence. + - 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. + +4. **Logic content**: + - Build clear flow for mechanics (movement, jumping, flying, scoring, collisions). + - Match each action closely to a Scratch block or tight sequence. + - Do **NOT** include any justification or comments—only the raw logic. + +5. **Examples for reference**: +**Correct** pattern for a simple start script: + ``` + when green flag clicked + switch backdrop to [blue sky v] + set [score v] to (0) + show variable [score v] + broadcast [Game Start v] + end + ``` +**Correct** pattern for updating the high score variable handling: + ``` + when I receive [Game Over v] + if <((score)) > (([High Score v]))> then + set [High Score v] to ([score v]) + end + switch backdrop to [Game Over v] + end + ``` +**Correct** pattern for level up and increase difficulty use: + ``` + when I receive [Level Up v] + change [level v] by (1) + set [ballSpeed v] to ((([ballSpeed v]) * (1.1))) + end + ``` +**Correct** pattern for jumping mechanics use: + ``` + when [space v] key pressed + if <((y position)) = (-100)> then + repeat (5) + change y by (100) + wait (0.1) seconds + change y by (-100) + wait (0.1) seconds + end + end + end + ``` +**Correct** pattern for continuos moving objects use: + ``` + when green flag clicked + go to x: (240) y: (-100) + set [speed v] to (-5) + show variable [speed v] + forever + change x by ([speed v]) + if <((x position)) < (-240)> then + go to x: (240) y: (-100) + end + end + end + ``` +**Correct** pattern for continuos moving objects use: + ``` + when green flag clicked + go to x: (240) y: (-100) + set [speed v] to (-5) + show variable [speed v] + forever + change x by ([speed v]) + if <((x position)) < (-240)> then + go to x: (240) y: (-100) + end + end + end + ``` +6. **Donot** add any explaination of logic or comments to justify or explain just put the logic content in the json. +7. **Output**: +Return **only** a JSON object, using double quotes everywhere: +```json +{{ + "refined_logic": "…your fully‑formatted pseudo‑code here…" +}} +``` +""" + + +refinement_prompt = f""" +You are an expert in Scratch 3.0 game development, specializing in understanding block relationships (stacked, nested). +Review the following plan for '{target_name}' triggered by '{event}'. + +Game Description: +{game_description} + +--- Scratch 3.0 Block Reference --- + ### Hat Blocks + Description: {hat_description} + Blocks: + {hat_opcodes_functionalities} + + ### Boolean Blocks + Description: {boolean_description} + Blocks: + {boolean_opcodes_functionalities} + + ### C Blocks + Description: {c_description} + Blocks: + {c_opcodes_functionalities} + + ### Cap Blocks + Description: {cap_description} + Blocks: + {cap_opcodes_functionalities} + + ### Reporter Blocks + Description: {reporter_description} + Blocks: + {reporter_opcodes_functionalities} + + ### Stack Blocks + Description: {stack_description} + Blocks: + {stack_opcodes_functionalities} +----------------------------------- +Current Plan Details: +- Event (Hat Block Opcode): {event} +- Overall plans made on {target_name} are in the list: {overall_plans_in_sprite} +- Associated Opcodes by Category: {json.dumps(opcodes, indent=2)} +- Reference code and functionality and logics script for to check is script correct: {combined_blocks} +- Current Logic Description to Update if required: "{original_logic}" + +Your task is to: +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. +2. The logic should build flow which can correctly describe motion or flow for e.g jumping, running, flying and other mechanics. +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. +4 The understading of the block generation and important instruction: + some understanding of pseudo-code as given: + logic: + " + when green flag clicked + go to x: (240) y: (-135) + set [score v] to (1) + set [speed v] to (1) + show variable [score v] + show variable [speed v] + forever + glide (2) seconds to x: (-240) y: (-135) + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end + end + " + * **Block Value Types:** + + * `[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. + * `[option v]` (e.g., `[space v]` in `when [space v] key pressed`): Denotes a **dropdown option** selected within a block. + * `(10)`: Denotes a **numeric value** input into a block, like in `move (50)`. + * `(hello)`: Denotes an **alphanumeric string value** input into a block, like in `say (hello)`. + * `([level v])`: Denotes a **variable used as an input value** for another block, like in `set x to ([level v])`. + * `((x position))`: Denotes an **inbuilt reporter block** used as an input value, like in `go to ((x position))`. + * `(input)`: Parentheses `()` can enclose a value, a variable, or another block that serves as an input. + * ``: Angle brackets `<>` enclose a **Boolean block**. + * Nested Boolean blocks: Some Boolean blocks can contain other Boolean blocks, for example, `>`, `< and >`, or `< or >`. + * **Structuring Pseudo-code:** + * **Indentation:** Use consistent indentation to show block nesting (e.g., blocks inside a `forever` loop or an `if` statement). + * **Stacking:** Blocks that are stacked sequentially should be on new lines without additional indentation. + * **Comments:** Do not include comments or explanations within the pseudo-code itself; the 'logic' should be self-explanatory based on Scratch block structure. + [Note: This understanding will let you know about the pseudo-code generation.] +5. Few Example of content of logics inside for a specific plan: + - example 1[continuos moving objects]: " + when green flag clicked + go to x: (240) y: (-100) + set [speed v] to (-5) + show variable [speed v] + forever + change x by ([speed v]) + if <((x position)) < (-240)> then + go to x: (240) y: (-100) + end + end + end + " + - example 2[jumping script of an plan]: " + when [space v] key pressed + if <((y position)) = (-100)> then + repeat (5) + change y by (100) + wait (0.1) seconds + change y by (-100) + wait (0.1) seconds + end + end + end + " + - example 3[score decrement on collision]: " + when green flag clicked + set [score v] to (0) + forever + if then + change [score v] by (-1) + wait (0.1) seconds + end + end + end + end + " +6. **Donot** add any explaination of logic or comments to justify or explain just put the logic content in the json. +7. Output a JSON object and [NOTE: use double quates always ""]: + - `refined_logic`: The refined logic string. +Output ONLY the JSON object. +""" + + +[Overall Action Plan received at the block generator]: { + "Stage": { + "description": "Background and global game state management, including broadcasts, rewards, and score.", + "plans": [ + { + "event": "event_whenflagclicked", + "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]", + "motion": [], + "control": [], + "operator": [], + "sensing": [], + "looks": [], + "sounds": [], + "events": [ + "event_broadcast" + ], + "data": [ + "data_setvariableto", + "data_showvariable" + ], + "opcode_counts": [ + { + "opcode": "event_whenflagclicked", + "count": 1 + }, + { + "opcode": "data_setvariableto", + "count": 2 + }, + { + "opcode": "data_showvariable", + "count": 2 + }, + { + "opcode": "event_broadcast", + "count": 1 + } + ] + }, + { + "event": "event_whenbroadcastreceived", + "logic": "when I receive [Game Over v]\n broadcast [Reset Game v]\n set [score v] to (0)\n set [lives v] to (3)", + "motion": [], + "control": [], + "operator": [], + "sensing": [], + "looks": [], + "sounds": [], + "events": [], + "data": [ + "data_setvariableto" + ], + "opcode_counts": [ + { + "opcode": "event_whenbroadcastreceived", + "count": 1 + }, + { + "opcode": "event_broadcast", + "count": 1 + }, + { + "opcode": "data_setvariableto", + "count": 2 + } + ] + } + ] + }, + "Cat": { + "description": "Main character (cat) actions", + "plans": [ + { + "event": "event_whenflagclicked", + "logic": "when green flag clicked\n go to x: (0) y: (-100)\n set [speed v] to (0)", + "motion": [ + "motion_gotoxy" + ], + "control": [], + "operator": [], + "sensing": [], + "looks": [], + "sounds": [], + "events": [], + "data": [], + "opcode_counts": [ + { + "opcode": "event_whenflagclicked", + "count": 1 + }, + { + "opcode": "motion_gotoxy", + "count": 1 + }, + { + "opcode": "data_setvariableto", + "count": 1 + } + ] + }, + { + "event": "event_whenkeypressed", + "logic": "when [right arrow v] key pressed\n change x by (10)", + "motion": [ + "motion_changexby" + ], + "control": [], + "operator": [], + "sensing": [], + "looks": [], + "sounds": [], + "events": [], + "data": [], + "opcode_counts": [ + { + "opcode": "event_whenkeypressed", + "count": 1 + }, + { + "opcode": "motion_changexby", + "count": 1 + } + ] + }, + { + "event": "event_whenkeypressed", + "logic": "when [left arrow v] key pressed\n change x by (-10)", + "motion": [ + "motion_changexby" + ], + "control": [], + "operator": [], + "sensing": [], + "looks": [], + "sounds": [], + "events": [], + "data": [], + "opcode_counts": [ + { + "opcode": "event_whenkeypressed", + "count": 1 + }, + { + "opcode": "motion_changexby", + "count": 1 + } + ] + }, + { + "event": "event_whenkeypressed", + "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", + "motion": [ + "motion_changeyby" + ], + "control": [ + "control_forever", + "control_if" + ], + "operator": [], + "sensing": [], + "looks": [], + "sounds": [], + "events": [], + "data": [], + "opcode_counts": [ + { + "opcode": "event_whenkeypressed", + "count": 1 + }, + { + "opcode": "control_if", + "count": 1 + }, + { + "opcode": "control_forever", + "count": 1 + }, + { + "opcode": "motion_changeyby", + "count": 2 + } + ] + } + ] + }, + "ball": { + "description": "Obstacle movement and interaction", + "plans": [ + { + "event": "event_whenflagclicked", + "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 + if <(x position) < (-235)> then\n set x to (240)\n end\n if then\n broadcast [Game Over v]\n stop [all v]\n end\n end", + "motion": [ + "motion_gotoxy", + "motion_glidesecstoxy", + "motion_xposition", + "motion_setx" + ], + "control": [ + "control_forever", + "control_if", + "control_stop" + ], + "operator": [ + "operator_lt" + ], + "sensing": [ + "sensing_istouching" + ], + "looks": [], + "sounds": [], + "events": [ + "event_broadcast" + ], + "data": [], + "opcode_counts": [ + { + "opcode": "event_whenflagclicked", + "count": 1 + }, + { + "opcode": "motion_gotoxy", + "count": 1 + }, + { + "opcode": "motion_glidesecstoxy", + "count": 1 + }, + { + "opcode": "motion_xposition", + "count": 1 + }, + { + "opcode": "motion_setx", + "count": 1 + }, + { + "opcode": "control_forever", + "count": 1 + }, + { + "opcode": "control_if", + "count": 2 + }, + { + "opcode": "operator_lt", + "count": 1 + }, + { + "opcode": "sensing_istouching", + "count": 1 + }, + { + "opcode": "event_broadcast", + "count": 1 + }, + { + "opcode": "control_stop", + "count": 1 + }, + { + "opcode": "data_setvariableto", + "count": 1 + } + ] + } + ] + } +} +[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}} +2025-07-25 14:08:27,550 - __main__ - INFO - Action blocks added for sprite 'Stage' by OverallBlockBuilderNode. +[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}} +2025-07-25 14:08:27,551 - __main__ - INFO - Action blocks added for sprite 'Stage' by OverallBlockBuilderNode. +[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}} +2025-07-25 14:08:27,552 - __main__ - INFO - Action blocks added for sprite 'Cat' by OverallBlockBuilderNode. +[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}} +2025-07-25 14:08:27,553 - __main__ - INFO - Action blocks added for sprite 'Cat' by OverallBlockBuilderNode. +[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}} +2025-07-25 14:08:27,554 - __main__ - INFO - Action blocks added for sprite 'Cat' by OverallBlockBuilderNode. +[THE CONDA MATCH]------------->((y position)) = (-100) +the stmt was this ((y position)) = (-100) and parsed was this ((y position)) = (-100) +[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}} +2025-07-25 14:08:27,557 - __main__ - INFO - Action blocks added for sprite 'Cat' by OverallBlockBuilderNode. +Error generating plan from blocks: Can't parse reporter or value: 2) seconds to x: ( +2025-07-25 14:08:27,560 - __main__ - INFO - Action blocks added for sprite 'ball' by OverallBlockBuilderNode. +2025-07-25 14:08:27,562 - __main__ - INFO - Final project JSON saved to generated_projects\8f9aca5d-60b6-4ca3-b9c9-d69410033020\project.json +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 +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 +2025-07-25 14:08:27,587 - __main__ - INFO - Successfully created SB3 file: generated_projects\8f9aca5d-60b6-4ca3-b9c9-d69410033020.sb3 +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 - + +Raw response from LLM [OverallPlannerNode 2]: ```json +{ + "action_overall_flow": { + "Stage": { + "description": "Background and global game state management, including broadcasts, rewards, and score.", + "plans": [ + { + "event": "event_whenflagclicked", + "logic": "when green flag clicked\nswitch backdrop to [blue sky v]\nset [score v] to (0)\nshow variable [score v]\nbroadcast [Game Start v]", + "motion": [], + "control": [], + "operator": [], + "sensing": [], + "looks": [ + "looks_switchbackdropto" + ], + "sounds": [], + "events": [ + "event_broadcast" + ], + "data": [ + "data_setvariableto", + "data_showvariable" + ] + }, + { + "event": "event_whenbroadcastreceived", + "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]", + "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": "when green flag clicked\ngo to x: (0) y: (-120)\nend", + "motion": [ + "motion_gotoxy" + ], + "control": [], + "operator": [], + "sensing": [], + "looks": [], + "sounds": [], + "events": [], + "data": [] + }, + { + "event": "event_whenkeypressed", + "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", + "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": "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 then\nbroadcast [Game Over v]\nstop [all v]\nend\nend", + "motion": [ + "motion_gotoxy", + "motion_changexby", + "motion_setx" + ], + "control": [ + "control_forever", + "control_if", + "control_stop" + ], + "operator": [], + "sensing": [ + "sensing_touchingobject" + ], + "looks": [], + "sounds": [], + "events": [ + "event_broadcast" + ], + "data": [] + } + ] + } + } +} +``` + +[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 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': []}]}} + + +[Refined Action Plan]: { + "action_overall_flow": { + "Stage": { + "description": "Background and global game state management, including broadcasts, rewards, and score.", + "plans": [ + { + "event": "event_whenflagclicked", + "logic": "when green flag clicked\n set [score v] to (0)\n show variable [score v]\n broadcast [Game Start v]", + "control": [ + "control_broadcast" + ], + "data": [ + "data_setvariableto", + "data_showvariable" + ], + "events": [ + "event_broadcast" + ] + }, + { + "event": "event_whenbroadcastreceived", + "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]", + "control": [ + "control_if" + ], + "operator": [ + "operator_gt" + ], + "looks": [ + "looks_switchbackdropto" + ], + "data": [ + "data_setvariableto" + ] + } + ] + }, + "Cat": { + "description": "Main character (cat) actions", + "plans": [ + { + "event": "event_whenflagclicked", + "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", + "motion": [ + "motion_gotoxy", + "motion_changexby" + ], + "control": [ + "control_forever", + "control_if" + ], + "operator": [ + "operator_gt", + "operator_lt" + ] + }, + { + "event": "event_whenkeypressed", + "logic": "when [space v] key pressed\n change y by (100)\n wait (0.2) seconds\n change y by (-100)", + "motion": [ + "motion_changeyby" + ], + "control": [ + "control_wait" + ] + } + ] + }, + "ball": { + "description": "Obstacle movement and interaction", + "plans": [ + { + "event": "event_whenflagclicked", + "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 then\n broadcast [Game Over v]\n stop [all v]\n end\n end", + "motion": [ + "motion_gotoxy", + "motion_glidesecstoxy", + "motion_xposition", + "motion_setx" + ], + "control": [ + "control_forever", + "control_if", + "control_stop" + ], + "sensing": [ + "sensing_istouching" + ], + "events": [ + "event_broadcast" + ] + } + ] + } + } +} diff --git a/utils/agent.py b/utils/agent.py new file mode 100644 index 0000000000000000000000000000000000000000..76c915f75058254e66372ed1b9d751798af36e36 --- /dev/null +++ b/utils/agent.py @@ -0,0 +1,1647 @@ +#─── Basic imports ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +import os +import math +import sqlite3 +import fitz # PyMuPDF for PDF parsing +import re + +from dotenv import load_dotenv +# Load environment variables from .env file +load_dotenv() # This line ensures .env variables are loaded + +from langgraph.graph import START, StateGraph, MessagesState, END +from langgraph.prebuilt import tools_condition +from langgraph.prebuilt import ToolNode +from langgraph.constants import START +from langchain_core.tools import tool +from langchain.schema import SystemMessage +#from langchain.chat_models import init_chat_model +#from langgraph.prebuilt import create_react_agent + +from langchain.embeddings import HuggingFaceEmbeddings +#from langchain.vectorstores import Pinecone +from langchain.tools.retriever import create_retriever_tool +#import pinecone +#from pinecone import Pinecone as PineconeClient, ServerlessSpec +#from pinecone import Index # the blocking‐call client constructor +#from pinecone import Pinecone as PineconeClient, ServerlessSpec +from langchain.embeddings import HuggingFaceEmbeddings +from langchain_community.vectorstores.pinecone import Pinecone as LC_Pinecone + +# ─── Langchain Frameworks ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +#from langchain.tools import Tool +from langchain.chat_models import ChatOpenAI +from langchain_groq import ChatGroq +from langchain_mistralai import ChatMistralAI +from langchain.agents import initialize_agent, AgentType +from langchain.schema import Document +from langchain.chains import RetrievalQA +from langchain.embeddings import OpenAIEmbeddings +from langchain_community.embeddings import HuggingFaceEmbeddings +from langchain.vectorstores import FAISS +from langchain.text_splitter import RecursiveCharacterTextSplitter +from langchain.prompts import PromptTemplate +from langchain_community.document_loaders import TextLoader, PyMuPDFLoader +from langchain_community.document_loaders.wikipedia import WikipediaLoader +from langchain_community.document_loaders.arxiv import ArxivLoader +from langchain_experimental.tools.python.tool import PythonREPLTool + + +# ─── Memory ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +from langchain.agents import initialize_agent, AgentType +from langchain.tools import Tool +from typing import List, Callable +from langchain.schema import BaseMemory, AIMessage, HumanMessage, SystemMessage +from langchain.schema import HumanMessage, SystemMessage +from langchain.llms.base import LLM +from langchain.memory.chat_memory import BaseChatMemory +from pydantic import PrivateAttr +from langchain_core.messages import get_buffer_string + +# ─── Image Processing ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + +from PIL import Image +import pytesseract +from transformers import pipeline +from groq import Groq +import requests +from io import BytesIO +from transformers import pipeline, TrOCRProcessor, VisionEncoderDecoderModel +import requests +import base64 +from PIL import UnidentifiedImageError + +# ─── Browser var ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +from typing import List, Dict +import json +from io import BytesIO +#from langchain.tools import tool # or langchain_core.tools +from playwright.sync_api import sync_playwright +from duckduckgo_search import DDGS +import time +import random +import logging +from functools import lru_cache, wraps +import requests +from playwright.sync_api import sync_playwright +from bs4 import BeautifulSoup +import tenacity +from tenacity import retry, stop_after_attempt, wait_exponential + +# Initialize logger +logger = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') + +# Additional imports for new functionality +import pandas as pd +from PyPDF2 import PdfReader +import docx +import pytesseract +import speech_recognition as sr +from pydub import AudioSegment +from pytube import YouTube +from newspaper import Article +from langchain.document_loaders import ArxivLoader +from langchain_community.document_loaders.youtube import YoutubeLoader, TranscriptFormat + +from playwright.sync_api import sync_playwright +# Attempt to import Playwright for dynamic page rendering +try: + from playwright.sync_api import sync_playwright + _playwright_available = True +except ImportError: + _playwright_available = False + +# Define forbidden keywords for basic NSFW filtering +_forbidden = ["porn", "sex", "xxx", "nude", "erotic"] + +# ─── LLM Setup ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# Load OpenAI API key from environment (required for LLM and embeddings) + +# API Keys from .env file +os.environ.setdefault("OPENAI_API_KEY", "") # Set your own key or env var +os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY", "default_key_or_placeholder") +os.environ["MISTRAL_API_KEY"] = os.getenv("MISTRAL_API_KEY", "default_key_or_placeholder") + +# Tavily API Key +TAVILY_API_KEY = os.getenv("TAVILY_API_KEY", "default_key_or_placeholder") +_forbidden = ["nsfw", "porn", "sex", "explicit"] +_playwright_available = True # set False to disable Playwright + +# Globals for RAG system +vector_store = None +rag_chain = None +DB_PATH = None # will be set when a .db is uploaded +DOC_PATH = None # will be set when a document is uploaded +IMG_PATH = None # will be set when an image is uploaded +OTH_PATH = None # will be set when an other file is uploaded + + +# ─── LLMS ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +#llm = ChatOpenAI(model_name="gpt-3.5-turbo", streaming=True, temperature=0) +from tenacity import retry, stop_after_attempt, wait_exponential + +# Import the RetryingChatGroq client +from retry_groq import RetryingChatGroq + +# Use the retrying version instead +llm = RetryingChatGroq(model="deepseek-r1-distill-llama-70b", streaming=False, temperature=0) +#llm = ChatMistralAI(model="mistral-large-latest", streaming=True, temperature=0) + +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# ─────────────────────────────────────────────── Tool for multiply ────────────────────────────────────────────────────────────────────── +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@tool(parse_docstring=True) +def multiply(a: int, b: int) -> int: + """ + Multiply two numbers. + + Args: + a (int): The first factor. + b (int): The second factor. + + Returns: + int: The product of a and b. + """ + try: + # Direct calculation without relying on LangChain handling + result = a * b + return result + except Exception as e: + return f"Error in multiplication: {str(e)}" + +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# ─────────────────────────────────────────────── Tool for add ────────────────────────────────────────────────────────────────────────── +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@tool(parse_docstring=True) +def add(a: int, b: int) -> int: + """ + Add two numbers. + + Args: + a (int): The first factor. + b (int): The second factor. + + Returns: + int: The addition of a and b. + """ + try: + # Direct calculation without relying on LangChain handling + result = a + b + return result + except Exception as e: + return f"Error in addition: {str(e)}" + +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# ─────────────────────────────────────────────── Tool for subtract ────────────────────────────────────────────────────────────────────── +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@tool(parse_docstring=True) +def subtract(a: int, b: int) -> int: + """ + Subtract two numbers. + + Args: + a (int): The first factor. + b (int): The second factor. + + Returns: + int: The subtraction of a and b. + """ + try: + # Direct calculation without relying on LangChain handling + result = a - b + return result + except Exception as e: + return f"Error in subtraction: {str(e)}" + +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# ─────────────────────────────────────────────── Tool for divide ────────────────────────────────────────────────────────────────────── +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@tool(parse_docstring=True) +def divide(a: int, b: int) -> int: + """ + Divide two numbers. + + Args: + a (int): The numerator. + b (int): The denominator. + + Returns: + float: The result of a divided by b. + + Raises: + ValueError: If b is zero. + """ + try: + if b == 0: + return "Error: Cannot divide by zero." + # Direct calculation without relying on LangChain handling + result = a / b + return result + except Exception as e: + return f"Error in division: {str(e)}" + +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# ─────────────────────────────────────────────── Tool for modulus ────────────────────────────────────────────────────────────────────── +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@tool(parse_docstring=True) +def modulus(a: int, b: int) -> int: + """ + Get the modulus (remainder) of two numbers. + + Args: + a (int): The dividend. + b (int): The divisor. + + Returns: + int: The remainder when a is divided by b. + """ + try: + if b == 0: + return "Error: Cannot calculate modulus with zero divisor." + # Direct calculation without relying on LangChain handling + result = a % b + return result + except Exception as e: + return f"Error in modulus calculation: {str(e)}" + +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# ─────────────────────────────────────────────── Tool for browsing ────────────────────────────────────────────────────────────────────── +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +def with_retry(max_attempts: int = 3, backoff_base: int = 2): + """ + Decorator for retrying a function with exponential backoff on exception. + """ + def decorator(fn): + @wraps(fn) + def wrapper(*args, **kwargs): + for attempt in range(max_attempts): + try: + return fn(*args, **kwargs) + except Exception as e: + wait = backoff_base ** attempt + random.uniform(0, 1) + logger.warning(f"{fn.__name__} failed (attempt {attempt+1}/{max_attempts}): {e}") + if attempt < max_attempts - 1: + time.sleep(wait) + logger.error(f"{fn.__name__} failed after {max_attempts} attempts.") + return [] + return wrapper + return decorator + +@with_retry() +@lru_cache(maxsize=128) +def tavily_search(query: str, top_k: int = 3) -> List[Dict]: + """Call Tavily API and return a list of result dicts.""" + if not TAVILY_API_KEY: + logger.info("[Tavily] No API key set. Skipping Tavily search.") + return [] + url = "https://api.tavily.com/search" + headers = { + "Authorization": f"Bearer {TAVILY_API_KEY}", + "Content-Type": "application/json", + } + payload = {"query": query, "num_results": top_k} + resp = requests.post(url, headers=headers, json=payload, timeout=10) + resp.raise_for_status() + data = resp.json() + results = [] + for item in data.get("results", []): + results.append({ + "title": item.get("title", ""), + "url": item.get("url", ""), + "content": item.get("content", "")[:200], + "source": "Tavily" + }) + return results + +@with_retry() +@lru_cache(maxsize=128) +def duckduckgo_search(query: str, top_k: int = 3) -> List[Dict]: + """Query DuckDuckGo and return up to top_k raw SERP hits.""" + results = [] + try: + with DDGS(timeout=15) as ddgs: # Increase timeout from default + for hit in ddgs.text(query, safesearch="On", max_results=top_k, timeout=15): + results.append({ + "title": hit.get("title", ""), + "url": hit.get("href") or hit.get("url", ""), + "content": hit.get("body", ""), + "source": "DuckDuckGo" + }) + if len(results) >= top_k: + break + except Exception as e: + logger.warning(f"DuckDuckGo search failed: {e}") + # Don't re-raise - just return empty results to allow fallbacks to work + + return results + +# Additional fallback search alternative +def simple_google_search(query: str, top_k: int = 3) -> List[Dict]: + """Simplified Google search as a fallback when other methods fail.""" + try: + # Encode the query + import urllib.parse + import bs4 + + encoded_query = urllib.parse.quote(query) + url = f"https://www.google.com/search?q={encoded_query}" + + headers = { + "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", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + "Referer": "https://www.google.com/", + "Connection": "keep-alive", + } + + response = requests.get(url, headers=headers, timeout=20) + response.raise_for_status() + + soup = bs4.BeautifulSoup(response.text, "html.parser") + results = [] + + # Extract search results + for result in soup.select("div.g")[:top_k]: + title_elem = result.select_one("h3") + link_elem = result.select_one("a") + snippet_elem = result.select_one("div.VwiC3b") + + if title_elem and link_elem and snippet_elem and "href" in link_elem.attrs: + href = link_elem["href"] + if href.startswith("/url?q="): + href = href.split("/url?q=")[1].split("&")[0] + + if href.startswith("http"): + results.append({ + "title": title_elem.get_text(), + "url": href, + "content": snippet_elem.get_text(), + "source": "Google" + }) + + return results + + except Exception as e: + logger.warning(f"Simple Google search failed: {e}") + return [] + +def hybrid_search(query: str, top_k: int = 3) -> List[Dict]: + """Combine multiple search sources with fallbacks.""" + # Try primary search methods first + results = [] + + # Start with Tavily if API key is available + if TAVILY_API_KEY and TAVILY_API_KEY != "default_key_or_placeholder": + try: + tavily_results = tavily_search(query, top_k) + results.extend(tavily_results) + logger.info(f"Retrieved {len(tavily_results)} results from Tavily") + except Exception as e: + logger.warning(f"Tavily search failed: {e}") + + # If we don't have enough results, try DuckDuckGo + if len(results) < top_k: + try: + ddg_results = duckduckgo_search(query, top_k - len(results)) + results.extend(ddg_results) + logger.info(f"Retrieved {len(ddg_results)} results from DuckDuckGo") + except Exception as e: + logger.warning(f"DuckDuckGo search failed: {e}") + + # If we still don't have enough results, try Google + if len(results) < top_k: + try: + google_results = simple_google_search(query, top_k - len(results)) + results.extend(google_results) + logger.info(f"Retrieved {len(google_results)} results from Google") + except Exception as e: + logger.warning(f"Google search failed: {e}") + + # If all search methods failed, return a dummy result + if not results: + results.append({ + "title": "Search Failed", + "url": "", + "content": f"Sorry, I couldn't find results for '{query}'. Please try refining your search terms or check your internet connection.", + "source": "No results" + }) + + return results[:top_k] # Ensure we only return top_k results + +def format_search_docs(search_docs: List[Dict]) -> Dict[str, str]: + """ + Turn a list of {source, page, content} dicts into one big + string with entries separated by `---`. + """ + formatted_search_docs = "\n\n---\n\n".join( + [ + f'\n' + f'{doc.get("content", "")}\n' + f'' + for doc in search_docs + ] + ) + return {"web_results": formatted_search_docs} + + +@tool(parse_docstring=True) +def web_search(query: str, top_k: int = 3) -> Dict[str, str]: + """ + Perform a hybrid web search combining multiple search engines with robust fallbacks. + + Args: + query: The search query string to look up. + top_k: The maximum number of search results to return (default is 3). + + Returns: + A dictionary mapping result indices to XML-like blocks, each containing: + - source: The URL of the webpage. + - page: Placeholder for page identifier (empty string by default). + - content: The first 200 words of the page text, cleaned of HTML tags. + """ + try: + # Use our robust hybrid search to get initial results + search_results = hybrid_search(query, top_k) + results = [] + + # Process each search result to get better content + for hit in search_results: + url = hit.get("url") + if not url: + continue + + # Start with the snippet from search + content = hit.get("content", "") + title = hit.get("title", "") + + # Try to scrape additional content if possible + try: + # Use a random user agent to avoid blocking + headers = { + "User-Agent": random.choice([ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36", + "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", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", + "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" + ]), + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + "Referer": "https://www.google.com/", + "DNT": "1", + "Connection": "keep-alive" + } + + # Higher timeout for better reliability + resp = requests.get(url, timeout=15, headers=headers) + + # Only process if successful + if resp.status_code == 200: + soup = BeautifulSoup(resp.text, "html.parser") + + # Try to find main content + main_content = soup.find('main') or soup.find('article') or soup.find('div', class_='content') + + # If we found main content, use it + if main_content: + extracted_text = main_content.get_text(separator=" ", strip=True) + # Take first 200 words + content = " ".join(extracted_text.split()[:200]) + else: + # Otherwise use all text + all_text = soup.get_text(separator=" ", strip=True) + content = " ".join(all_text.split()[:200]) + + # Use content from page only if it's substantial + if len(content) < 50: + content = hit.get("content", "")[:200] + + # Random delay between 0.5-1.5 seconds to avoid rate limits + time.sleep(0.5 + random.random()) + + except requests.exceptions.HTTPError as e: + logger.warning(f"HTTP error when scraping {url}: {e}") + # Keep the search snippet as a fallback + except requests.exceptions.RequestException as e: + logger.warning(f"Request error when scraping {url}: {e}") + # Keep the search snippet as a fallback + except Exception as e: + logger.warning(f"Unexpected error when scraping {url}: {e}") + # Keep the search snippet as a fallback + + # Filter out inappropriate content + if any(f in content.lower() for f in _forbidden): + continue + + # Add to results + results.append({ + "source": url, + "page": "", + "content": content + }) + + # Return formatted search docs + return format_search_docs(results[:top_k]) + except Exception as e: + logger.error(f"Web search failed: {e}") + # Return a helpful error message + return format_search_docs([{ + "source": "Error", + "page": "", + "content": f"Search failed with error: {e}. Please try again with different search terms." + }]) + +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# ─────────────────────────────────────────────── Tool for File System ─────────────────────────────────────────────────────────────────── +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@tool(parse_docstring=True) +def download_file(url: str, dest_path: str) -> str: + """ + Download a file from a given URL and save it locally. + + Args: + url: The direct URL of the file to download. + dest_path: The local path to save the downloaded file. + + Returns: + The destination path where the file was saved. + """ + r = requests.get(url, stream=True) + r.raise_for_status() + with open(dest_path, 'wb') as f: + for chunk in r.iter_content(8192): + f.write(chunk) + return dest_path + +@tool(parse_docstring=True) +def process_excel_to_text(file_path: str) -> str: + """ + Convert an Excel file into CSV-formatted text. + + Args: + file_path: Path to the Excel (.xlsx) file. + + Returns: + A string of CSV-formatted content extracted from the Excel file. + """ + try: + # Check if file exists + import os + if not os.path.exists(file_path): + return f"Error: Excel file '{file_path}' does not exist." + + # Try different engines + engines = ['openpyxl', 'xlrd', None] + + for engine in engines: + try: + # For engine=None, pandas will try to auto-detect + if engine: + df = pd.read_excel(file_path, engine=engine) + else: + df = pd.read_excel(file_path) + return df.to_csv(index=False) + except Exception as e: + print(f"Excel engine {engine} failed: {e}") + last_error = e + continue + + # If we got here, all engines failed + return f"Error processing Excel file: {str(last_error)}" + except Exception as e: + return f"Error with Excel file: {str(e)}" + +@tool(parse_docstring=True) +def read_text_from_pdf(file_path: str, question: str = None) -> str: + """ + Extract text from a PDF file, chunking large documents if needed. + + Args: + file_path: Path to the PDF file. + question: Optional question to help retrieve relevant parts of long documents. + + Returns: + The extracted text content, potentially chunked if the document is large. + """ + try: + # Check if file exists + import os + if not os.path.exists(file_path): + return f"Error: PDF file '{file_path}' does not exist." + + reader = PdfReader(file_path) + full_text = "\n".join([page.extract_text() or "" for page in reader.pages]) + + # If a question is provided, use retrieval to get relevant parts + if question and len(full_text) > 5000: # Only chunk if text is large + return process_large_document(full_text, question) + + return full_text + except Exception as e: + return f"Error reading PDF: {str(e)}" + +@tool(parse_docstring=True) +def read_text_from_docx(file_path: str, question: str = None) -> str: + """ + Extract text from a DOCX (Word) document, chunking large documents if needed. + + Args: + file_path: Path to the DOCX file. + question: Optional question to help retrieve relevant parts of long documents. + + Returns: + The extracted text, potentially chunked if the document is large. + """ + try: + # Check if file exists + import os + if not os.path.exists(file_path): + return f"Error: File '{file_path}' does not exist." + + try: + doc = docx.Document(file_path) + full_text = "\n".join([para.text for para in doc.paragraphs]) + except Exception as docx_err: + # Handle "Package not found" error specifically + if "Package not found" in str(docx_err): + # Try to read raw text if possible + try: + import zipfile + from xml.etree.ElementTree import XML + + WORD_NAMESPACE = '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}' + PARA = WORD_NAMESPACE + 'p' + TEXT = WORD_NAMESPACE + 't' + + with zipfile.ZipFile(file_path) as docx_file: + with docx_file.open('word/document.xml') as document: + tree = XML(document.read()) + paragraphs = [] + for paragraph in tree.iter(PARA): + texts = [node.text for node in paragraph.iter(TEXT) if node.text] + if texts: + paragraphs.append(''.join(texts)) + full_text = '\n'.join(paragraphs) + except Exception as e: + return f"Error reading DOCX file: {str(e)}" + else: + return f"Error reading DOCX file: {str(docx_err)}" + + # If a question is provided, use retrieval to get relevant parts + if question and len(full_text) > 5000: # Only chunk if text is large + return process_large_document(full_text, question) + + return full_text + except Exception as e: + return f"Error reading DOCX file: {str(e)}" + + +@tool(parse_docstring=True) +def transcribe_audio(file_path: str) -> str: + """ + Transcribe speech from a local audio file to text. + + Args: + file_path: Path to the audio file. + + Returns: + Transcribed text using Google Web Speech API. + """ + try: + # Check if file exists + import os + if not os.path.exists(file_path): + return f"Error: Audio file '{file_path}' does not exist." + + # For non-WAV files, convert to WAV first + if not file_path.lower().endswith('.wav'): + try: + from pydub import AudioSegment + temp_wav = os.path.splitext(file_path)[0] + "_temp.wav" + audio = AudioSegment.from_file(file_path) + audio.export(temp_wav, format="wav") + file_path = temp_wav + except Exception as e: + return f"Failed to convert audio to WAV format: {str(e)}" + + recognizer = sr.Recognizer() + with sr.AudioFile(file_path) as src: + audio = recognizer.record(src) + return recognizer.recognize_google(audio) + except Exception as e: + if "Audio file could not be read" in str(e): + return f"Error: Audio format not supported. Try converting to WAV, MP3, OGG, or FLAC." + return f"Error transcribing audio: {str(e)}" + +@tool(parse_docstring=True) +def youtube_audio_processing(youtube_url: str) -> str: + """ + Download and transcribe audio from a YouTube video. + + Args: + youtube_url: URL of the YouTube video. + + Returns: + Transcription text extracted from the video's audio. + """ + yt = YouTube(youtube_url) + audio_stream = yt.streams.filter(only_audio=True).first() + out_file = audio_stream.download(output_path='.', filename='yt_audio') + wav_path = 'yt_audio.wav' + AudioSegment.from_file(out_file).export(wav_path, format='wav') + return transcribe_audio(wav_path) + +@tool(parse_docstring=True) +def extract_article_text(url: str, question: str = None) -> str: + """ + Download and extract the main article content from a webpage, chunking large articles if needed. + + Args: + url: The URL of the article to extract. + question: Optional question to help retrieve relevant parts of long articles. + + Returns: + The article's textual content, potentially chunked if large. + """ + try: + art = Article(url) + art.download() + art.parse() + full_text = art.text + + # If a question is provided, use retrieval to get relevant parts + if question and len(full_text) > 5000: # Only chunk if text is large + return process_large_document(full_text, question) + + return full_text + except Exception as e: + return f"Error extracting article: {str(e)}" + +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# ───────────────────────────────────────────────────────────── Tool for ArXiv ──────────────────────────────────────────────────────────── +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + +@tool(parse_docstring=True) +def arvix_search(query: str) -> Dict[str, str]: + """ + Search for academic papers on ArXiv. + + Args: + query: The search term to look for in ArXiv. + + Returns: + A dictionary of up to 3 relevant paper entries in JSON format. + """ + papers = ArxivLoader(query=query, load_max_docs=3).load() + results = [] + for doc in papers: + try: + # Handle different metadata formats that might be returned + source = doc.metadata.get("source", "ArXiv") + doc_id = doc.metadata.get("id", doc.metadata.get("entry_id", "")) + result = { + "source": source, + "id": doc_id, + "summary": doc.page_content[:1000] if hasattr(doc, "page_content") else str(doc)[:1000], + } + results.append(result) + except Exception as e: + # Add error information as a fallback + results.append({ + "source": "ArXiv Error", + "id": "error", + "summary": f"Error processing paper: {str(e)}" + }) + + return {"arvix_results": json.dumps(results)} + +@tool(parse_docstring=True) +def answer_youtube_video_question( + youtube_url: str, + question: str, + chunk_size_seconds: int = 30 +) -> str: + """ + Answer a question based on a YouTube video's transcript. + + Args: + youtube_url: URL of the YouTube video. + question: The question to be answered using video content. + chunk_size_seconds: Duration of each transcript chunk. + + Returns: + The answer to the question generated from the video transcript. + """ + loader = YoutubeLoader.from_youtube_url( + youtube_url, + add_video_info=True, + transcript_format=TranscriptFormat.CHUNKS, + chunk_size_seconds=chunk_size_seconds, + ) + documents = loader.load() + embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-mpnet-base-v2') + vectorstore = FAISS.from_documents(documents, embeddings) + llm = RetryingChatGroq(model="deepseek-r1-distill-llama-70b", streaming=False) + qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=vectorstore.as_retriever()) + return qa_chain.run(question) + +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# ───────────────────────────────────────────────────────────── Tool for Python REPL tool ──────────────────────────────────────────────── +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + +python_repl = PythonREPLTool() + +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# ───────────────────────────────────────────────────────────── Tool for Wiki ──────────────────────────────────────────────────────────── +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + +@tool(parse_docstring=True) +def wiki_search(query: str) -> str: + """ + Search Wikipedia for information on a given topic. + + Args: + query: The search term for Wikipedia. + + Returns: + A JSON string with up to 3 summary results. + """ + # load up to top_k pages + pages = WikipediaLoader(query=query, load_max_docs=3).load() + results: List[Dict] = [] + for doc in pages: + results.append({ + "source": doc.metadata["source"], + "page": doc.metadata.get("page", ""), + "content": doc.page_content[:1000], # truncate if you like + }) + return {"wiki_results": format_search_docs(results)} + +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# ───────────────────────────────────── Tool for Image (understading, captioning & classification) ───────────────────────────────────────── +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + +def _load_image(img_path: str, resize_to=(512, 512)) -> Image.Image: + """ + Load, verify, convert, and resize an image. + Raises ValueError on failure. + """ + if not img_path: + raise ValueError("No image path provided.") + try: + with Image.open(img_path) as img: + img.verify() + img = Image.open(img_path).convert("RGB") + img = img.resize(resize_to) + return img + except UnidentifiedImageError: + raise ValueError(f"File at {img_path} is not a valid image.") + except Exception as e: + raise ValueError(f"Failed to load image at {img_path}: {e}") + +def _encode_image_to_base64(img_path: str) -> str: + """ + Load an image, save optimized PNG into memory, and base64‑encode it. + """ + img = _load_image(img_path) + buffer = BytesIO() + img.save(buffer, format="PNG", optimize=True) + return base64.b64encode(buffer.getvalue()).decode("utf-8") + +@tool +def image_processing(prompt: str, img_path: str) -> str: + """Process an image using a vision LLM, with OCR fallback. + + Args: + prompt: Instruction or question related to the image. + img_path: Path to the image file. + + Returns: + The model's response or fallback OCR result. + """ + try: + import os + # Check if file exists + if not os.path.exists(img_path): + return f"Error: Image file '{img_path}' does not exist." + + try: + b64 = _encode_image_to_base64(img_path) + # Build a single markdown string with inline base64 image + md = f"{prompt}\n\n![](data:image/png;base64,{b64})" + message = HumanMessage(content=md) + # Use RetryingChatGroq with Llama 4 Maverick for vision + llm = RetryingChatGroq(model="meta-llama/llama-4-maverick-17b-128e-instruct", streaming=False, temperature=0) + try: + resp = llm.invoke([message]) + if hasattr(resp, 'content'): + return resp.content.strip() + elif isinstance(resp, str): + return resp.strip() + else: + # Handle dictionary or other response types + return str(resp) + except Exception as invoke_err: + print(f"[LLM invoke error] {invoke_err}") + # Fall back to OCR + raise ValueError("LLM invocation failed") + except Exception as llama_err: + print(f"[LLM vision failed] {llama_err}") + try: + img = _load_image(img_path) + return pytesseract.image_to_string(img).strip() + except Exception as ocr_err: + print(f"[OCR fallback failed] {ocr_err}") + return "Unable to process the image. Please check the file and try again." + except Exception as e: + # Catch any other errors + print(f"[image_processing error] {e}") + return f"Error processing image: {str(e)}" + +python_repl_tool = PythonREPLTool() + +@tool +def echo(text: str) -> str: + """Echo back the input text. + + Args: + text: The string to be echoed. + + Returns: + The same text that was provided as input. + """ + return text + +# ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# ─────────────────────────────────────────────── Langgraph Agent ─────────────────────────────────────────────────────────────────────── +# ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + + +# Build graph function +from langchain_core.tools import tool +from langchain.chat_models import ChatOpenAI +from langgraph.prebuilt.chat_agent_executor import create_react_agent, AgentState +from langchain.chat_models import init_chat_model + + + +def build_graph(provider: str = "groq"): + """Construct and compile the multi‑agent GAIA workflow StateGraph. + + This graph wires together three React‑style agents into a streamlined pipeline: + PerceptionAgent → ActionAgent → EvaluationAgent (with appropriate entry/exit points) + + The agents have the following responsibilities: + - PerceptionAgent: Handles web searches, Wikipedia, ArXiv, and image processing + - ActionAgent: Performs calculations, file operations, and code analysis + - EvaluationAgent: Reviews results and ensures the final answer is properly formatted + + Args: + provider: The name of the LLM provider. Must be "groq". + + Returns: + CompiledGraph: A compiled LangGraph state machine ready for invocation. + + Raises: + ValueError: If `provider` is anything other than "groq". + """ + try: + if provider != "groq": + raise ValueError("Invalid provider. Expected 'groq'.") + + # Initialize LLM + try: + logger.info("Initializing LLM with model: deepseek-r1-distill-llama-70b") + api_key = os.getenv("GROQ_API_KEY") + if not api_key or api_key == "default_key_or_placeholder": + logger.error("GROQ_API_KEY is not set or is using placeholder value") + raise ValueError("GROQ_API_KEY environment variable is not set properly. Please set a valid API key.") + + llm = RetryingChatGroq(model="deepseek-r1-distill-llama-70b", temperature=0) + logger.info("LLM initialized successfully") + except Exception as e: + logger.error(f"Error initializing LLM: {str(e)}") + raise + + # General system message for agents + sys_msg = SystemMessage(content=""" + You are a general AI assistant. I will ask you a question. Report your thoughts, and finish your answer with the following template: + + FINAL ANSWER: [YOUR FINAL ANSWER] + + YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma-separated list of numbers and/or strings. + + If you are asked for a number, don't use commas or units (e.g., $, %, kg) unless specified otherwise. + + If you are asked for a string, don't use articles (a, an, the), and don't use abbreviations (e.g., for states). + + If you are asked for a comma-separated list, apply the above rules to each element in the list. + """.strip()) + + # Special system message for the evaluation agent with stricter formatting requirements + eval_sys_msg = SystemMessage(content=""" + You are a specialized evaluation agent. Your job is to review the work done by other agents + and provide a final, properly formatted answer. + + IMPORTANT: You MUST ALWAYS format your answer using this exact template: + + FINAL ANSWER: [concise answer] + + Rules for formatting the answer: + 1. The answer must be extremely concise - use as few words as possible + 2. For numeric answers, provide only the number without units unless units are specifically requested + 3. For text answers, avoid articles (a, an, the) and unnecessary words + 4. For list answers, use a comma-separated format + 5. NEVER explain your reasoning in the FINAL ANSWER section + 6. NEVER skip the "FINAL ANSWER:" prefix + + Example good answers: + FINAL ANSWER: 42 + FINAL ANSWER: Paris + FINAL ANSWER: 1912, 1945, 1989 + + Example bad answers (don't do these): + - Based on my analysis, the answer is 42. + - I think it's Paris because that's the capital of France. + - The years were 1912, 1945, and 1989. + + Remember: ALWAYS include "FINAL ANSWER:" followed by the most concise answer possible. + """.strip()) + + # Define tools for each agent + logger.info("Setting up agent tools") + perception_tools = [web_search, wiki_search, news_article_search, arvix_search, image_processing, echo] + execution_tools = [ + multiply, add, subtract, divide, modulus, + download_file, process_excel_to_text, + read_text_from_pdf, read_text_from_docx, + transcribe_audio, youtube_audio_processing, + extract_article_text, answer_youtube_video_question, + python_repl_tool, analyze_code, read_code_file, analyze_python_function + ] + + # ─────────────── Agent Creation ─────────────── + logger.info("Creating agents") + try: + # Create agents with proper error handling + PerceptionAgent = create_react_agent( + model=llm, + tools=perception_tools, + prompt=sys_msg, + state_schema=AgentState, + name="PerceptionAgent" + ) + logger.info("Created PerceptionAgent successfully") + + # Combined Planning and Execution agent for better efficiency + ActionAgent = create_react_agent( + model=llm, + tools=execution_tools, # Has access to all execution tools + prompt=sys_msg, + state_schema=AgentState, + name="ActionAgent" + ) + logger.info("Created ActionAgent successfully") + + # Evaluation agent with stricter prompt + EvaluationAgent = create_react_agent( + model=llm, + tools=[], # No tools needed for evaluation + prompt=eval_sys_msg, # Use the specialized evaluation prompt + state_schema=AgentState, + name="EvaluationAgent" + ) + logger.info("Created EvaluationAgent successfully") + except Exception as e: + logger.error(f"Error creating agent: {str(e)}") + import traceback + logger.error(f"Traceback: {traceback.format_exc()}") + raise + + # Build the StateGraph + logger.info("Building StateGraph") + try: + builder = StateGraph(AgentState) + + # Add agent nodes first + builder.add_node("PerceptionAgent", PerceptionAgent) + builder.add_node("ActionAgent", ActionAgent) + builder.add_node("EvaluationAgent", EvaluationAgent) + + # Define the flow with a starting edge + builder.set_entry_point("PerceptionAgent") + + # Add the edges for the simpler linear flow + builder.add_edge("PerceptionAgent", "ActionAgent") + builder.add_edge("ActionAgent", "EvaluationAgent") + + # Set EvaluationAgent as the end node + builder.set_finish_point("EvaluationAgent") + + logger.info("Compiling StateGraph") + return builder.compile() + except Exception as e: + logger.error(f"Error building graph: {str(e)}") + import traceback + logger.error(f"Traceback: {traceback.format_exc()}") + raise + except Exception as e: + logger.error(f"Overall error in build_graph: {str(e)}") + import traceback + logger.error(f"Traceback: {traceback.format_exc()}") + raise + +def get_final_answer(text): + """Extract just the FINAL ANSWER from the model's response. + + Args: + text: The full text response from the LLM + + Returns: + str: The extracted answer without the "FINAL ANSWER:" prefix + """ + # Log the raw text for debugging if needed + logger.debug(f"Extracting answer from: {text[:200]}...") + + if not text: + logger.warning("Empty response received") + return "No answer provided." + + # Method 1: Look for "FINAL ANSWER:" with most comprehensive pattern matching + pattern = r'(?:^|\n)FINAL ANSWER:\s*(.*?)(?:\n\s*$|$)' + match = re.search(pattern, text, re.DOTALL | re.IGNORECASE) + if match: + # Return just the answer part, cleaned up + logger.debug("Found answer using pattern 1") + return match.group(1).strip() + + # Method 2: Try looking for variations on the final answer format + for variant in ["FINAL ANSWER:", "FINAL_ANSWER:", "Final Answer:", "Answer:"]: + lines = text.split('\n') + for i, line in enumerate(reversed(lines)): + if variant in line: + # Extract everything after the variant text + logger.debug(f"Found answer using variant: {variant}") + answer = line[line.find(variant) + len(variant):].strip() + if answer: + return answer + # If the answer is on the next line, return that + if i > 0: + next_line = lines[len(lines) - i] + if next_line.strip(): + return next_line.strip() + + # Method 3: Look for phrases that suggest an answer + for phrase in ["The answer is", "The result is", "We get", "Therefore,", "In conclusion,"]: + phrase_pos = text.find(phrase) + if phrase_pos != -1: + # Try to extract everything after the phrase until the end of the sentence + sentence_end = text.find(".", phrase_pos) + if sentence_end != -1: + logger.debug(f"Found answer using phrase: {phrase}") + return text[phrase_pos + len(phrase):sentence_end].strip() + + # Method 4: Fall back to taking the last paragraph with actual content + paragraphs = text.strip().split('\n\n') + for para in reversed(paragraphs): + para = para.strip() + if para and not para.startswith("I ") and not para.lower().startswith("to "): + logger.debug("Using last meaningful paragraph") + # If paragraph is very long, try to extract a concise answer + if len(para) > 100: + sentences = re.split(r'[.!?]', para) + for sentence in reversed(sentences): + sent = sentence.strip() + if sent and len(sent) > 5 and not sent.startswith("I "): + return sent + return para + + # Method 5: Last resort - just return the last line with content + lines = text.strip().split('\n') + for line in reversed(lines): + line = line.strip() + if line and len(line) > 3: + logger.debug("Using last line with content") + return line + + # If everything fails, warn and return the truncated response + logger.warning("Could not find a properly formatted answer") + return text[:100] + "..." if len(text) > 100 else text + +# test +if __name__ == "__main__": + question = "When was a picture of St. Thomas Aquinas first added to the Wikipedia page on the Principle of double effect?" + # Build the graph + graph = build_graph(provider="groq") + # Run the graph + messages = [HumanMessage(content=question)] + messages = graph.invoke({"messages": messages}) + for m in messages["messages"]: + m.pretty_print() + +# ─────────────────────────────────────────────── Tool for Code Analysis ─────────────────────────────────────────────────────────────── +@tool +def analyze_code(code_string: str) -> str: + """Analyze a string of code to understand its structure, functionality, and potential issues. + + Args: + code_string: The code to analyze as a string. + + Returns: + A structured analysis of the code including functions, classes, and key operations. + """ + try: + import ast + + # Try to parse with Python's AST module + try: + parsed = ast.parse(code_string) + + # Extract functions and classes + functions = [node.name for node in ast.walk(parsed) if isinstance(node, ast.FunctionDef)] + classes = [node.name for node in ast.walk(parsed) if isinstance(node, ast.ClassDef)] + imports = [node.names[0].name for node in ast.walk(parsed) if isinstance(node, ast.Import)] + imports.extend([f"{node.module}.{name.name}" if node.module else name.name + for node in ast.walk(parsed) if isinstance(node, ast.ImportFrom) + for name in node.names]) + + # Count various node types for complexity assessment + num_loops = len([node for node in ast.walk(parsed) + if isinstance(node, (ast.For, ast.While))]) + num_conditionals = len([node for node in ast.walk(parsed) + if isinstance(node, (ast.If, ast.IfExp))]) + + analysis = { + "language": "Python", + "functions": functions, + "classes": classes, + "imports": imports, + "complexity": { + "functions": len(functions), + "classes": len(classes), + "loops": num_loops, + "conditionals": num_conditionals + } + } + return str(analysis) + except SyntaxError: + # If not valid Python, try some simple pattern matching + if "{" in code_string and "}" in code_string: + if "function" in code_string or "=>" in code_string: + language = "JavaScript/TypeScript" + elif "func" in code_string or "struct" in code_string: + language = "Go or Rust" + elif "public" in code_string or "private" in code_string or "class" in code_string: + language = "Java/C#/C++" + else: + language = "Unknown C-like language" + elif "<" in code_string and ">" in code_string and ("/>" in code_string or " str: + """Read a code file and return its contents with proper syntax detection. + + Args: + file_path: Path to the code file. + + Returns: + The file contents and detected language. + """ + try: + # Check if file exists + import os + if not os.path.exists(file_path): + return f"Error: File '{file_path}' does not exist." + + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Try to detect language from extension + ext = os.path.splitext(file_path)[1].lower() + + language_map = { + '.py': 'Python', + '.js': 'JavaScript', + '.ts': 'TypeScript', + '.html': 'HTML', + '.css': 'CSS', + '.java': 'Java', + '.c': 'C', + '.cpp': 'C++', + '.cs': 'C#', + '.go': 'Go', + '.rs': 'Rust', + '.php': 'PHP', + '.rb': 'Ruby', + '.sh': 'Shell', + '.bat': 'Batch', + '.ps1': 'PowerShell', + '.sql': 'SQL', + '.json': 'JSON', + '.xml': 'XML', + '.yaml': 'YAML', + '.yml': 'YAML', + } + + language = language_map.get(ext, 'Unknown') + + return f"File content ({language}):\n\n{content}" + except Exception as e: + return f"Error reading file: {str(e)}" + +@tool +def analyze_python_function(function_name: str, code_string: str) -> str: + """Extract and analyze a specific function from Python code. + + Args: + function_name: The name of the function to analyze. + code_string: The complete code containing the function. + + Returns: + Analysis of the function including parameters, return type, and docstring. + """ + try: + import ast + import inspect + from types import CodeType, FunctionType + + # Parse the code string + parsed = ast.parse(code_string) + + # Find the function definition + function_def = None + for node in ast.walk(parsed): + if isinstance(node, ast.FunctionDef) and node.name == function_name: + function_def = node + break + + if not function_def: + return f"Function '{function_name}' not found in the provided code." + + # Extract parameters + params = [] + for arg in function_def.args.args: + param_name = arg.arg + # Get annotation if it exists + if arg.annotation: + if isinstance(arg.annotation, ast.Name): + param_type = arg.annotation.id + elif isinstance(arg.annotation, ast.Attribute): + param_type = f"{arg.annotation.value.id}.{arg.annotation.attr}" + else: + param_type = "complex_type" + params.append(f"{param_name}: {param_type}") + else: + params.append(param_name) + + # Extract return type if it exists + return_type = None + if function_def.returns: + if isinstance(function_def.returns, ast.Name): + return_type = function_def.returns.id + elif isinstance(function_def.returns, ast.Attribute): + return_type = f"{function_def.returns.value.id}.{function_def.returns.attr}" + else: + return_type = "complex_return_type" + + # Extract docstring + docstring = ast.get_docstring(function_def) + + # Create a summary + summary = { + "function_name": function_name, + "parameters": params, + "return_type": return_type, + "docstring": docstring, + "decorators": [d.id if isinstance(d, ast.Name) else "complex_decorator" for d in function_def.decorator_list], + "line_count": len(function_def.body) + } + + # Create a more explicit string representation that ensures key terms are included + result = f"Function '{function_name}' analysis:\n" + result += f"- Parameters: {', '.join(params)}\n" + result += f"- Return type: {return_type or 'None specified'}\n" + result += f"- Docstring: {docstring or 'None'}\n" + result += f"- Line count: {len(function_def.body)}" + + return result + except Exception as e: + return f"Error analyzing function: {str(e)}" + +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +# ─────────────────────────────────────────────── Tool for News Article Retrieval ────────────────────────────────────────────────────────────────────── +# ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + +@tool +def news_article_search(query: str, top_k: int = 3) -> Dict[str, str]: + """Search for and retrieve news articles with robust error handling for news sites. + + Args: + query: The news topic or keywords to search for. + top_k: Maximum number of articles to retrieve. + + Returns: + A dictionary with search results formatted as XML-like document entries. + """ + # First, get URLs from DuckDuckGo with "news" focus + results = [] + news_sources = [ + "bbc.com", "reuters.com", "apnews.com", "nasa.gov", + "space.com", "universetoday.com", "nature.com", "science.org", + "scientificamerican.com", "nytimes.com", "theguardian.com" + ] + + # Find news from reliable sources + try: + with DDGS() as ddgs: + search_query = f"{query} site:{' OR site:'.join(news_sources)}" + for hit in ddgs.text(search_query, safesearch="On", max_results=top_k*2): + url = hit.get("href") or hit.get("url", "") + if not url: + continue + + # Add the search snippet first as a fallback + result = { + "source": url, + "page": "", + "content": hit.get("body", "")[:250], + "title": hit.get("title", "") + } + + # Try to get better content via a more robust method + try: + headers = { + "User-Agent": random.choice([ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36", + "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", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36" + ]), + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + "Referer": "https://www.google.com/", + "DNT": "1", + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1" + } + + # Add a short delay between requests + time.sleep(1 + random.random()) + + # Try to use newspaper3k for more reliable article extraction + from newspaper import Article + article = Article(url) + article.download() + article.parse() + + # If we got meaningful content, update the result + if article.text and len(article.text) > 100: + # Get a summary - first paragraph + some highlights + paragraphs = article.text.split('\n\n') + first_para = paragraphs[0] if paragraphs else "" + summary = first_para[:300] + if len(paragraphs) > 1: + summary += "... " + paragraphs[1][:200] + + result["content"] = summary + if article.title: + result["title"] = article.title + + except Exception as article_err: + logger.warning(f"Article extraction failed for {url}: {article_err}") + # Fallback to simple requests-based extraction + try: + resp = requests.get(url, timeout=12, headers=headers) + resp.raise_for_status() + soup = BeautifulSoup(resp.text, "html.parser") + + # Try to get main content + main_content = soup.find('main') or soup.find('article') or soup.find('div', class_='content') + + if main_content: + content = " ".join(main_content.get_text(separator=" ", strip=True).split()[:250]) + result["content"] = content + except Exception as req_err: + logger.warning(f"Fallback extraction failed for {url}: {req_err}") + # Keep the original snippet as fallback + + results.append(result) + if len(results) >= top_k: + break + + except Exception as e: + logger.error(f"News search failed: {e}") + return format_search_docs([{ + "source": "Error", + "page": "", + "content": f"Failed to retrieve news articles for '{query}': {str(e)}" + }]) + + if not results: + # Fallback to regular web search + logger.info(f"No news results found, falling back to web_search for {query}") + return web_search(query, top_k) + + return format_search_docs(results[:top_k]) + +# ───────────────────────────────────────────────────────────── Document Chunking Utilities ────────────────────────────────────────────────────────── +def chunk_document(text: str, chunk_size: int = 1000, overlap: int = 100) -> List[str]: + """ + Split a large document into smaller chunks with overlap to maintain context across chunks. + + Args: + text: The document text to split into chunks + chunk_size: Maximum size of each chunk in characters + overlap: Number of characters to overlap between chunks + + Returns: + List of text chunks + """ + # If text is smaller than chunk_size, return it as is + if len(text) <= chunk_size: + return [text] + + chunks = [] + start = 0 + + while start < len(text): + # Get chunk with overlap + end = min(start + chunk_size, len(text)) + + # Try to find sentence boundary for cleaner breaks + if end < len(text): + # Look for sentence endings: period, question mark, or exclamation followed by space + for sentence_end in ['. ', '? ', '! ']: + last_period = text[start:end].rfind(sentence_end) + if last_period != -1: + end = start + last_period + 2 # +2 to include the period and space + break + + # Add chunk to list + chunks.append(text[start:end]) + + # Move start position, accounting for overlap + start = end - overlap if end < len(text) else len(text) + + return chunks + +# Document processing utility that uses chunking +def process_large_document(text: str, question: str, llm=None) -> str: + """ + Process a large document by chunking it and using retrieval to find relevant parts. + + Args: + text: The document text to process + question: The question being asked about the document + llm: Optional language model to use (defaults to agent's LLM) + + Returns: + Summarized answer based on relevant chunks + """ + if not llm: + llm = RetryingChatGroq(model="deepseek-r1-distill-llama-70b", streaming=False, temperature=0) + + # Split document into chunks + chunks = chunk_document(text) + + # If document is small enough, don't bother with retrieval + if len(chunks) <= 1: + return text + + # For larger documents, create embeddings to find relevant chunks + try: + from langchain_community.embeddings import HuggingFaceEmbeddings + from langchain.vectorstores import FAISS + from langchain.schema import Document + + # Create documents with chunk content + documents = [Document(page_content=chunk, metadata={"chunk_id": i}) for i, chunk in enumerate(chunks)] + + # Create embeddings and vector store + embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2") + vectorstore = FAISS.from_documents(documents, embeddings) + + # Get most relevant chunks + relevant_chunks = vectorstore.similarity_search(question, k=2) # Get top 2 most relevant chunks + + # Join the relevant chunks + relevant_text = "\n\n".join([doc.page_content for doc in relevant_chunks]) + + # Option 1: Return relevant chunks directly + return relevant_text + + # Option 2: Summarize with LLM (commented out for now) + # prompt = f"Using only the following information, answer the question: '{question}'\n\nInformation:\n{relevant_text}" + # response = llm.invoke([HumanMessage(content=prompt)]) + # return response.content + + except Exception as e: + # Fall back to first chunk if retrieval fails + logger.warning(f"Retrieval failed: {e}. Falling back to first chunk.") + return chunks[0] \ No newline at end of file diff --git a/utils/agent2.py b/utils/agent2.py new file mode 100644 index 0000000000000000000000000000000000000000..654c793cabf087d0af8eeae5e14551c03f7bd465 --- /dev/null +++ b/utils/agent2.py @@ -0,0 +1,259 @@ +import os +from dotenv import load_dotenv + +from langchain_core.tools import tool +from langgraph.prebuilt import tools_condition, ToolNode +from langgraph.graph import START, StateGraph, MessagesState + +from langchain_community.tools.tavily_search import TavilySearchResults +from langchain_community.document_loaders import WikipediaLoader, ArxivLoader +from langchain_core.messages import SystemMessage, HumanMessage +from langchain_community.vectorstores import FAISS +from langchain_huggingface import HuggingFaceEmbeddings +from langchain_groq import ChatGroq +from langchain.tools.retriever import create_retriever_tool + +"""LangGraph Agent""" +import os +from dotenv import load_dotenv +from langgraph.graph import START, StateGraph, MessagesState +from langgraph.prebuilt import tools_condition +from langgraph.prebuilt import ToolNode +from langchain_google_genai import ChatGoogleGenerativeAI +from langchain_groq import ChatGroq +from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint, HuggingFaceEmbeddings +from langchain_community.tools.tavily_search import TavilySearchResults +from langchain_community.document_loaders import WikipediaLoader +from langchain_community.document_loaders import ArxivLoader +from langchain_community.vectorstores import SupabaseVectorStore,FAISS +from langchain_core.messages import SystemMessage, HumanMessage +from langchain_core.tools import tool +from langchain.tools.retriever import create_retriever_tool +#from supabase.client import Client, create_client + +# ────────────────────────────────────────────────────────── +# ENV +# ────────────────────────────────────────────────────────── +load_dotenv() +# API Keys from .env file +os.environ.setdefault("OPENAI_API_KEY", "") # Set your own key or env var +os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY", "default_key_or_placeholder") +os.environ["MISTRAL_API_KEY"] = os.getenv("MISTRAL_API_KEY", "default_key_or_placeholder") + +# Tavily API Key +TAVILY_API_KEY = os.getenv("TAVILY_API_KEY", "default_key_or_placeholder") +_forbidden = ["nsfw", "porn", "sex", "explicit"] +_playwright_available = True # set False to disable Playwright + +embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2") # dim = 768 + +@tool +def multiply(a: int, b: int) -> int: + """Multiply two numbers. + + Args: + a: first int + b: second int + """ + return a * b + +@tool +def add(a: int, b: int) -> int: + """Add two numbers. + + Args: + a: first int + b: second int + """ + return a + b + +@tool +def subtract(a: int, b: int) -> int: + """Subtract two numbers. + + Args: + a: first int + b: second int + """ + return a - b + +@tool +def divide(a: int, b: int) -> int: + """Divide two numbers. + + Args: + a: first int + b: second int + """ + if b == 0: + raise ValueError("Cannot divide by zero.") + return a / b + +@tool +def modulus(a: int, b: int) -> int: + """Get the modulus of two numbers. + + Args: + a: first int + b: second int + """ + return a % b + +@tool +def wiki_search(query: str) -> str: + """Search Wikipedia for a query and return maximum 2 results. + + Args: + query: The search query.""" + search_docs = WikipediaLoader(query=query, load_max_docs=2).load() + formatted_search_docs = "\n\n---\n\n".join( + [ + f'\n{doc.page_content}\n' + for doc in search_docs + ]) + return {"wiki_results": formatted_search_docs} + +@tool +def web_search(query: str) -> str: + """Search Tavily for a query and return maximum 3 results. + + Args: + query: The search query.""" + search_docs = TavilySearchResults(max_results=3).invoke(query=query) + formatted_search_docs = "\n\n---\n\n".join( + [ + f'\n{doc.page_content}\n' + for doc in search_docs + ]) + return {"web_results": formatted_search_docs} + +@tool +def arvix_search(query: str) -> str: + """Search Arxiv for a query and return maximum 3 result. + + Args: + query: The search query.""" + search_docs = ArxivLoader(query=query, load_max_docs=3).load() + formatted_search_docs = "\n\n---\n\n".join( + [ + f'\n{doc.page_content[:1000]}\n' + for doc in search_docs + ]) + return {"arvix_results": formatted_search_docs} + + + +# load the system prompt from the file +with open("system_prompt.txt", "r", encoding="utf-8") as f: + system_prompt = f.read() + +# System message +sys_msg = SystemMessage(content=system_prompt) + +# build a retriever +embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2") # dim=768 + +INDEX_PATH = "faiss_index" +if os.path.exists(INDEX_PATH): + vector_store = FAISS.load_local(INDEX_PATH, embeddings, allow_dangerous_deserialization=True) +else: + vector_store = FAISS.from_texts(["__init__"], embeddings) + vector_store.save_local(INDEX_PATH) + +create_retriever_tool = create_retriever_tool( + retriever=vector_store.as_retriever(), + name="Question Search", + description="A tool to retrieve similar questions from a local FAISS vector store." +) + + + +tools = [ + multiply, + add, + subtract, + divide, + modulus, + wiki_search, + web_search, + arvix_search, +] + +# Build graph function +def build_graph(provider: str = "groq"): + """Build the graph""" + # Load environment variables from .env file + if provider == "google": + # Google Gemini + llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash", temperature=0) + elif provider == "groq": + # Groq https://console.groq.com/docs/models + try: + llm = ChatGroq(model="deepseek-r1-distill-llama-70b", temperature=0) # optional : qwen-qwq-32b gemma2-9b-it + except Exception as e: + print(f"Error initializing Groq: {str(e)}") + raise + elif provider == "huggingface": + # TODO: Add huggingface endpoint + llm = ChatHuggingFace( + llm=HuggingFaceEndpoint( + url="https://api-inference.huggingface.co/models/Meta-DeepLearning/llama-2-7b-chat-hf", + temperature=0, + ), + ) + else: + raise ValueError("Invalid provider. Choose 'google', 'groq' or 'huggingface'.") + + # Bind tools to LLM + llm_with_tools = llm.bind_tools(tools) + + # Node + def assistant(state: MessagesState): + """Assistant node""" + try: + if not state["messages"] or not state["messages"][-1].content: + raise ValueError("Empty message content") + return {"messages": [llm_with_tools.invoke(state["messages"])]} + except Exception as e: + print(f"Error in assistant node: {str(e)}") + raise + + def retriever(state: MessagesState): + """Retriever node""" + try: + if not state["messages"] or not state["messages"][0].content: + raise ValueError("Empty message content") + similar_question = vector_store.similarity_search(state["messages"][0].content) + example_msg = HumanMessage( + content=f"Here I provide a similar question and answer for reference: \n\n{similar_question[0].page_content}", + ) + return {"messages": [sys_msg] + state["messages"] + [example_msg]} + except Exception as e: + print(f"Error in retriever node: {str(e)}") + raise + + builder = StateGraph(MessagesState) + builder.add_node("retriever", retriever) + builder.add_node("assistant", assistant) + builder.add_node("tools", ToolNode(tools)) + builder.add_edge(START, "retriever") + builder.add_edge("retriever", "assistant") + builder.add_conditional_edges( + "assistant", + tools_condition, + ) + builder.add_edge("tools", "assistant") + + # Compile graph + return builder.compile() + +# test +if __name__ == "__main__": + question = "When was a picture of St. Thomas Aquinas first added to the Wikipedia page on the Principle of double effect?" + # Build the graph + graph = build_graph(provider="groq") + # Run the graph + messages = [HumanMessage(content=question)] + messages = graph.invoke({"messages": messages}) + for m in messages["messages"]: + m.pretty_print() \ No newline at end of file diff --git a/utils/all_analysis.txt b/utils/all_analysis.txt new file mode 100644 index 0000000000000000000000000000000000000000..319e0e7d90c4a9dd4c1d8f181070b9eff7b56e9d --- /dev/null +++ b/utils/all_analysis.txt @@ -0,0 +1 @@ +{'flow': [{'block_key': 'event_whenflagclicked_1', 'opcode': 'event_whenflagclicked', 'type': 'hat', 'inputs': {}, 'fields': {}, 'shadow': False, 'topLevel': True, 'parent': None, 'next': 'data_setvariableto_1'}, {'block_key': 'data_setvariableto_1', 'opcode': 'data_setvariableto', 'type': 'stack', 'inputs': {'VALUE': 1}, 'fields': {'VARIABLE': ['score', None]}, 'shadow': False, 'topLevel': False, 'parent': 'event_whenflagclicked_1', 'next': 'motion_gotoxy_1'}, {'block_key': 'motion_gotoxy_1', 'opcode': 'motion_gotoxy', 'type': 'stack', 'inputs': {'X': 240, 'Y': -135}, 'fields': {}, 'shadow': False, 'topLevel': False, 'parent': 'data_setvariableto_1', 'next': 'data_setvariableto_2'}, {'block_key': 'data_setvariableto_2', 'opcode': 'data_setvariableto', 'type': 'stack', 'inputs': {'VALUE': 1}, 'fields': {'VARIABLE': ['speed', None]}, 'shadow': False, 'topLevel': False, 'parent': 'motion_gotoxy_1', 'next': 'data_showvariable_1'}, {'block_key': 'data_showvariable_1', 'opcode': 'data_showvariable', 'type': 'stack', 'inputs': {}, 'fields': {'VARIABLE': ['score', None]}, 'shadow': False, 'topLevel': False, 'parent': 'data_setvariableto_2', 'next': 'data_showvariable_2'}, {'block_key': 'data_showvariable_2', 'opcode': 'data_showvariable', 'type': 'stack', 'inputs': {}, 'fields': {'VARIABLE': ['speed', None]}, 'shadow': False, 'topLevel': False, 'parent': 'data_showvariable_1', 'next': 'control_forever_1'}, {'block_key': 'control_forever_1', 'opcode': 'control_forever', 'type': 'c_block', 'inputs': {}, 'fields': {}, 'shadow': False, 'topLevel': False, 'parent': 'data_showvariable_2', 'next': None}]} diff --git a/utils/all_analysis_trace.txt b/utils/all_analysis_trace.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/utils/all_generated_blocks.json b/utils/all_generated_blocks.json new file mode 100644 index 0000000000000000000000000000000000000000..a1088238202e018cca8f5cb5d9059d0d8c39cfd9 --- /dev/null +++ b/utils/all_generated_blocks.json @@ -0,0 +1,345 @@ +{ + "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": null, + "next": "data_setvariableto_1", + "sub_stacks": {} + }, + "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": 240 + }, + "Y": { + "kind": "value", + "value": -135 + } + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "id": "motion_gotoxy_1", + "parent": "data_setvariableto_1", + "next": "data_setvariableto_2", + "sub_stacks": {} + }, + "motion_xposition_1": { + "block_name": "(x position)", + "block_type": "Motion", + "block_shape": "Reporter Block", + "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "id": "motion_xposition_1", + "parent": "operator_lt_1", + "next": null, + "sub_stacks": {} + }, + "motion_setx_1": { + "block_name": "set x to ()", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": { + "X": { + "kind": "value", + "value": 240 + } + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "id": "motion_setx_1", + "parent": "control_if_1", + "next": null, + "sub_stacks": {} + }, + "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": {}, + "fields": {}, + "shadow": false, + "topLevel": false, + "sub_stacks": { + "SUBSTACK": [ + 2, + null + ] + }, + "id": "control_forever_1", + "parent": "data_showvariable_2", + "next": null + }, + "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_lt_1" + } + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "sub_stacks": { + "SUBSTACK": [ + 2, + null + ] + }, + "id": "control_if_1", + "parent": "control_forever_1", + "next": "control_if_2" + }, + "control_stop_1": { + "block_name": "stop [v]", + "block_type": "Control", + "block_shape": "Cap Block", + "op_code": "control_stop", + "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.", + "inputs": {}, + "fields": { + "STOP_OPTION": [ + "all ", + null + ] + }, + "shadow": false, + "topLevel": false, + "mutation": { + "tagName": "mutation", + "children": [], + "hasnext": "false" + }, + "id": "control_stop_1", + "parent": "event_broadcast_1", + "next": null, + "sub_stacks": {} + }, + "operator_lt_1": { + "block_name": "<() < ()>", + "block_type": "operator", + "block_shape": "Boolean Block", + "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": { + "OPERAND1": [ + 2, + "motion_xposition_1" + ], + "OPERAND2": [ + 1, + "-235" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "id": "operator_lt_1", + "parent": "control_if_1", + "next": null, + "sub_stacks": {} + }, + "sensing_touchingobject_1": { + "block_name": "", + "block_type": "Sensing", + "op_code": "sensing_touchingobject", + "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": { + "TOUCHINGOBJECTMENU": [ + 2, + "sensing_touchingobjectmenu_1" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": true, + "id": "sensing_touchingobject_1", + "parent": "control_if_2", + "next": null, + "sub_stacks": {} + }, + "sensing_touchingobjectmenu_1": { + "block_name": "touching object menu", + "block_type": "Sensing", + "block_shape": "Reporter Block", + "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, + "fields": { + "TOUCHINGOBJECTMENU": [ + "sprite1", + null + ] + }, + "shadow": true, + "topLevel": false, + "id": "sensing_touchingobjectmenu_1", + "parent": "sensing_touchingobject_1", + "next": null, + "sub_stacks": {} + }, + "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": "value", + "value": "Game Over" + } + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "id": "event_broadcast_1", + "parent": "control_if_2", + "next": "control_stop_1", + "sub_stacks": {} + }, + "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": 1 + } + }, + "fields": { + "VARIABLE": [ + "score", + null + ] + }, + "shadow": false, + "topLevel": false, + "id": "data_setvariableto_1", + "parent": "event_whenflagclicked_1", + "next": "motion_gotoxy_1", + "sub_stacks": {} + }, + "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", + null + ] + }, + "shadow": false, + "topLevel": false, + "id": "data_showvariable_1", + "parent": "data_setvariableto_2", + "next": "data_showvariable_2", + "sub_stacks": {} + }, + "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": [ + "speed", + null + ] + }, + "shadow": false, + "topLevel": false, + "id": "data_showvariable_2", + "parent": "data_showvariable_1", + "next": "control_forever_1", + "sub_stacks": {} + }, + "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": 1 + } + }, + "fields": { + "VARIABLE": [ + "speed", + null + ] + }, + "shadow": false, + "topLevel": false, + "id": "data_setvariableto_2", + "sub_stacks": {}, + "parent": "motion_gotoxy_1", + "next": "data_showvariable_1" + }, + "control_if_2": { + "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": "sensing_touchingobject_1" + } + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "sub_stacks": { + "SUBSTACK": [ + 2, + null + ] + }, + "id": "control_if_2", + "parent": "control_if_1", + "next": null + } +} \ No newline at end of file diff --git a/utils/block_builder_main.py b/utils/block_builder_main.py new file mode 100644 index 0000000000000000000000000000000000000000..4fa5f191b9b599bc825b4b8d2f61865a60d37628 --- /dev/null +++ b/utils/block_builder_main.py @@ -0,0 +1,446 @@ +import json +import copy +import re +from collections import defaultdict +import secrets +import string +from typing import Dict, Any, TypedDict +from plan_generator_10 import generate_plan,generate_blocks_from_opcodes,all_block_definitions + +################################################################################################################################################################# +#--------------------------------------------------[Security key id generation for the better understanding of keys]--------------------------------------------- +################################################################################################################################################################# + +def generate_secure_token(length=20): + charset = string.ascii_letters + string.digits + "!@#$%^&*()[]{}=+-_~" + return ''.join(secrets.choice(charset) for _ in range(length)) + +################################################################################################################################################################# +#--------------------------------------------------[Processed the two Skelton as input and generate refined skelton json]---------------------------------------- +################################################################################################################################################################# + +def process_scratch_blocks(all_generated_blocks, generated_output_json): + + processed_blocks = {} + + # Initialize dictionaries to store and reuse generated unique IDs + # This prevents creating multiple unique IDs for the same variable/broadcast across different blocks + variable_id_map = defaultdict(lambda: generate_secure_token(20)) + broadcast_id_map = defaultdict(lambda: generate_secure_token(20)) + + for block_id, gen_block_data in generated_output_json.items(): + processed_block = {} + all_gen_block_data = all_generated_blocks.get(block_id, {}) + + # Copy and update fields, inputs, next, parent, shadow, topLevel, mutation, and opcode + processed_block["opcode"] = all_gen_block_data.get("op_code", gen_block_data.get("op_code")) + processed_block["inputs"] = {} + processed_block["fields"] = {} + processed_block["shadow"] = all_gen_block_data.get("shadow", gen_block_data.get("shadow")) + processed_block["topLevel"] = all_gen_block_data.get("topLevel", gen_block_data.get("topLevel")) + processed_block["parent"] = all_gen_block_data.get("parent", gen_block_data.get("parent")) + processed_block["next"] = all_gen_block_data.get("next", gen_block_data.get("next")) + if "mutation" in all_gen_block_data: + processed_block["mutation"] = all_gen_block_data["mutation"] + + # Process inputs + if "inputs" in all_gen_block_data: + for input_name, input_data in all_gen_block_data["inputs"].items(): + if input_name in ["SUBSTACK", "CONDITION"]: + # These should always be type 2 + if isinstance(input_data, list) and len(input_data) == 2: + processed_block["inputs"][input_name] = [2, input_data[1]] + elif isinstance(input_data, dict) and input_data.get("kind") == "block": + processed_block["inputs"][input_name] = [2, input_data.get("block")] + else: # Fallback for unexpected formats, try to use the original if possible + processed_block["inputs"][input_name] = gen_block_data["inputs"].get(input_name, [2, None]) + + elif isinstance(input_data, dict): + if input_data.get("kind") == "value": + # Case 1: Direct value input + processed_block["inputs"][input_name] = [ + 1, + [ + 4, + str(input_data.get("value", "")) + ] + ] + elif input_data.get("kind") == "block": + # Case 3: Nested block input + existing_shadow_value = "" + if input_name in gen_block_data.get("inputs", {}) and \ + isinstance(gen_block_data["inputs"][input_name], list) and \ + len(gen_block_data["inputs"][input_name]) > 2 and \ + isinstance(gen_block_data["inputs"][input_name][2], list) and \ + len(gen_block_data["inputs"][input_name][2]) > 1: + existing_shadow_value = gen_block_data["inputs"][input_name][2][1] + + processed_block["inputs"][input_name] = [ + 3, + input_data.get("block", ""), + [ + 10, # Assuming 10 for number/string shadow + existing_shadow_value + ] + ] + elif input_data.get("kind") == "menu": + # Handle menu inputs like in event_broadcast + menu_option = input_data.get("option", "") + + # Generate or retrieve a unique ID for the broadcast message + broadcast_id = broadcast_id_map[menu_option] # Use defaultdict for unique IDs + + processed_block["inputs"][input_name] = [ + 1, + [ + 11, # This is typically the code for menu dropdowns + menu_option, + broadcast_id + ] + ] + elif isinstance(input_data, list): + # For cases like TOUCHINGOBJECTMENU, where input_data is a list [1, "block_id"] + processed_block["inputs"][input_name] = input_data + + + # Process fields + if "fields" in all_gen_block_data: + for field_name, field_value in all_gen_block_data["fields"].items(): + if field_name == "VARIABLE" and isinstance(field_value, list) and len(field_value) > 0: + # Generate or retrieve a unique ID for the variable + variable_name = field_value[0] + unique_id = variable_id_map[variable_name] # Use defaultdict for unique IDs + + processed_block["fields"][field_name] = [ + variable_name, + unique_id + ] + elif field_name == "STOP_OPTION": + processed_block["fields"][field_name] = [ + field_value[0], + None + ] + elif field_name == "TOUCHINGOBJECTMENU": + referenced_menu_block_id = all_gen_block_data["inputs"].get("TOUCHINGOBJECTMENU", [None, None])[1] + if referenced_menu_block_id and referenced_menu_block_id in all_generated_blocks: + menu_block = all_generated_blocks[referenced_menu_block_id] + menu_value = menu_block.get("fields", {}).get("TOUCHINGOBJECTMENU", ["", None])[0] + processed_block["fields"][field_name] = [menu_value, None] + else: + processed_block["fields"][field_name] = [field_value[0], None] + else: + processed_block["fields"][field_name] = field_value + + # Remove unwanted keys from the processed block + keys_to_remove = ["functionality", "block_shape", "id", "block_name", "block_type"] + for key in keys_to_remove: + if key in processed_block: + del processed_block[key] + + processed_blocks[block_id] = processed_block + return processed_blocks +################################################################################################################################################################# +#--------------------------------------------------[Unique secret key for skelton json to make sure it donot overwrite each other]------------------------------- +################################################################################################################################################################# + +def rename_blocks(block_json: dict, opcode_count: dict) -> tuple[dict, dict]: + """ + Replace each block key in block_json and each identifier in opcode_count + with a newly generated secure token. + + Args: + block_json: Mapping of block_key -> block_data. + opcode_count: Mapping of opcode -> list of block_keys. + + Returns: + A tuple of (new_block_json, new_opcode_count) with updated keys. + """ + # Step 1: Generate a secure token mapping for every existing block key + token_map = {} + for old_key in block_json.keys(): + # Ensure uniqueness in the unlikely event of a collision + while True: + new_key = generate_secure_token() + if new_key not in token_map.values(): + break + token_map[old_key] = new_key + + # Step 2: Rebuild block_json with new keys + new_block_json = {} + for old_key, block in block_json.items(): + new_key = token_map[old_key] + new_block_json[new_key] = block.copy() + + # Update parent and next references + if 'parent' in block and block['parent'] in token_map: + new_block_json[new_key]['parent'] = token_map[block['parent']] + if 'next' in block and block['next'] in token_map: + new_block_json[new_key]['next'] = token_map[block['next']] + + # Update inputs if they reference blocks + for inp_key, inp_val in block.get('inputs', {}).items(): + if isinstance(inp_val, list) and len(inp_val) == 2: + idx, ref = inp_val + if idx in (2, 3) and isinstance(ref, str) and ref in token_map: + new_block_json[new_key]['inputs'][inp_key] = [idx, token_map[ref]] + + # Step 3: Update opcode count map + new_opcode_count = {} + for opcode, key_list in opcode_count.items(): + new_opcode_count[opcode] = [token_map.get(k, k) for k in key_list] + + return new_block_json, new_opcode_count + +################################################################################################################################################################# +#--------------------------------------------------[Helper function to add Variables and Broadcasts [USed in main app file for main projectjson]]---------------- +################################################################################################################################################################# + +def variable_intialization(project_data): + """ + Updates variable and broadcast definitions in a Scratch project JSON, + populating the 'variables' and 'broadcasts' sections of the Stage target + and extracting initial values for variables. + + Args: + project_data (dict): The loaded JSON data of the Scratch project. + + Returns: + dict: The updated project JSON data. + """ + + stage_target = None + for target in project_data['targets']: + if target.get('isStage'): + stage_target = target + break + + if stage_target is None: + print("Error: Stage target not found in the project data.") + return project_data + + # Ensure 'variables' and 'broadcasts' exist in the Stage target + if "variables" not in stage_target: + stage_target["variables"] = {} + if "broadcasts" not in stage_target: + stage_target["broadcasts"] = {} + + # Helper function to recursively find and update variable/broadcast fields + def process_dict(obj): + if isinstance(obj, dict): + # Check for "data_setvariableto" opcode to extract initial values + if obj.get("opcode") == "data_setvariableto": + variable_field = obj.get("fields", {}).get("VARIABLE") + value_input = obj.get("inputs", {}).get("VALUE") + + if variable_field and isinstance(variable_field, list) and len(variable_field) == 2: + var_name = variable_field[0] + var_id = variable_field[1] + + initial_value = "" + if value_input and isinstance(value_input, list) and len(value_input) > 1 and \ + isinstance(value_input[1], list) and len(value_input[1]) > 1: + # Extract value from various formats, e.g., [1, [10, "0"]] or [3, [12, "score", "id"], [10, "0"]] + if value_input[1][0] == 10: # Direct value like [10, "0"] + initial_value = str(value_input[1][1]) + 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 + initial_value = str(value_input[2][1]) + elif isinstance(value_input[1], (str, int, float)): # For direct number/string inputs + initial_value = str(value_input[1]) + + + # Add/update the variable in the Stage's 'variables' with its initial value + stage_target["variables"][var_id] = [var_name, initial_value] + + + for key, value in obj.items(): + # Process variable definitions in 'fields' (for blocks that define variables like 'show variable') + if key == "VARIABLE" and isinstance(value, list) and len(value) == 2: + var_name = value[0] + var_id = value[1] + # Only add if not already defined with an initial value from set_variableto + if var_id not in stage_target["variables"]: + stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet + elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different + stage_target["variables"][var_id][0] = var_name + + + # Process broadcast definitions in 'inputs' (BROADCAST_INPUT) + elif key == "BROADCAST_INPUT" and isinstance(value, list) and len(value) == 2 and \ + isinstance(value[1], list) and len(value[1]) == 3 and value[1][0] == 11: + broadcast_name = value[1][1] + broadcast_id = value[1][2] + # Add/update the broadcast in the Stage's 'broadcasts' + stage_target["broadcasts"][broadcast_id] = broadcast_name + + # Process broadcast definitions in 'fields' (BROADCAST_OPTION) + elif key == "BROADCAST_OPTION" and isinstance(value, list) and len(value) == 2: + broadcast_name = value[0] + broadcast_id = value[1] + # Add/update the broadcast in the Stage's 'broadcasts' + stage_target["broadcasts"][broadcast_id] = broadcast_name + + # Recursively call for nested dictionaries or lists + process_dict(value) + elif isinstance(obj, list): + for i, item in enumerate(obj): + # Process variable references in 'inputs' (like [12, "score", "id"]) + if isinstance(item, list) and len(item) == 3 and item[0] == 12: + var_name = item[1] + var_id = item[2] + # Only add if not already defined with an initial value from set_variableto + if var_id not in stage_target["variables"]: + stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet + elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different + stage_target["variables"][var_id][0] = var_name + + process_dict(item) + + # Iterate through all targets to process their blocks + for target in project_data['targets']: + if "blocks" in target: + for block_id, block_data in target["blocks"].items(): + process_dict(block_data) + + return project_data + +def deduplicate_variables(project_data): + """ + Removes duplicate variable entries in the 'variables' dictionary of the Stage target, + prioritizing entries with non-empty values. + + Args: + project_data (dict): The loaded JSON data of the Scratch project. + + Returns: + dict: The updated project JSON data with deduplicated variables. + """ + + stage_target = None + for target in project_data['targets']: + if target.get('isStage'): + stage_target = target + break + + if stage_target is None: + print("Error: Stage target not found in the project data.") + return project_data + + if "variables" not in stage_target: + return project_data # No variables to deduplicate + + # Use a temporary dictionary to store the preferred variable entry by name + # Format: {variable_name: [variable_id, variable_name, variable_value]} + resolved_variables = {} + + for var_id, var_info in stage_target["variables"].items(): + var_name = var_info[0] + var_value = var_info[1] + + if var_name not in resolved_variables: + # If the variable name is not yet seen, add it + resolved_variables[var_name] = [var_id, var_name, var_value] + else: + # If the variable name is already seen, decide which one to keep + existing_id, existing_name, existing_value = resolved_variables[var_name] + + # Prioritize the entry with a non-empty value + if var_value != "" and existing_value == "": + resolved_variables[var_name] = [var_id, var_name, var_value] + # If both have non-empty values, or both are empty, keep the current one (arbitrary choice, but consistent) + # The current logic will effectively keep the last one encountered that has a value, + # or the very last one if all are empty. + elif var_value != "" and existing_value != "": + # If there are multiple non-empty values for the same variable name + # this keeps the one from the most recent iteration. + # For the given example, this will correctly keep "5". + resolved_variables[var_name] = [var_id, var_name, var_value] + elif var_value == "" and existing_value == "": + # If both are empty, just keep the current one (arbitrary) + resolved_variables[var_name] = [var_id, var_name, var_value] + + + # Reconstruct the 'variables' dictionary using the resolved entries + new_variables_dict = {} + for var_name, var_data in resolved_variables.items(): + var_id_to_keep = var_data[0] + var_name_to_keep = var_data[1] + var_value_to_keep = var_data[2] + new_variables_dict[var_id_to_keep] = [var_name_to_keep, var_value_to_keep] + + stage_target["variables"] = new_variables_dict + + return project_data + +def variable_adder_main(project_data): + try: + declare_variable_json= variable_intialization(project_data) + except Exception as e: + print(f"Error error in the variable initialization opcodes: {e}") + try: + processed_json= deduplicate_variables(declare_variable_json) + return + except Exception as e: + print(f"Error error in the variable initialization opcodes: {e}") + +################################################################################################################################################################# +#--------------------------------------------------[Helper main function]---------------------------------------------------------------------------------------- +################################################################################################################################################################# + +def block_builder(opcode_count,pseudo_code): + try: + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(opcode_count, all_block_definitions) + except Exception as e: + print(f"Error generating blocks from opcodes: {e}") + return {} + try: + all_generated_blocks = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code) + except Exception as e: + print(f"Error generating plan from blocks: {e}") + return {} + try: + processed_blocks= process_scratch_blocks(all_generated_blocks, generated_output_json) + except Exception as e: + print(f"Error processing Scratch blocks: {e}") + return {} + renamed_blocks, renamed_counts = rename_blocks(processed_blocks, initial_opcode_occurrences) + return renamed_blocks + +################################################################################################################################################################# +#--------------------------------------------------[Example use of the function here]---------------------------------------------------------------------------- +################################################################################################################################################################# + +initial_opcode_counts = [ + { + "opcode": "event_whenflagclicked", + "count": 1 + }, + { + "opcode": "data_setvariableto", + "count": 2 + }, + { + "opcode": "data_showvariable", + "count": 2 + }, + { + "opcode": "event_broadcast", + "count": 1 + } + ] +pseudo_code=""" +when green flag clicked + set [score v] to (0) + set [lives v] to (3) + show variable [score v] + show variable [lives v] + broadcast [Game Start v] +""" + +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) +all_generated_blocks = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code) +processed_blocks= process_scratch_blocks(all_generated_blocks, generated_output_json) +print(all_generated_blocks) +print("--------------\n\n") +print(processed_blocks) +print("--------------\n\n") +print(initial_opcode_occurrences) \ No newline at end of file diff --git a/utils/block_correcter.py b/utils/block_correcter.py new file mode 100644 index 0000000000000000000000000000000000000000..06814f5185572647fdbc71596b7c547d0abdd556 --- /dev/null +++ b/utils/block_correcter.py @@ -0,0 +1,143 @@ +import json +import secrets +import string +from collections import defaultdict + +def generate_secure_token(length=20): + charset = string.ascii_letters + string.digits + "!@#$%^&*()[]{}=+-_~" + return ''.join(secrets.choice(charset) for _ in range(length)) + +def process_scratch_blocks(all_generated_blocks, generated_output_json): + processed_blocks = {} + + # Initialize dictionaries to store and reuse generated unique IDs + # This prevents creating multiple unique IDs for the same variable/broadcast across different blocks + variable_id_map = defaultdict(lambda: generate_secure_token(20)) + broadcast_id_map = defaultdict(lambda: generate_secure_token(20)) + + for block_id, gen_block_data in generated_output_json.items(): + processed_block = {} + all_gen_block_data = all_generated_blocks.get(block_id, {}) + + # Copy and update fields, inputs, next, parent, shadow, topLevel, mutation, and opcode + processed_block["opcode"] = all_gen_block_data.get("op_code", gen_block_data.get("op_code")) + processed_block["inputs"] = {} + processed_block["fields"] = {} + processed_block["shadow"] = all_gen_block_data.get("shadow", gen_block_data.get("shadow")) + processed_block["topLevel"] = all_gen_block_data.get("topLevel", gen_block_data.get("topLevel")) + processed_block["parent"] = all_gen_block_data.get("parent", gen_block_data.get("parent")) + processed_block["next"] = all_gen_block_data.get("next", gen_block_data.get("next")) + if "mutation" in all_gen_block_data: + processed_block["mutation"] = all_gen_block_data["mutation"] + + # Process inputs + if "inputs" in all_gen_block_data: + for input_name, input_data in all_gen_block_data["inputs"].items(): + if input_name in ["SUBSTACK", "CONDITION"]: + # These should always be type 2 + if isinstance(input_data, list) and len(input_data) == 2: + processed_block["inputs"][input_name] = [2, input_data[1]] + elif isinstance(input_data, dict) and input_data.get("kind") == "block": + processed_block["inputs"][input_name] = [2, input_data.get("block")] + else: # Fallback for unexpected formats, try to use the original if possible + processed_block["inputs"][input_name] = gen_block_data["inputs"].get(input_name, [2, None]) + + elif isinstance(input_data, dict): + if input_data.get("kind") == "value": + # Case 1: Direct value input + processed_block["inputs"][input_name] = [ + 1, + [ + 4, + str(input_data.get("value", "")) + ] + ] + elif input_data.get("kind") == "block": + # Case 3: Nested block input + existing_shadow_value = "" + if input_name in gen_block_data.get("inputs", {}) and \ + isinstance(gen_block_data["inputs"][input_name], list) and \ + len(gen_block_data["inputs"][input_name]) > 2 and \ + isinstance(gen_block_data["inputs"][input_name][2], list) and \ + len(gen_block_data["inputs"][input_name][2]) > 1: + existing_shadow_value = gen_block_data["inputs"][input_name][2][1] + + processed_block["inputs"][input_name] = [ + 3, + input_data.get("block", ""), + [ + 10, # Assuming 10 for number/string shadow + existing_shadow_value + ] + ] + elif input_data.get("kind") == "menu": + # Handle menu inputs like in event_broadcast + menu_option = input_data.get("option", "") + + # Generate or retrieve a unique ID for the broadcast message + broadcast_id = broadcast_id_map[menu_option] # Use defaultdict for unique IDs + + processed_block["inputs"][input_name] = [ + 1, + [ + 11, # This is typically the code for menu dropdowns + menu_option, + broadcast_id + ] + ] + elif isinstance(input_data, list): + # For cases like TOUCHINGOBJECTMENU, where input_data is a list [1, "block_id"] + processed_block["inputs"][input_name] = input_data + + + # Process fields + if "fields" in all_gen_block_data: + for field_name, field_value in all_gen_block_data["fields"].items(): + if field_name == "VARIABLE" and isinstance(field_value, list) and len(field_value) > 0: + # Generate or retrieve a unique ID for the variable + variable_name = field_value[0] + unique_id = variable_id_map[variable_name] # Use defaultdict for unique IDs + + processed_block["fields"][field_name] = [ + variable_name, + unique_id + ] + elif field_name == "STOP_OPTION": + processed_block["fields"][field_name] = [ + field_value[0], + None + ] + elif field_name == "TOUCHINGOBJECTMENU": + referenced_menu_block_id = all_gen_block_data["inputs"].get("TOUCHINGOBJECTMENU", [None, None])[1] + if referenced_menu_block_id and referenced_menu_block_id in all_generated_blocks: + menu_block = all_generated_blocks[referenced_menu_block_id] + menu_value = menu_block.get("fields", {}).get("TOUCHINGOBJECTMENU", ["", None])[0] + processed_block["fields"][field_name] = [menu_value, None] + else: + processed_block["fields"][field_name] = [field_value[0], None] + else: + processed_block["fields"][field_name] = field_value + + # Remove unwanted keys from the processed block + keys_to_remove = ["functionality", "block_shape", "id", "block_name", "block_type"] + for key in keys_to_remove: + if key in processed_block: + del processed_block[key] + + processed_blocks[block_id] = processed_block + return processed_blocks + +# Path to your JSON file +if __name__ == "__main__": + all_generated_blocks_path = 'all_generated_blocks.json' + generated_output_json_path = 'generated_output_json.json' + + # Open and load the JSON files into Python dictionaries + with open(all_generated_blocks_path, 'r') as f: + all_generated_blocks_data = json.load(f) + + with open(generated_output_json_path, 'r') as f: + generated_output_json_data = json.load(f) + + processed_blocks = process_scratch_blocks(all_generated_blocks_data, generated_output_json_data) + print(json.dumps(processed_blocks, indent=4)) \ No newline at end of file diff --git a/utils/block_function.py b/utils/block_function.py new file mode 100644 index 0000000000000000000000000000000000000000..0a91ac6a09f66c85493688fa504f348886ff9155 --- /dev/null +++ b/utils/block_function.py @@ -0,0 +1,1147 @@ +import json +import copy +import re + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition. + It now correctly links parent and menu blocks using their generated unique keys, and handles various block categories. + It ensures that menu blocks are only generated as children of their respective parent blocks. + + Args: + opcode_counts (list): An array of objects, each with an 'opcode' and 'count' property. + Example: [{"opcode": "motion_gotoxy", "count": 1}] + all_block_definitions (dict): A comprehensive dictionary containing definitions for all block types. + + Returns: + tuple: A tuple containing: + - dict: A JSON object where keys are generated block IDs and values are the block definitions. + - dict: The opcode_occurrences dictionary for consistent unique key generation across functions. + """ + generated_blocks = {} + opcode_occurrences = {} # To keep track of how many times each opcode (main or menu) has been used for unique keys + + # Define explicit parent-menu relationships for linking purposes + # This maps main_opcode -> list of (input_field_name, menu_opcode) + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + # --- Step 1: Process explicitly requested opcodes and generate their instances and associated menus --- + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if not opcode: + print("Warning: Skipping item with missing 'opcode'.") + continue + + if opcode not in all_block_definitions: + print(f"Warning: Opcode '{opcode}' not found in all_block_definitions. Skipping.") + continue + + for _ in range(count): + # Increment occurrence count for the current main opcode + opcode_occurrences[opcode] = opcode_occurrences.get(opcode, 0) + 1 + main_block_instance_num = opcode_occurrences[opcode] + + main_block_unique_key = f"{opcode}_{main_block_instance_num}" + + # Create a deep copy of the main block definition + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + + # Set properties for a top-level main block + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False # Main blocks are typically not shadows + + generated_blocks[main_block_unique_key] = main_block_data + + # If this main block has associated menus, generate and link them now + if opcode in explicit_menu_links: + for input_field_name, menu_opcode_type in explicit_menu_links[opcode]: + if menu_opcode_type in all_block_definitions: + # Increment the occurrence for the menu block type + opcode_occurrences[menu_opcode_type] = opcode_occurrences.get(menu_opcode_type, 0) + 1 + menu_block_instance_num = opcode_occurrences[menu_opcode_type] + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode_type]) + + # Generate a unique key for this specific menu instance + menu_unique_key = f"{menu_opcode_type}_{menu_block_instance_num}" + + # Set properties for a shadow menu block + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_block_unique_key # Link menu to its parent instance + + # Update the main block's input to point to this unique menu instance + if input_field_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_field_name], list) and \ + len(main_block_data["inputs"][input_field_name]) > 1 and \ + main_block_data["inputs"][input_field_name][0] == 1: + + main_block_data["inputs"][input_field_name][1] = menu_unique_key + + generated_blocks[menu_unique_key] = menu_block_data + + return generated_blocks, opcode_occurrences + +def interpret_pseudo_code_and_update_blocks(generated_blocks_json, pseudo_code, all_block_definitions, opcode_occurrences): + """ + Interprets pseudo-code to update the generated Scratch blocks, replacing static values + with dynamic values and establishing stacking/nesting logic. + + Args: + generated_blocks_json (dict): The JSON object of pre-generated blocks. + pseudo_code (str): The pseudo-code string to interpret. + all_block_definitions (dict): A comprehensive dictionary containing definitions for all block types. + opcode_occurrences (dict): A dictionary to keep track of opcode occurrences for unique key generation. + + Returns: + dict: The updated JSON object of Scratch blocks. + """ + updated_blocks = copy.deepcopy(generated_blocks_json) + + # Helper to find a block by opcode and optionally by a unique part of its key + def find_block_by_opcode(opcode_to_find, instance_num=None, parent_key=None): + for key, block in updated_blocks.items(): + if block["opcode"] == opcode_to_find: + if instance_num is not None: + # Check if the key ends with the instance number + if key.endswith(f"_{instance_num}"): + return key, block + elif parent_key is not None: + # For menu blocks, check if their parent matches + if block.get("shadow") and block.get("parent") == parent_key: + return key, block + else: + # Return the first one found if no specific instance is needed + return key, block + return None, None + + # Helper to get a unique key for a new block if needed + def get_unique_key(opcode_prefix): + count = 1 + while f"{opcode_prefix}_{count}" in updated_blocks: + count += 1 + return f"{opcode_prefix}_{count}" + + lines = [line.strip() for line in pseudo_code.strip().split('\n') if line.strip()] + + # Track the current script and nesting + current_script_head = None + current_parent_stack = [] # Stores (parent_block_key, indent_level, last_child_key) + indent_level = 0 + + # Create a mapping from block name patterns to their opcodes and input details + # Prioritize more specific patterns first + pseudo_code_to_opcode_map = { + re.compile(r"when green flag clicked"): {"opcode": "event_whenflagclicked"}, + re.compile(r"go to x: \((.+?)\) y: \((.+?)\)"): {"opcode": "motion_gotoxy", "input_names": ["X", "Y"]}, + re.compile(r"set \[(.+?) v\] to (.+)"): {"opcode": "data_setvariableto", "field_name": "VARIABLE", "input_name": "VALUE"}, + re.compile(r"change \[(.+?) v\] by \((.+)\)"): {"opcode": "data_changevariableby", "field_name": "VARIABLE", "input_name": "VALUE"}, + re.compile(r"show variable \[(.+?) v\]"): {"opcode": "data_showvariable", "field_name": "VARIABLE"}, + re.compile(r"hide variable \[(.+?) v\]"): {"opcode": "data_hidevariable", "field_name": "VARIABLE"}, + re.compile(r"forever"): {"opcode": "control_forever"}, + re.compile(r"glide \((.+?)\) seconds to x: \((.+?)\) y: \((.+?)\)"): {"opcode": "motion_glidesecstoxy", "input_names": ["SECS", "X", "Y"]}, + re.compile(r"if <\((.+)\) < \((.+)\)> then"): {"opcode": "control_if", "input_names": ["OPERAND1", "OPERAND2"], "condition_opcode": "operator_lt"}, + re.compile(r"if then"): {"opcode": "control_if", "input_name": "TOUCHINGOBJECTMENU", "condition_opcode": "sensing_touchingobject"}, + re.compile(r"set x to \((.+?)\)"): {"opcode": "motion_setx", "input_name": "X"}, + re.compile(r"broadcast \[(.+?) v\]"): {"opcode": "event_broadcast", "input_name": "BROADCAST_INPUT"}, + re.compile(r"stop \[(.+?) v\]"): {"opcode": "control_stop", "field_name": "STOP_OPTION"}, + re.compile(r"end"): {"opcode": "end_block"}, # Special marker for script end/C-block end + } + + # Create a reverse lookup for reporter block opcodes based on their pseudo-code representation + reporter_opcode_lookup = {} + for opcode, definition in all_block_definitions.items(): + if definition.get("block_shape") == "Reporter Block": + block_name = definition.get("block_name") + if block_name: + # Remove parentheses for matching + clean_name = block_name.replace("(", "").replace(")", "").strip() + reporter_opcode_lookup[clean_name] = opcode + # Handle cases like "x position" vs "(x position)" + if clean_name not in reporter_opcode_lookup: + reporter_opcode_lookup[clean_name] = opcode + + # Function to create a new block instance + def create_block_instance(opcode, opcode_occurrences, parent_key=None, is_shadow=False, is_top_level=False): + # Ensure unique key generation is consistent + opcode_occurrences[opcode] = opcode_occurrences.get(opcode, 0) + 1 + unique_key = f"{opcode}_{opcode_occurrences[opcode]}" + + new_block = copy.deepcopy(all_block_definitions.get(opcode, {})) + if not new_block: + print(f"Error: Definition for opcode '{opcode}' not found.") + return None, None + + new_block["parent"] = parent_key + new_block["next"] = None # Will be set by stacking logic + new_block["topLevel"] = is_top_level + new_block["shadow"] = is_shadow + + # Clear inputs/fields to be populated by pseudo-code parsing + if "inputs" in new_block: + new_block["inputs"] = {k: copy.deepcopy(v) for k, v in new_block["inputs"].items()} # Deep copy inputs + for input_name in new_block["inputs"]: + if isinstance(new_block["inputs"][input_name], list) and len(new_block["inputs"][input_name]) > 1: + # Reset input value, keep type 1 for block reference or type 4/10 for literal + if new_block["inputs"][input_name][0] == 1: + new_block["inputs"][input_name][1] = None # Placeholder for linked block ID + else: + new_block["inputs"][input_name][1] = ["", ""] # Default empty value + if "fields" in new_block: + new_block["fields"] = {k: copy.deepcopy(v) for k, v in new_block["fields"].items()} # Deep copy fields + for field_name in new_block["fields"]: + if isinstance(new_block["fields"][field_name], list) and len(new_block["fields"][field_name]) > 0: + new_block["fields"][field_name][0] = "" # Reset field value + + updated_blocks[unique_key] = new_block + return unique_key, new_block + + # Helper to parse input values + def parse_input_value(value_str): + value_str = value_str.strip() + # Handle numeric values (including those with + or - prefix) + if re.fullmatch(r"[-+]?\d+(\.\d+)?", value_str): + return [4, value_str] # Type 4 for number + # Handle string literals (e.g., "Hello!") + if value_str.startswith('"') and value_str.endswith('"'): + return [10, value_str.strip('"')] # Type 10 for string + # Handle variable/list names (e.g., [score v], [my list v]) + if value_str.startswith('[') and value_str.endswith(']'): + var_name = value_str[1:-1].replace(' v', '').strip() + # For inputs that expect a variable, we might need a data_variable block + # For now, if it's a variable reference in an input, we'll return its name. + # The calling context (e.g., set variable's field vs. an input) will determine type. + return [12, var_name] # Custom type 12 for variable name, to be resolved later + + # Handle nested reporter blocks (e.g., (x position)) + if value_str.startswith('(') and value_str.endswith(')'): + inner_content = value_str[1:-1].strip() + # Check if it's a known reporter block + if inner_content in reporter_opcode_lookup: + return [3, reporter_opcode_lookup[inner_content]] # Type 3 for reporter block reference + # If not a known reporter, treat as a number or string + if re.fullmatch(r"[-+]?\d+(\.\d+)?", inner_content): + return [4, inner_content] + return [10, inner_content] # Default to string if not found in reporters + + # Handle boolean conditions (e.g., <(x position) < (-235)>) - these are usually handled by parent regex + if value_str.startswith('<') and value_str.endswith('>'): + inner_condition = value_str[1:-1].strip() + # This is typically handled by the regex that matched the 'if' block itself. + # If this is called for a standalone boolean, it would be a reporter. + for op, def_ in all_block_definitions.items(): + if def_.get("block_shape") == "Boolean Block" and def_.get("block_name") and \ + def_["block_name"].replace("<", "").replace(">", "").strip() == inner_condition: + return [2, op] # Type 2 for boolean block reference + return [10, inner_condition] # Default to string if not found + + return [10, value_str] # Default to string literal + + + # Main parsing loop + block_stack = [] # (block_key, indent_level, last_child_key_in_scope) for tracking nesting + + for line_idx, raw_line in enumerate(lines): + current_line_indent = len(raw_line) - len(raw_line.lstrip()) + line = raw_line.strip() + + # Adjust block_stack based on current indent level + while block_stack and current_line_indent <= block_stack[-1][1]: + block_stack.pop() + + matched_block_info = None + matched_values = None + + # Try to match the line against known block patterns + for pattern_regex, info in pseudo_code_to_opcode_map.items(): + match = pattern_regex.match(line) + if match: + matched_block_info = info + matched_values = match.groups() + break + + if not matched_block_info: + print(f"Warning: Could not interpret line: '{line}'") + continue + + opcode = matched_block_info["opcode"] + + # Handle 'end' block separately as it signifies closing a C-block + if opcode == "end_block": + if block_stack: + block_stack.pop() # Pop the C-block parent + continue + + parent_key = None + if block_stack: + parent_key = block_stack[-1][0] # The last block on the stack is the parent + + # Create the new block instance + new_block_key, new_block_data = create_block_instance( + opcode, + opcode_occurrences, + parent_key=parent_key, + is_top_level=(parent_key is None) + ) + if not new_block_key: + continue + + # Link to previous block in the same script/nesting level + if block_stack: + # Update the 'next' of the previous block in the current scope + last_child_key_in_scope = block_stack[-1][2] if len(block_stack[-1]) > 2 else None + if last_child_key_in_scope and last_child_key_in_scope in updated_blocks: + updated_blocks[last_child_key_in_scope]["next"] = new_block_key + + # Update the last child in the current scope + block_stack[-1] = (block_stack[-1][0], block_stack[-1][1], new_block_key) + + # Populate inputs and fields + if matched_values: + # Handle specific block types with their inputs/fields + if opcode == "motion_gotoxy": + x_val = parse_input_value(matched_values[0]) + y_val = parse_input_value(matched_values[1]) + new_block_data["inputs"]["X"][1] = x_val[1] + new_block_data["inputs"]["Y"][1] = y_val[1] + new_block_data["inputs"]["X"][0] = x_val[0] + new_block_data["inputs"]["Y"][0] = y_val[0] + elif opcode == "data_setvariableto": + var_name = matched_values[0].replace(' v', '').strip() + value_parsed = parse_input_value(matched_values[1]) + new_block_data["fields"]["VARIABLE"][0] = var_name + # Assuming variable ID is generated elsewhere or can be looked up + new_block_data["fields"]["VARIABLE"][1] = f"`var_{var_name}" # Placeholder for variable ID + new_block_data["inputs"]["VALUE"][0] = value_parsed[0] + new_block_data["inputs"]["VALUE"][1] = value_parsed[1] + elif opcode == "data_showvariable": + var_name = matched_values[0].replace(' v', '').strip() + new_block_data["fields"]["VARIABLE"][0] = var_name + new_block_data["fields"]["VARIABLE"][1] = f"`var_{var_name}" # Placeholder for variable ID + elif opcode == "motion_glidesecstoxy": + secs_val = parse_input_value(matched_values[0]) + x_val = parse_input_value(matched_values[1]) + y_val = parse_input_value(matched_values[2]) + new_block_data["inputs"]["SECS"][1] = secs_val[1] + new_block_data["inputs"]["X"][1] = x_val[1] + new_block_data["inputs"]["Y"][1] = y_val[1] + new_block_data["inputs"]["SECS"][0] = secs_val[0] + new_block_data["inputs"]["X"][0] = x_val[0] + new_block_data["inputs"]["Y"][0] = y_val[0] + elif opcode == "motion_setx": + x_val = parse_input_value(matched_values[0]) + new_block_data["inputs"]["X"][1] = x_val[1] + new_block_data["inputs"]["X"][0] = x_val[0] + elif opcode == "control_if": + condition_opcode = matched_block_info["condition_opcode"] + + if condition_opcode == "operator_lt": + op1_str = matched_values[0].strip() + op2_str = matched_values[1].strip() + + op1_parsed = parse_input_value(op1_str) + op2_parsed = parse_input_value(op2_str) + + # Create operator_lt block as a shadow input for the IF condition + lt_block_key, lt_block_data = create_block_instance( + "operator_lt", + opcode_occurrences, + parent_key=new_block_key, + is_shadow=True, + is_top_level=False + ) + if lt_block_key: + new_block_data["inputs"]["CONDITION"][1] = lt_block_key + new_block_data["inputs"]["CONDITION"][0] = 2 # Type 2 for boolean block reference + + # Populate operator_lt inputs + if op1_parsed[0] == 3: # If it's a reporter block + op1_reporter_key, op1_reporter_data = create_block_instance( + op1_parsed[1], # Opcode of the reporter + opcode_occurrences, + parent_key=lt_block_key, + is_shadow=True, + is_top_level=False + ) + if op1_reporter_key: + lt_block_data["inputs"]["OPERAND1"][1] = op1_reporter_key + lt_block_data["inputs"]["OPERAND1"][0] = 3 + else: # Literal value + lt_block_data["inputs"]["OPERAND1"][1] = op1_parsed[1] + lt_block_data["inputs"]["OPERAND1"][0] = op1_parsed[0] + + lt_block_data["inputs"]["OPERAND2"][1] = op2_parsed[1] + lt_block_data["inputs"]["OPERAND2"][0] = op2_parsed[0] + + elif condition_opcode == "sensing_touchingobject": + sprite_name = matched_values[0].replace(' v', '').strip() + touching_opcode = "sensing_touchingobject" + + touching_block_key, touching_block_data = create_block_instance( + touching_opcode, + opcode_occurrences, + parent_key=new_block_key, + is_shadow=True, + is_top_level=False + ) + if touching_block_key: + new_block_data["inputs"]["CONDITION"][1] = touching_block_key + new_block_data["inputs"]["CONDITION"][0] = 2 # Type 2 for boolean block reference + + # Create the menu block for TOUCHINGOBJECTMENU + menu_opcode = "sensing_touchingobjectmenu" + menu_key, menu_data = create_block_instance( + menu_opcode, + opcode_occurrences, + parent_key=touching_block_key, + is_shadow=True, + is_top_level=False + ) + if menu_key: + touching_block_data["inputs"]["TOUCHINGOBJECTMENU"][1] = menu_key + touching_block_data["inputs"]["TOUCHINGOBJECTMENU"][0] = 1 # Type 1 for block reference + menu_data["fields"]["TOUCHINGOBJECTMENU"][0] = sprite_name + else: + print(f"Warning: Could not create touching object block for condition: '{line}'") + + + elif opcode == "control_forever": + # Forever blocks are C-blocks, so push them onto the stack + block_stack.append((new_block_key, current_line_indent, None)) # (parent_key, indent, last_child_key) + elif opcode == "control_stop": + option = matched_values[0].replace(' v', '').strip() + new_block_data["fields"]["STOP_OPTION"][0] = option + elif opcode == "event_broadcast": + message = matched_values[0].replace(' v', '').strip() + # For broadcast, the input is usually a string literal or a variable + new_block_data["inputs"]["BROADCAST_INPUT"][0] = 11 # Type 11 for broadcast input (string or variable) + new_block_data["inputs"]["BROADCAST_INPUT"][1] = [10, message] # Assume string literal for now + # A more robust solution would create a data_variable block if it's a variable. + + # For C-blocks, push onto stack to track nesting + if all_block_definitions[opcode].get("block_shape") == "C-Block" and opcode != "control_if": # if is handled above + block_stack.append((new_block_key, current_line_indent, None)) # (parent_key, indent, last_child_key) + + + return updated_blocks + +# --- Consolidated Block Definitions from all provided JSONs --- +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "opcode": "motion_movesteps", "next": None, "parent": None, + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 464, "y": -416 + }, + "motion_turnright": { + "opcode": "motion_turnright", "next": None, "parent": None, + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 467, "y": -316 + }, + "motion_turnleft": { + "opcode": "motion_turnleft", "next": None, "parent": None, + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 464, "y": -210 + }, + "motion_goto": { + "opcode": "motion_goto", "next": None, "parent": None, + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 465, "y": -95 + }, + "motion_goto_menu": { + "opcode": "motion_goto_menu", "next": None, "parent": "motion_goto", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "opcode": "motion_gotoxy", "next": None, "parent": None, + "inputs": {"X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 468, "y": 12 + }, + "motion_glideto": { + "opcode": "motion_glideto", "next": None, "parent": None, + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 470, "y": 129 + }, + "motion_glideto_menu": { + "opcode": "motion_glideto_menu", "next": None, "parent": "motion_glideto", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "opcode": "motion_glidesecstoxy", "next": None, "parent": None, + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 476, "y": 239 + }, + "motion_pointindirection": { + "opcode": "motion_pointindirection", "next": None, "parent": None, + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 493, "y": 361 + }, + "motion_pointtowards": { + "opcode": "motion_pointtowards", "next": None, "parent": None, + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 492, "y": 463 + }, + "motion_pointtowards_menu": { + "opcode": "motion_pointtowards_menu", "next": None, "parent": "motion_pointtowards", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "opcode": "motion_changexby", "next": None, "parent": None, + "inputs": {"DX": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 851, "y": -409 + }, + "motion_setx": { + "opcode": "motion_setx", "next": None, "parent": None, + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 864, "y": -194 + }, + "motion_changeyby": { + "opcode": "motion_changeyby", "next": None, "parent": None, + "inputs": {"DY": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 861, "y": -61 + }, + "motion_sety": { + "opcode": "motion_sety", "next": None, "parent": None, + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 864, "y": 66 + }, + "motion_ifonedgebounce": { + "opcode": "motion_ifonedgebounce", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1131, "y": -397 + }, + "motion_setrotationstyle": { + "opcode": "motion_setrotationstyle", "next": None, "parent": None, + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True, + "x": 1128, "y": -287 + }, + "motion_xposition": { + "opcode": "motion_xposition", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1193, "y": -136 + }, + "motion_yposition": { + "opcode": "motion_yposition", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1181, "y": -64 + }, + "motion_direction": { + "opcode": "motion_direction", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1188, "y": 21 + }, + + # control_block.json + "control_wait": { + "opcode": "control_wait", "next": None, "parent": None, + "inputs": {"DURATION": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 337, "y": 129 + }, + "control_repeat": { + "opcode": "control_repeat", "next": None, "parent": None, + "inputs": {"TIMES": [1, [6, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 348, "y": 265 + }, + "control_forever": { + "opcode": "control_forever", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 334, "y": 439 + }, + "control_if": { + "opcode": "control_if", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 331, "y": 597 + }, + "control_if_else": { + "opcode": "control_if_else", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 335, "y": 779 + }, + "control_wait_until": { + "opcode": "control_wait_until", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 676, "y": 285 + }, + "control_repeat_until": { + "opcode": "control_repeat_until", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 692, "y": 381 + }, + "control_stop": { + "opcode": "control_stop", "next": None, "parent": None, + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, + "x": 708, "y": 545, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "opcode": "control_start_as_clone", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 665, "y": 672 + }, + "control_create_clone_of": { + "opcode": "control_create_clone_of", "next": None, "parent": None, + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 648, "y": 797 + }, + "control_create_clone_of_menu": { + "opcode": "control_create_clone_of_menu", "next": None, "parent": "control_create_clone_of", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "opcode": "control_delete_this_clone", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 642, "y": 914 + }, + + # data_block.json + "data_setvariableto": { + "opcode": "data_setvariableto", "next": None, "parent": None, + "inputs": {"VALUE": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True, + "x": 348, "y": 241 + }, + "data_changevariableby": { + "opcode": "data_changevariableby", "next": None, "parent": None, + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True, + "x": 313, "y": 363 + }, + "data_showvariable": { + "opcode": "data_showvariable", "next": None, "parent": None, + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True, + "x": 415, "y": 473 + }, + "data_hidevariable": { + "opcode": "data_hidevariable", "next": None, "parent": None, + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True, + "x": 319, "y": 587 + }, + "data_addtolist": { + "opcode": "data_addtolist", "next": None, "parent": None, + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 385, "y": 109 + }, + "data_deleteoflist": { + "opcode": "data_deleteoflist", "next": None, "parent": None, + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 384, "y": 244 + }, + "data_deletealloflist": { + "opcode": "data_deletealloflist", "next": None, "parent": None, + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 387, "y": 374 + }, + "data_insertatlist": { + "opcode": "data_insertatlist", "next": None, "parent": None, + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 366, "y": 527 + }, + "data_replaceitemoflist": { + "opcode": "data_replaceitemoflist", "next": None, "parent": None, + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 365, "y": 657 + }, + "data_itemoflist": { + "opcode": "data_itemoflist", "next": None, "parent": None, + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 862, "y": 117 + }, + "data_itemnumoflist": { + "opcode": "data_itemnumoflist", "next": None, "parent": None, + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 883, "y": 238 + }, + "data_lengthoflist": { + "opcode": "data_lengthoflist", "next": None, "parent": None, + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 876, "y": 342 + }, + "data_listcontainsitem": { + "opcode": "data_listcontainsitem", "next": None, "parent": None, + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 871, "y": 463 + }, + "data_showlist": { + "opcode": "data_showlist", "next": None, "parent": None, + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 931, "y": 563 + }, + "data_hidelist": { + "opcode": "data_hidelist", "next": None, "parent": None, + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 962, "y": 716 + }, + + # event_block.json + "event_whenflagclicked": { + "opcode": "event_whenflagclicked", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 166, "y": -422 + }, + "event_whenkeypressed": { + "opcode": "event_whenkeypressed", "next": None, "parent": None, + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": False, "topLevel": True, + "x": 151, "y": -329 + }, + "event_whenthisspriteclicked": { + "opcode": "event_whenthisspriteclicked", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 156, "y": -223 + }, + "event_whenbackdropswitchesto": { + "opcode": "event_whenbackdropswitchesto", "next": None, "parent": None, + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True, + "x": 148, "y": -101 + }, + "event_whengreaterthan": { + "opcode": "event_whengreaterthan", "next": None, "parent": None, + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True, + "x": 150, "y": 10 + }, + "event_whenbroadcastreceived": { + "opcode": "event_whenbroadcastreceived", "next": None, "parent": None, + "inputs": {}, "fields": {"BROADCAST_OPTION": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True, + "x": 141, "y": 118 + }, + "event_broadcast": { + "opcode": "event_broadcast", "next": None, "parent": None, + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 151, "y": 229 + }, + "event_broadcastandwait": { + "opcode": "event_broadcastandwait", "next": None, "parent": None, + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 157, "y": 340 + }, + + # look_block.json + "looks_sayforsecs": { + "opcode": "looks_sayforsecs", "next": None, "parent": None, + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 408, "y": 91 + }, + "looks_say": { + "opcode": "looks_say", "next": None, "parent": None, + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 413, "y": 213 + }, + "looks_thinkforsecs": { + "opcode": "looks_thinkforsecs", "next": None, "parent": None, + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 413, "y": 317 + }, + "looks_think": { + "opcode": "looks_think", "next": None, "parent": None, + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 412, "y": 432 + }, + "looks_switchcostumeto": { + "opcode": "looks_switchcostumeto", "next": None, "parent": None, + "inputs": {"COSTUME": [1, "8;bti4wv(iH9nkOacCJ|"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 411, "y": 555 + }, + "looks_costume": { + "opcode": "looks_costume", "next": None, "parent": "Q#a,6LPWHqo9-0Nu*[SV", + "inputs": {}, "fields": {"COSTUME": ["costume2", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "opcode": "looks_nextcostume", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 419, "y": 687 + }, + "looks_switchbackdropto": { + "opcode": "looks_switchbackdropto", "next": None, "parent": None, + "inputs": {"BACKDROP": [1, "-?yeX}29V*wd6W:unW0i"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 901, "y": 91 + }, + "looks_backdrops": { + "opcode": "looks_backdrops", "next": None, "parent": "`Wm^p~l[(IWzc1|wNv*.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_changesizeby": { + "opcode": "looks_changesizeby", "next": None, "parent": None, + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 895, "y": 192 + }, + "looks_setsizeto": { + "opcode": "looks_setsizeto", "next": None, "parent": None, + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 896, "y": 303 + }, + "looks_changeeffectby": { + "opcode": "looks_changeeffectby", "next": None, "parent": None, + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True, + "x": 892, "y": 416 + }, + "looks_seteffectto": { + "opcode": "looks_seteffectto", "next": None, "parent": None, + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True, + "x": 902, "y": 527 + }, + "looks_cleargraphiceffects": { + "opcode": "looks_cleargraphiceffects", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 902, "y": 638 + }, + "looks_show": { + "opcode": "looks_show", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 908, "y": 758 + }, + "looks_hide": { + "opcode": "looks_hide", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 455, "y": 861 + }, + "looks_gotofrontback": { + "opcode": "looks_gotofrontback", "next": None, "parent": None, + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True, + "x": 853, "y": 878 + }, + "looks_goforwardbackwardlayers": { + "opcode": "looks_goforwardbackwardlayers", "next": None, "parent": None, + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True, + "x": 851, "y": 999 + }, + "looks_costumenumbername": { + "opcode": "looks_costumenumbername", "next": None, "parent": None, + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True, + "x": 458, "y": 1007 + }, + "looks_backdropnumbername": { + "opcode": "looks_backdropnumbername", "next": None, "parent": None, + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True, + "x": 1242, "y": 753 + }, + "looks_size": { + "opcode": "looks_size", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1249, "y": 876 + }, + + # operator_block.json + "operator_add": { + "opcode": "operator_add", "next": None, "parent": None, + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 128, "y": 153 + }, + "operator_subtract": { + "opcode": "operator_subtract", "next": None, "parent": None, + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 134, "y": 214 + }, + "operator_multiply": { + "opcode": "operator_multiply", "next": None, "parent": None, + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 134, "y": 278 + }, + "operator_divide": { + "opcode": "operator_divide", "next": None, "parent": None, + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 138, "y": 359 + }, + "operator_random": { + "opcode": "operator_random", "next": None, "parent": None, + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 311, "y": 157 + }, + "operator_gt": { + "opcode": "operator_gt", "next": None, "parent": None, + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 348, "y": 217 + }, + "operator_lt": { + "opcode": "operator_lt", "next": None, "parent": None, + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 345, "y": 286 + }, + "operator_equals": { + "opcode": "operator_equals", "next": None, "parent": None, + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 345, "y": 372 + }, + "operator_and": { + "opcode": "operator_and", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 701, "y": 158 + }, + "operator_or": { + "opcode": "operator_or", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 705, "y": 222 + }, + "operator_not": { + "opcode": "operator_not", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 734, "y": 283 + }, + "operator_join": { + "opcode": "operator_join", "next": None, "parent": None, + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 663, "y": 378 + }, + "operator_letter_of": { + "opcode": "operator_letter_of", "next": None, "parent": None, + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 664, "y": 445 + }, + "operator_length": { + "opcode": "operator_length", "next": None, "parent": None, + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 664, "y": 521 + }, + "operator_contains": { + "opcode": "operator_contains", "next": None, "parent": None, + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 634, "y": 599 + }, + "operator_mod": { + "opcode": "operator_mod", "next": None, "parent": None, + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 295, "y": 594 + }, + "operator_round": { + "opcode": "operator_round", "next": None, "parent": None, + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 307, "y": 674 + }, + "operator_mathop": { + "opcode": "operator_mathop", "next": None, "parent": None, + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True, + "x": 280, "y": 754 + }, + + # sensing_block.json + "sensing_touchingobject": { + "opcode": "sensing_touchingobject", "next": None, "parent": None, + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 359, "y": 116 + }, + "sensing_touchingobjectmenu": { + "opcode": "sensing_touchingobjectmenu", "next": None, "parent": "sensing_touchingobject", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "opcode": "sensing_touchingcolor", "next": None, "parent": None, + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 360, "y": 188 + }, + "sensing_coloristouchingcolor": { + "opcode": "sensing_coloristouchingcolor", "next": None, "parent": None, + "inputs": {"COLOR": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 348, "y": 277 + }, + "sensing_askandwait": { + "opcode": "sensing_askandwait", "next": None, "parent": None, + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 338, "y": 354 + }, + "sensing_answer": { + "opcode": "sensing_answer", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 782, "y": 111 + }, + "sensing_keypressed": { + "opcode": "sensing_keypressed", "next": None, "parent": None, + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 762, "y": 207 + }, + "sensing_keyoptions": { + "opcode": "sensing_keyoptions", "next": None, "parent": "sensing_keypressed", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "opcode": "sensing_mousedown", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 822, "y": 422 + }, + "sensing_mousex": { + "opcode": "sensing_mousex", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 302, "y": 528 + }, + "sensing_mousey": { + "opcode": "sensing_mousey", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 668, "y": 547 + }, + "sensing_setdragmode": { + "opcode": "sensing_setdragmode", "next": None, "parent": None, + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True, + "x": 950, "y": 574 + }, + "sensing_loudness": { + "opcode": "sensing_loudness", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 658, "y": 703 + }, + "sensing_timer": { + "opcode": "sensing_timer", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 459, "y": 671 + }, + "sensing_resettimer": { + "opcode": "sensing_resettimer", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 462, "y": 781 + }, + "sensing_of": { + "opcode": "sensing_of", "next": None, "parent": None, + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True, + "x": 997, "y": 754 + }, + "sensing_of_object_menu": { + "opcode": "sensing_of_object_menu", "next": None, "parent": "sensing_of", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "opcode": "sensing_current", "next": None, "parent": None, + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True, + "x": 627, "y": 884 + }, + "sensing_dayssince2000": { + "opcode": "sensing_dayssince2000", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 959, "y": 903 + }, + "sensing_username": { + "opcode": "sensing_username", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 833, "y": 757 + }, + + # sound_block.json + "sound_playuntildone": { + "opcode": "sound_playuntildone", "next": None, "parent": None, + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 253, "y": 17 + }, + "sound_sounds_menu": { + "opcode": "sound_sounds_menu", "next": None, "parent": "sound_playuntildone and sound_play", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "opcode": "sound_play", "next": None, "parent": None, + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 245, "y": 122 + }, + "sound_stopallsounds": { + "opcode": "sound_stopallsounds", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 253, "y": 245 + }, + "sound_changeeffectby": { + "opcode": "sound_changeeffectby", "next": None, "parent": None, + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True, + "x": 653, "y": 14 + }, + "sound_seteffectto": { + "opcode": "sound_seteffectto", "next": None, "parent": None, + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True, + "x": 653, "y": 139 + }, + "sound_cleareffects": { + "opcode": "sound_cleareffects", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 651, "y": 242 + }, + "sound_changevolumeby": { + "opcode": "sound_changevolumeby", "next": None, "parent": None, + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 645, "y": 353 + }, + "sound_setvolumeto": { + "opcode": "sound_setvolumeto", "next": None, "parent": None, + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1108, "y": 5 + }, + "sound_volume": { + "opcode": "sound_volume", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1136, "y": 123 + }, +} + +# #Example input with opcodes from various categories +# input_opcodes = [ +# {"opcode": "sound_play", "count": 2}, # New: Sound block with menu +# {"opcode": "sound_playuntildone", "count": 2}, # New: Sound block with menu +# ] + +# Example input with opcodes from various categories +# input_opcodes = [ +# {"opcode": "sound_play", "count": 2}, +# {"opcode": "sound_playuntildone", "count": 2}, +# {"opcode":"motion_goto","count":2}, +# {"opcode":"motion_glideto","count":2}, +# {"opcode":"looks_switchbackdropto","count":2}, +# {"opcode":"looks_switchcostumeto","count":2}, +# {"opcode":"control_create_clone_of","count":2}, +# {"opcode":"sensing_touchingobject","count":2}, +# {"opcode":"sensing_of","count":2}, +# {"opcode":"sensing_keypressed","count":2}, +# {"opcode":"motion_pointtowards","count":2}, +# ] + +# generated_output = generate_blocks_from_opcodes(input_opcodes, all_block_definitions) +# print(json.dumps(generated_output, indent=2)) + +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_glidesecstoxy","count":1}, + {"opcode":"motion_xposition","count":1}, + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":1}, + {"opcode":"control_stop","count":1}, + {"opcode":"operator_lt","count":1}, + {"opcode":"sensing_istouching","count":1}, + {"opcode":"sensing_touchingobjectmenu","count":1}, # This will now be generated as a child of sensing_touchingobject + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":2}, + {"opcode":"data_showvariable","count":2}, +] + +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + +# Pseudo-code to interpret +pseudo_code_input = """ +when green flag clicked + go to x: (240) y: (-135) + set [score v] to +1 + set [speed v] to +1 + show variable [score v] + show variable [speed v] + forever + glide (2) seconds to x: (-240) y: (-135) + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end +end +""" + +# Interpret the pseudo-code and update the blocks, passing opcode_occurrences +final_generated_blocks = interpret_pseudo_code_and_update_blocks(generated_output_json, pseudo_code_input, all_block_definitions, initial_opcode_occurrences) + +print(json.dumps(final_generated_blocks, indent=2)) diff --git a/utils/block_function_v1.py b/utils/block_function_v1.py new file mode 100644 index 0000000000000000000000000000000000000000..04e4159ee22a0da5cf03c9e653c3d59fc140cbd4 --- /dev/null +++ b/utils/block_function_v1.py @@ -0,0 +1,759 @@ +import json +import copy + +import json +import copy + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition. + It now correctly links parent and menu blocks using their generated unique keys, and handles various block categories. + It ensures that menu blocks are only generated as children of their respective parent blocks. + + Args: + opcode_counts (list): An array of objects, each with an 'opcode' and 'count' property. + Example: [{"opcode": "motion_gotoxy", "count": 1}] + all_block_definitions (dict): A comprehensive dictionary containing definitions for all block types. + + Returns: + dict: A JSON object where keys are generated block IDs and values are the block definitions. + """ + generated_blocks = {} + opcode_occurrences = {} # To keep track of how many times each opcode (main or menu) has been used for unique keys + + # Define explicit parent-menu relationships for linking purposes + # This maps main_opcode -> list of (input_field_name, menu_opcode) + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + # --- Step 1: Process explicitly requested opcodes and generate their instances and associated menus --- + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if not opcode: + print("Warning: Skipping item with missing 'opcode'.") + continue + + if opcode not in all_block_definitions: + print(f"Warning: Opcode '{opcode}' not found in all_block_definitions. Skipping.") + continue + + for _ in range(count): + # Increment occurrence count for the current main opcode + opcode_occurrences[opcode] = opcode_occurrences.get(opcode, 0) + 1 + main_block_instance_num = opcode_occurrences[opcode] + + main_block_unique_key = f"{opcode}_{main_block_instance_num}" + + # Create a deep copy of the main block definition + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + + # Set properties for a top-level main block + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False # Main blocks are typically not shadows + + generated_blocks[main_block_unique_key] = main_block_data + + # If this main block has associated menus, generate and link them now + if opcode in explicit_menu_links: + for input_field_name, menu_opcode_type in explicit_menu_links[opcode]: + if menu_opcode_type in all_block_definitions: + # Increment the occurrence for the menu block type + opcode_occurrences[menu_opcode_type] = opcode_occurrences.get(menu_opcode_type, 0) + 1 + menu_block_instance_num = opcode_occurrences[menu_opcode_type] + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode_type]) + + # Generate a unique key for this specific menu instance + menu_unique_key = f"{menu_opcode_type}_{menu_block_instance_num}" + + # Set properties for a shadow menu block + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_block_unique_key # Link menu to its parent instance + + # Update the main block's input to point to this unique menu instance + if input_field_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_field_name], list) and \ + len(main_block_data["inputs"][input_field_name]) > 1 and \ + main_block_data["inputs"][input_field_name][0] == 1: + + main_block_data["inputs"][input_field_name][1] = menu_unique_key + + generated_blocks[menu_unique_key] = menu_block_data + + return generated_blocks + + +# --- Consolidated Block Definitions from all provided JSONs --- +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "opcode": "motion_movesteps", "next": None, "parent": None, + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 464, "y": -416 + }, + "motion_turnright": { + "opcode": "motion_turnright", "next": None, "parent": None, + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 467, "y": -316 + }, + "motion_turnleft": { + "opcode": "motion_turnleft", "next": None, "parent": None, + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 464, "y": -210 + }, + "motion_goto": { + "opcode": "motion_goto", "next": None, "parent": None, + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 465, "y": -95 + }, + "motion_goto_menu": { + "opcode": "motion_goto_menu", "next": None, "parent": "motion_goto", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "opcode": "motion_gotoxy", "next": None, "parent": None, + "inputs": {"X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 468, "y": 12 + }, + "motion_glideto": { + "opcode": "motion_glideto", "next": None, "parent": None, + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 470, "y": 129 + }, + "motion_glideto_menu": { + "opcode": "motion_glideto_menu", "next": None, "parent": "motion_glideto", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "opcode": "motion_glidesecstoxy", "next": None, "parent": None, + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 476, "y": 239 + }, + "motion_pointindirection": { + "opcode": "motion_pointindirection", "next": None, "parent": None, + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 493, "y": 361 + }, + "motion_pointtowards": { + "opcode": "motion_pointtowards", "next": None, "parent": None, + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 492, "y": 463 + }, + "motion_pointtowards_menu": { + "opcode": "motion_pointtowards_menu", "next": None, "parent": "motion_pointtowards", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "opcode": "motion_changexby", "next": None, "parent": None, + "inputs": {"DX": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 851, "y": -409 + }, + "motion_setx": { + "opcode": "motion_setx", "next": None, "parent": None, + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 864, "y": -194 + }, + "motion_changeyby": { + "opcode": "motion_changeyby", "next": None, "parent": None, + "inputs": {"DY": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 861, "y": -61 + }, + "motion_sety": { + "opcode": "motion_sety", "next": None, "parent": None, + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 864, "y": 66 + }, + "motion_ifonedgebounce": { + "opcode": "motion_ifonedgebounce", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1131, "y": -397 + }, + "motion_setrotationstyle": { + "opcode": "motion_setrotationstyle", "next": None, "parent": None, + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True, + "x": 1128, "y": -287 + }, + "motion_xposition": { + "opcode": "motion_xposition", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1193, "y": -136 + }, + "motion_yposition": { + "opcode": "motion_yposition", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1181, "y": -64 + }, + "motion_direction": { + "opcode": "motion_direction", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1188, "y": 21 + }, + + # control_block.json + "control_wait": { + "opcode": "control_wait", "next": None, "parent": None, + "inputs": {"DURATION": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 337, "y": 129 + }, + "control_repeat": { + "opcode": "control_repeat", "next": None, "parent": None, + "inputs": {"TIMES": [1, [6, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 348, "y": 265 + }, + "control_forever": { + "opcode": "control_forever", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 334, "y": 439 + }, + "control_if": { + "opcode": "control_if", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 331, "y": 597 + }, + "control_if_else": { + "opcode": "control_if_else", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 335, "y": 779 + }, + "control_wait_until": { + "opcode": "control_wait_until", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 676, "y": 285 + }, + "control_repeat_until": { + "opcode": "control_repeat_until", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 692, "y": 381 + }, + "control_stop": { + "opcode": "control_stop", "next": None, "parent": None, + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, + "x": 708, "y": 545, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "opcode": "control_start_as_clone", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 665, "y": 672 + }, + "control_create_clone_of": { + "opcode": "control_create_clone_of", "next": None, "parent": None, + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 648, "y": 797 + }, + "control_create_clone_of_menu": { + "opcode": "control_create_clone_of_menu", "next": None, "parent": "control_create_clone_of", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "opcode": "control_delete_this_clone", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 642, "y": 914 + }, + + # data_block.json + "data_setvariableto": { + "opcode": "data_setvariableto", "next": None, "parent": None, + "inputs": {"VALUE": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True, + "x": 348, "y": 241 + }, + "data_changevariableby": { + "opcode": "data_changevariableby", "next": None, "parent": None, + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True, + "x": 313, "y": 363 + }, + "data_showvariable": { + "opcode": "data_showvariable", "next": None, "parent": None, + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True, + "x": 415, "y": 473 + }, + "data_hidevariable": { + "opcode": "data_hidevariable", "next": None, "parent": None, + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True, + "x": 319, "y": 587 + }, + "data_addtolist": { + "opcode": "data_addtolist", "next": None, "parent": None, + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 385, "y": 109 + }, + "data_deleteoflist": { + "opcode": "data_deleteoflist", "next": None, "parent": None, + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 384, "y": 244 + }, + "data_deletealloflist": { + "opcode": "data_deletealloflist", "next": None, "parent": None, + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 387, "y": 374 + }, + "data_insertatlist": { + "opcode": "data_insertatlist", "next": None, "parent": None, + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 366, "y": 527 + }, + "data_replaceitemoflist": { + "opcode": "data_replaceitemoflist", "next": None, "parent": None, + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 365, "y": 657 + }, + "data_itemoflist": { + "opcode": "data_itemoflist", "next": None, "parent": None, + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 862, "y": 117 + }, + "data_itemnumoflist": { + "opcode": "data_itemnumoflist", "next": None, "parent": None, + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 883, "y": 238 + }, + "data_lengthoflist": { + "opcode": "data_lengthoflist", "next": None, "parent": None, + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 876, "y": 342 + }, + "data_listcontainsitem": { + "opcode": "data_listcontainsitem", "next": None, "parent": None, + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 871, "y": 463 + }, + "data_showlist": { + "opcode": "data_showlist", "next": None, "parent": None, + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 931, "y": 563 + }, + "data_hidelist": { + "opcode": "data_hidelist", "next": None, "parent": None, + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True, + "x": 962, "y": 716 + }, + + # event_block.json + "event_whenflagclicked": { + "opcode": "event_whenflagclicked", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 166, "y": -422 + }, + "event_whenkeypressed": { + "opcode": "event_whenkeypressed", "next": None, "parent": None, + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": False, "topLevel": True, + "x": 151, "y": -329 + }, + "event_whenthisspriteclicked": { + "opcode": "event_whenthisspriteclicked", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 156, "y": -223 + }, + "event_whenbackdropswitchesto": { + "opcode": "event_whenbackdropswitchesto", "next": None, "parent": None, + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True, + "x": 148, "y": -101 + }, + "event_whengreaterthan": { + "opcode": "event_whengreaterthan", "next": None, "parent": None, + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True, + "x": 150, "y": 10 + }, + "event_whenbroadcastreceived": { + "opcode": "event_whenbroadcastreceived", "next": None, "parent": None, + "inputs": {}, "fields": {"BROADCAST_OPTION": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True, + "x": 141, "y": 118 + }, + "event_broadcast": { + "opcode": "event_broadcast", "next": None, "parent": None, + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 151, "y": 229 + }, + "event_broadcastandwait": { + "opcode": "event_broadcastandwait", "next": None, "parent": None, + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 157, "y": 340 + }, + + # look_block.json + "looks_sayforsecs": { + "opcode": "looks_sayforsecs", "next": None, "parent": None, + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 408, "y": 91 + }, + "looks_say": { + "opcode": "looks_say", "next": None, "parent": None, + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 413, "y": 213 + }, + "looks_thinkforsecs": { + "opcode": "looks_thinkforsecs", "next": None, "parent": None, + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 413, "y": 317 + }, + "looks_think": { + "opcode": "looks_think", "next": None, "parent": None, + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 412, "y": 432 + }, + "looks_switchcostumeto": { + "opcode": "looks_switchcostumeto", "next": None, "parent": None, + "inputs": {"COSTUME": [1, "8;bti4wv(iH9nkOacCJ|"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 411, "y": 555 + }, + "looks_costume": { + "opcode": "looks_costume", "next": None, "parent": "Q#a,6LPWHqo9-0Nu*[SV", + "inputs": {}, "fields": {"COSTUME": ["costume2", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "opcode": "looks_nextcostume", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 419, "y": 687 + }, + "looks_switchbackdropto": { + "opcode": "looks_switchbackdropto", "next": None, "parent": None, + "inputs": {"BACKDROP": [1, "-?yeX}29V*wd6W:unW0i"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 901, "y": 91 + }, + "looks_backdrops": { + "opcode": "looks_backdrops", "next": None, "parent": "`Wm^p~l[(IWzc1|wNv*.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_changesizeby": { + "opcode": "looks_changesizeby", "next": None, "parent": None, + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 895, "y": 192 + }, + "looks_setsizeto": { + "opcode": "looks_setsizeto", "next": None, "parent": None, + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 896, "y": 303 + }, + "looks_changeeffectby": { + "opcode": "looks_changeeffectby", "next": None, "parent": None, + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True, + "x": 892, "y": 416 + }, + "looks_seteffectto": { + "opcode": "looks_seteffectto", "next": None, "parent": None, + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True, + "x": 902, "y": 527 + }, + "looks_cleargraphiceffects": { + "opcode": "looks_cleargraphiceffects", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 902, "y": 638 + }, + "looks_show": { + "opcode": "looks_show", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 908, "y": 758 + }, + "looks_hide": { + "opcode": "looks_hide", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 455, "y": 861 + }, + "looks_gotofrontback": { + "opcode": "looks_gotofrontback", "next": None, "parent": None, + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True, + "x": 853, "y": 878 + }, + "looks_goforwardbackwardlayers": { + "opcode": "looks_goforwardbackwardlayers", "next": None, "parent": None, + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True, + "x": 851, "y": 999 + }, + "looks_costumenumbername": { + "opcode": "looks_costumenumbername", "next": None, "parent": None, + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True, + "x": 458, "y": 1007 + }, + "looks_backdropnumbername": { + "opcode": "looks_backdropnumbername", "next": None, "parent": None, + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True, + "x": 1242, "y": 753 + }, + "looks_size": { + "opcode": "looks_size", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1249, "y": 876 + }, + + # operator_block.json + "operator_add": { + "opcode": "operator_add", "next": None, "parent": None, + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 128, "y": 153 + }, + "operator_subtract": { + "opcode": "operator_subtract", "next": None, "parent": None, + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 134, "y": 214 + }, + "operator_multiply": { + "opcode": "operator_multiply", "next": None, "parent": None, + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 134, "y": 278 + }, + "operator_divide": { + "opcode": "operator_divide", "next": None, "parent": None, + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 138, "y": 359 + }, + "operator_random": { + "opcode": "operator_random", "next": None, "parent": None, + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 311, "y": 157 + }, + "operator_gt": { + "opcode": "operator_gt", "next": None, "parent": None, + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 348, "y": 217 + }, + "operator_lt": { + "opcode": "operator_lt", "next": None, "parent": None, + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 345, "y": 286 + }, + "operator_equals": { + "opcode": "operator_equals", "next": None, "parent": None, + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 345, "y": 372 + }, + "operator_and": { + "opcode": "operator_and", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 701, "y": 158 + }, + "operator_or": { + "opcode": "operator_or", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 705, "y": 222 + }, + "operator_not": { + "opcode": "operator_not", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 734, "y": 283 + }, + "operator_join": { + "opcode": "operator_join", "next": None, "parent": None, + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 663, "y": 378 + }, + "operator_letter_of": { + "opcode": "operator_letter_of", "next": None, "parent": None, + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 664, "y": 445 + }, + "operator_length": { + "opcode": "operator_length", "next": None, "parent": None, + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 664, "y": 521 + }, + "operator_contains": { + "opcode": "operator_contains", "next": None, "parent": None, + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 634, "y": 599 + }, + "operator_mod": { + "opcode": "operator_mod", "next": None, "parent": None, + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 295, "y": 594 + }, + "operator_round": { + "opcode": "operator_round", "next": None, "parent": None, + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 307, "y": 674 + }, + "operator_mathop": { + "opcode": "operator_mathop", "next": None, "parent": None, + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True, + "x": 280, "y": 754 + }, + + # sensing_block.json + "sensing_touchingobject": { + "opcode": "sensing_touchingobject", "next": None, "parent": None, + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 359, "y": 116 + }, + "sensing_touchingobjectmenu": { + "opcode": "sensing_touchingobjectmenu", "next": None, "parent": "sensing_touchingobject", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "opcode": "sensing_touchingcolor", "next": None, "parent": None, + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 360, "y": 188 + }, + "sensing_coloristouchingcolor": { + "opcode": "sensing_coloristouchingcolor", "next": None, "parent": None, + "inputs": {"COLOR": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 348, "y": 277 + }, + "sensing_askandwait": { + "opcode": "sensing_askandwait", "next": None, "parent": None, + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 338, "y": 354 + }, + "sensing_answer": { + "opcode": "sensing_answer", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 782, "y": 111 + }, + "sensing_keypressed": { + "opcode": "sensing_keypressed", "next": None, "parent": None, + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 762, "y": 207 + }, + "sensing_keyoptions": { + "opcode": "sensing_keyoptions", "next": None, "parent": "sensing_keypressed", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "opcode": "sensing_mousedown", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 822, "y": 422 + }, + "sensing_mousex": { + "opcode": "sensing_mousex", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 302, "y": 528 + }, + "sensing_mousey": { + "opcode": "sensing_mousey", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 668, "y": 547 + }, + "sensing_setdragmode": { + "opcode": "sensing_setdragmode", "next": None, "parent": None, + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True, + "x": 950, "y": 574 + }, + "sensing_loudness": { + "opcode": "sensing_loudness", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 658, "y": 703 + }, + "sensing_timer": { + "opcode": "sensing_timer", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 459, "y": 671 + }, + "sensing_resettimer": { + "opcode": "sensing_resettimer", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 462, "y": 781 + }, + "sensing_of": { + "opcode": "sensing_of", "next": None, "parent": None, + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True, + "x": 997, "y": 754 + }, + "sensing_of_object_menu": { + "opcode": "sensing_of_object_menu", "next": None, "parent": "sensing_of", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "opcode": "sensing_current", "next": None, "parent": None, + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True, + "x": 627, "y": 884 + }, + "sensing_dayssince2000": { + "opcode": "sensing_dayssince2000", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 959, "y": 903 + }, + "sensing_username": { + "opcode": "sensing_username", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 833, "y": 757 + }, + + # sound_block.json + "sound_playuntildone": { + "opcode": "sound_playuntildone", "next": None, "parent": None, + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 253, "y": 17 + }, + "sound_sounds_menu": { + "opcode": "sound_sounds_menu", "next": None, "parent": "sound_playuntildone and sound_play", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "opcode": "sound_play", "next": None, "parent": None, + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 245, "y": 122 + }, + "sound_stopallsounds": { + "opcode": "sound_stopallsounds", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 253, "y": 245 + }, + "sound_changeeffectby": { + "opcode": "sound_changeeffectby", "next": None, "parent": None, + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True, + "x": 653, "y": 14 + }, + "sound_seteffectto": { + "opcode": "sound_seteffectto", "next": None, "parent": None, + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True, + "x": 653, "y": 139 + }, + "sound_cleareffects": { + "opcode": "sound_cleareffects", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 651, "y": 242 + }, + "sound_changevolumeby": { + "opcode": "sound_changevolumeby", "next": None, "parent": None, + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 645, "y": 353 + }, + "sound_setvolumeto": { + "opcode": "sound_setvolumeto", "next": None, "parent": None, + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1108, "y": 5 + }, + "sound_volume": { + "opcode": "sound_volume", "next": None, "parent": None, + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, + "x": 1136, "y": 123 + }, +} + +# #Example input with opcodes from various categories +# input_opcodes = [ +# {"opcode": "sound_play", "count": 2}, # New: Sound block with menu +# {"opcode": "sound_playuntildone", "count": 2}, # New: Sound block with menu +# ] + +# Example input with opcodes from various categories +input_opcodes = [ + {"opcode": "sound_play", "count": 2}, + {"opcode": "sound_playuntildone", "count": 2}, + {"opcode":"motion_goto","count":2}, + {"opcode":"motion_glideto","count":2}, + {"opcode":"looks_switchbackdropto","count":2}, + {"opcode":"looks_switchcostumeto","count":2}, + {"opcode":"control_create_clone_of","count":2}, + {"opcode":"sensing_touchingobject","count":2}, + {"opcode":"sensing_of","count":2}, + {"opcode":"sensing_keypressed","count":2}, + {"opcode":"motion_pointtowards","count":2}, +] + +generated_output = generate_blocks_from_opcodes(input_opcodes, all_block_definitions) +print(json.dumps(generated_output, indent=2)) \ No newline at end of file diff --git a/utils/block_naming.py b/utils/block_naming.py new file mode 100644 index 0000000000000000000000000000000000000000..d6aef68e9ba8de41b292d0de0c9ea7d1c71c3d4f --- /dev/null +++ b/utils/block_naming.py @@ -0,0 +1,62 @@ +import secrets +import string +from typing import Dict, Any, TypedDict + +def generate_secure_token(length=20): + charset = string.ascii_letters + string.digits + "!@#$%^&*()[]{}=+-_~" + return ''.join(secrets.choice(charset) for _ in range(length)) + +def rename_blocks(block_json: dict, opcode_count: dict) -> tuple[dict, dict]: + """ + Replace each block key in block_json and each identifier in opcode_count + with a newly generated secure token. + + Args: + block_json: Mapping of block_key -> block_data. + opcode_count: Mapping of opcode -> list of block_keys. + + Returns: + A tuple of (new_block_json, new_opcode_count) with updated keys. + """ + # Step 1: Generate a secure token mapping for every existing block key + token_map = {} + for old_key in block_json.keys(): + # Ensure uniqueness in the unlikely event of a collision + while True: + new_key = generate_secure_token() + if new_key not in token_map.values(): + break + token_map[old_key] = new_key + + # Step 2: Rebuild block_json with new keys + new_block_json = {} + for old_key, block in block_json.items(): + new_key = token_map[old_key] + new_block_json[new_key] = block.copy() + + # Update parent and next references + if 'parent' in block and block['parent'] in token_map: + new_block_json[new_key]['parent'] = token_map[block['parent']] + if 'next' in block and block['next'] in token_map: + new_block_json[new_key]['next'] = token_map[block['next']] + + # Update inputs if they reference blocks + for inp_key, inp_val in block.get('inputs', {}).items(): + if isinstance(inp_val, list) and len(inp_val) == 2: + idx, ref = inp_val + if idx in (2, 3) and isinstance(ref, str) and ref in token_map: + new_block_json[new_key]['inputs'][inp_key] = [idx, token_map[ref]] + + # Step 3: Update opcode count map + new_opcode_count = {} + for opcode, key_list in opcode_count.items(): + new_opcode_count[opcode] = [token_map.get(k, k) for k in key_list] + + return new_block_json, new_opcode_count + +# Example usage: +if __name__ == "__main__": + 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', '']]}, '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 + 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 + renamed_blocks, renamed_counts = rename_blocks(blocks, opcode_keys) + print(renamed_blocks) diff --git a/utils/block_relation_builder.py b/utils/block_relation_builder.py new file mode 100644 index 0000000000000000000000000000000000000000..7ad99c9a9590835c09f1687ef518d312c3dd1670 --- /dev/null +++ b/utils/block_relation_builder.py @@ -0,0 +1,2560 @@ +import json +import copy +import re +from collections import defaultdict +import secrets +import string +from typing import Dict, Any, TypedDict + + +################################################################################################################################################################# +#--------------------------------------------------[Creating a skelton json with some initial default value inside it]------------------------------------------- +################################################################################################################################################################# + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False + + # Ensure inputs and fields are dictionaries, even if they were None or list in definition + if "inputs" not in main_block_data or not isinstance(main_block_data["inputs"], dict): + main_block_data["inputs"] = {} + if "fields" not in main_block_data or not isinstance(main_block_data["fields"], dict): + main_block_data["fields"] = {} + + generated_blocks[main_key] = main_block_data + + # Handle menus + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_key + + # Ensure inputs and fields are dictionaries for menu blocks too + if "inputs" not in menu_block_data or not isinstance(menu_block_data["inputs"], dict): + menu_block_data["inputs"] = {} + if "fields" not in menu_block_data or not isinstance(menu_block_data["fields"], dict): + menu_block_data["fields"] = {} + + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 1 and \ + main_block_data["inputs"][input_name][0] == 1: + + main_block_data["inputs"][input_name][1] = menu_key + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +################################################################################################################################################################# +#--------------------------------------------------[Block Defination which hold skelton json with default value inside it]--------------------------------------- +################################################################################################################################################################# + +# Consolidated block definitions from all JSON files +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_distanceto": { # Added sensing_distanceto + "block_name": "(distance to ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto", + "functionality": "Reports the distance from the sprite to the mouse-pointer or another specified sprite.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": False, "topLevel": True + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True + } +} + +################################################################################################################################################################# +#--------------------------------------------------[Helper Functions]--------------------------------------------------------------------------------------------- +################################################################################################################################################################# + +def unparen(s): + s = s.strip() + # keep peeling off *all* matching outer parens + while True: + m = re.fullmatch(r"\((.*)\)", s) + if not m: + break + s = m.group(1).strip() + return s + +def _register_block(opcode, parent_key, is_shadow, pick_key_func, all_blocks_dict, inputs=None, fields=None): + """ + Helper to create and register a block in the all_blocks_dict. + It uses pick_key_func to get a unique ID. + """ + key = pick_key_func(opcode) + block_data = copy.deepcopy(all_block_definitions[opcode]) + block_data["id"] = key + block_data["parent"] = parent_key # Set parent directly here if known + block_data["next"] = None + block_data["topLevel"] = not is_shadow # Shadow blocks are not top-level + block_data["shadow"] = is_shadow + + # Ensure inputs and fields are dictionaries + if "inputs" not in block_data or not isinstance(block_data["inputs"], dict): + block_data["inputs"] = {} + if "fields" not in block_data or not isinstance(block_data["fields"], dict): + block_data["fields"] = {} + + if inputs: + block_data["inputs"].update(inputs) + if fields: + block_data["fields"].update(fields) + + all_blocks_dict[key] = block_data + return key + +def _auto_balance(text): + # if there are more "(" than ")", append the missing ")" + diff = text.count("(") - text.count(")") + if diff > 0: + text = text + ")"*diff + # same for square brackets + diff = text.count("[") - text.count("]") + if diff > 0: + text = text + "]"*diff + return text + +def strip_outer_angle_brackets(text): + """ + Strip exactly one balanced pair of outer <...> brackets, only if they wrap the whole string. + """ + text = text.strip() + if text.startswith("<") and text.endswith(">"): + depth = 0 + for i, char in enumerate(text): + if char == '<': + depth += 1 + elif char == '>': + depth -= 1 + if depth == 0 and i == len(text) - 1: + return text[1:-1].strip() + # If we exit the loop and depth is 0, it means the outer brackets were balanced and wrapped the whole string + if depth == 0: + return text[1:-1].strip() + return text + +def extract_condition_balanced(stmt): + # 1. Remove "if" and "then" + stmt = stmt.strip() + if stmt.lower().startswith("if "): + stmt = stmt[3:].strip() + if stmt.lower().startswith("repeat until"): + stmt = stmt[12:].strip() + if stmt.lower().startswith("wait until "): + stmt = stmt[11:].strip() + if stmt.lower().endswith(" then"): + stmt = stmt[:-5].strip() + + # Helper to detect and strip single outer balanced angle brackets + def unwrap_balanced(s): + if s.startswith("<") and s.endswith(">"): + depth = 0 + for i in range(len(s)): + if s[i] == "<": + depth += 1 + elif s[i] == ">": + depth -= 1 + if depth == 0 and i < len(s) - 1: + return s # Early balance → not a single outer wrapper + if depth == 0: + return s[1:-1].strip() + return s + + # Recursively simplify things like > to not + def simplify(s): + s = unwrap_balanced(s) + s = s.strip() + + # Match > pattern + m = re.fullmatch(r"not\s*<(.+)>", s, re.IGNORECASE) + if m: + inner = m.group(1).strip() + inner = simplify(inner) + return f"not <{inner}>" + + # Match comparison operators like <(x position) < (100)> + # This part might be redundant if the main parser handles it, but good for internal consistency + m_comp = re.fullmatch(r"<\s*\(([^<>]+?)\)\s*([<>=])\s*\(([^<>]+?)\)\s*>", stmt) + if m_comp: + return f"({m_comp.group(1).strip()}) {m_comp.group(2)} ({m_comp.group(3).strip()})" + + return s + + return simplify(stmt) + +################################################################################################################################################################# +#--------------------------------------------------[Regular Expression which handle the reporter variable and values]-------------------------------------------- +################################################################################################################################################################# +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, parent_key, pick_key_func, all_generated_blocks): + text = _auto_balance(text.strip()) + text = unparen(text.strip()) + # Check for numeric literal (including parenthesized numbers like "(0)" or "(10)") + m_num = re.fullmatch(r"\(?\s*(-?\d+(\.\d+)?)\s*\)?", text) + if m_num: + val_str = m_num.group(1) + return {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # Variable reporter: [score v], [health v], etc. + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, + fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + + # Now catch other bracketed values as literal strings + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + # Check for simple reporters, potentially with outer parentheses + m_simple_reporter = re.fullmatch(r"\((.+?)\)", text) + if m_simple_reporter: + inner_text = m_simple_reporter.group(1).strip() + if inner_text in simple_reporters: + block_id = _register_block(simple_reporters[inner_text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + # Also check for simple reporters without parentheses (e.g., if passed directly) + if text in simple_reporters: + block_id = _register_block(simple_reporters[text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + + # Variable reporter: [score v] or (score) or just "score" + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + # Ensure it's not a simple reporter already handled, or a number + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [potential_var_name, None]}) + return {"kind": "block", "block": block_id} + # Handle plain variable names like "score", "number 1", "total score" + if re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_ ]*", text): # Allow spaces for "number 1" etc. + # Exclude known simple reporters that don't have 'v' or parentheses + if text not in simple_reporters: + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [text, None]}) + return {"kind": "block", "block": block_id} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + return {"kind": "block", "block": block_id} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + max_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + inputs = {"FROM": min_val_obj, "TO": max_val_obj} + block_id = _register_block("operator_random", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if min_val_obj.get("kind") == "block": all_generated_blocks[min_val_obj["block"]]["parent"] = block_id + if max_val_obj.get("kind") == "block": all_generated_blocks[max_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (join ()()) (operator_join) - handle both [] and () for inputs + + #m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s+(\[.+?\]|\(.+?\))", text) # Try (val) (val) + m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s*(\[.+?\]|\(.+?\))", text) + if m: + part1_txt = m.group(1).strip() + part2_txt = m.group(2).strip() + str1_obj = parse_reporter_or_value(part1_txt, None, pick_key_func, all_generated_blocks) + str2_obj = parse_reporter_or_value(part2_txt, None, pick_key_func, all_generated_blocks) + inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + block_id = _register_block("operator_join", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs) + # set parents if nested blocks + for obj in (str1_obj, str2_obj): + if obj.get("kind") == "block": + all_generated_blocks[obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # letter () of () (operator_letterof) - handle both [] and () for inputs + m = re.search(r"letter \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + string_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"LETTER": index_obj, "STRING": string_val_obj} + block_id = _register_block("operator_letterof", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + if string_val_obj.get("kind") == "block": all_generated_blocks[string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (length of ()) (operator_length) - handle both [] and () for inputs + #m = re.search(r"length of \((.+?)\)", text) + m = re.search(r"length of\s*(?:\((.+?)\)|\[(.+?)\])", text) + if not m: + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + arg_txt = (m.group(1) or m.group(2)).strip() + list_or_string_val_obj = parse_reporter_or_value(arg_txt, None, pick_key_func, all_generated_blocks) + #list_or_string_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"STRING": list_or_string_val_obj} + block_id = _register_block("operator_length", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if list_or_string_val_obj.get("kind") == "block": all_generated_blocks[list_or_string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (() mod ()) (operator_mod) + # m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + m = re.search(r"\[([^\]]+)\s*v\]\s*mod\s*\(?\s*(.+?)\s*\)?", text) + if m: + num1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + num2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM1": num1_obj, "NUM2": num2_obj} + block_id = _register_block("operator_mod", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num1_obj.get("kind") == "block": all_generated_blocks[num1_obj["block"]]["parent"] = block_id + if num2_obj.get("kind") == "block": all_generated_blocks[num2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": num_obj} + block_id = _register_block("operator_round", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num_obj.get("kind") == "block": all_generated_blocks[num_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (() of ()) (operator_mathop) - handle variable for function type + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) # e.g. [sqrt v] of ((x pos) * (x pos)) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + # Also handle direct string for function type (e.g., "abs of (x)") + m = re.search(r"([a-zA-Z]+)\s*of\s*\((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + # This regex is designed to handle nested parentheses correctly. + # It looks for an opening parenthesis, then non-parenthesis characters or balanced parentheses, + # followed by an operator, and then the second operand. + # This is a simplified approach; a full-fledged parser would use a stack. + # arithmetic_match = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + arithmetic_match = re.search(r"\(?\s*(.+?)\s*\)?\s*([\+\-\*/])\s*\(?\s*(.+?)\s*\)?", text) + if not arithmetic_match: + # Try to match without outer parentheses for the operands, but still with an operator + arithmetic_match = re.search(r"(.+?)\s*([+\-*/])\s*(.+)", text) + + if arithmetic_match: + op1_str = arithmetic_match.group(1).strip() + operator_symbol = arithmetic_match.group(2).strip() + op2_str = arithmetic_match.group(3).strip() + + op1_obj = parse_reporter_or_value(op1_str, None, pick_key_func, all_generated_blocks) + op2_obj = parse_reporter_or_value(op2_str, None, pick_key_func, all_generated_blocks) + + opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + inputs = {"NUM1": op1_obj, "NUM2": op2_obj} + block_id = _register_block(opcode_map[operator_symbol], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if op1_obj.get("kind") == "block": all_generated_blocks[op1_obj["block"]]["parent"] = block_id + if op2_obj.get("kind") == "block": all_generated_blocks[op2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (costume ()) (looks_costumenumbername) - handle with or without 'v' + m = re.search(r"costume \((.+?)\)", text) + if not m: + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_costumenumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (backdrop ()) (looks_backdropnumbername) - handle with or without 'v' + m = re.search(r"backdrop \((.+?)\)", text) + if not m: + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_backdropnumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (distance to ()) (sensing_distanceto) - handle with or without 'v' + m = re.search(r"distance to \((.+?)\)", text) + if not m: + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + + # This block has a dropdown FIELD, not an input that links to a shadow block + fields = {"TARGET": [target_val, None]} + block_id = _register_block("sensing_distanceto", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (current ()) (sensing_current) - handle with or without 'v' + m = re.search(r"current \((.+?)\)", text) + if not m: + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + fields = {"CURRENTMENU": [unit.upper(), None]} + block_id = _register_block("sensing_current", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (() of ()) (sensing_of) - Corrected logic + #m = re.search(r"\((.+?)\)\s*of\s*(?:\((.+?)\)|\[(.+?)\s*v\])", text) + m = re.search(r"\((.+?)\)\s*of\s*(?:\((.+?)\)|\[(.+?)\s*v\])", text) + if m: + prop_str = m.group(1).strip() + obj = (m.group(2) or m.group(3)).strip() + + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + property_value = prop_map.get(prop_str, prop_str) + + if obj.lower() == "stage": obj_val = "_stage_" + elif obj.lower() == "myself": obj_val = "_myself_" + else: obj_val = obj + + # Create the sensing_of_object_menu shadow block + object_menu_id = _register_block("sensing_of_object_menu", parent_key, True, pick_key_func, all_generated_blocks, fields={"OBJECT": [obj_val, None]}) + + # Create the main sensing_of block + inputs = {"OBJECT": [1, object_menu_id]} + fields = {"PROPERTY": [property_value, None]} # PROPERTY is a field of the main block + + block_id = _register_block("sensing_of", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + all_generated_blocks[object_menu_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item (index) of [list v]) (data_itemoflist) + m = re.search(r"item \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + # Create data_list shadow block for the list name + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + + inputs = {"INDEX": index_obj, "LIST": [1, list_block_id]} + fields = {} # No fields in data_itemoflist itself for the list name + block_id = _register_block("data_itemoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item # of [item] in [list v]) (data_itemnumoflist) + m = re.search(r"item # of \((.+?)\) in \((.+?)\)", text) + if not m: + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + # Create data_list shadow block for the list name + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + + inputs = {"ITEM": item_obj, "LIST": [1, list_block_id]} + fields = {} # No fields in data_itemnumoflist itself for the list name + block_id = _register_block("data_itemnumoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if item_obj.get("kind") == "block": all_generated_blocks[item_obj["block"]]["parent"] = block_id + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + raise ValueError(f"Can't parse reporter or value: {text}") + +################################################################################################################################################################# +#--------------------------------------------------[Regular Expression which handle the conditon and other operation logics]------------------------------------- +################################################################################################################################################################# + +def parse_condition(stmt, parent_key, pick_key_func, all_generated_blocks): + """ + Parse Scratch-style boolean conditions, handling comparisons (<, =, >), + boolean operators (and, or, not), and other sensing conditions. + """ + s = stmt.strip() + s = extract_condition_balanced(s) + s = s.lower() + + print(f"the stmt was this {stmt} and parsed was this {s}") + # 1) Boolean NOT: `not <...>` + #m_not = re.fullmatch(r'not\s*<\s*(.+?)\s*>', s, re.IGNORECASE) + #m_not = re.fullmatch(r"\s*<\s*not\s+(.+?)\s*>\s*", s, re.IGNORECASE) + m_not = re.fullmatch(r"\s*(?:<\s*)?not\s+(.+?)(?:\s*>)?\s*",s, re.IGNORECASE) + if m_not: + inner = m_not.group(1).strip() + print(f"[2]the stmt was this {stmt} and parsed was this {s}") + inner_obj = parse_condition(inner, parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + bid = _register_block("operator_not", parent_key, False, pick_key_func, all_generated_blocks, # Pass parent_key + inputs={"OPERAND": inner_obj}) + if inner_obj.get("kind") == "block": + all_generated_blocks[inner_obj["block"]]["parent"] = bid + return {"kind": "block", "block": bid} + + # 2) Boolean AND / OR + #m_andor = re.fullmatch(r"<\s*(.+?)\s+(and|or)\s+(.+?)\s*>", s, re.IGNORECASE) + m_andor = re.fullmatch(r"\s*(.+?)\s+(and|or)\s+(.+?)\s*", s, re.IGNORECASE) + if m_andor: + cond1_obj = parse_condition(m_andor.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + cond2_obj = parse_condition(m_andor.group(3).strip(), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_block = 'operator_and' if m_andor.group(2).lower() == 'and' else 'operator_or' + print(f"The cond1: {cond1_obj} and the cond2: {cond2_obj} [for testing]") + inputs = {"OPERAND1": cond1_obj, "OPERAND2": cond2_obj} + block_id = _register_block(op_block, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + if cond1_obj.get("kind") == "block": all_generated_blocks[cond1_obj["block"]]["parent"] = block_id + if cond2_obj.get("kind") == "block": all_generated_blocks[cond2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # 1a) Comparisons with explicit angle wrappers: < (...) op (...) > + m = re.fullmatch( + r"\s*<\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*>\s*", + s, + re.VERBOSE + ) + if m: + left_txt, right_txt = m.group(1), m.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + # Set parents for nested inputs + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 1b) Simple comparisons without angle wrappers: A op B + m_simple = re.fullmatch(r"\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*", s) + if m_simple: + left_txt, right_txt = m_simple.group(1), m_simple.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m_simple.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 4) Contains: <[list v] contains [item]?> + #m = re.search(r"\[([^\]]+)\s*v\] contains \[(.+?)\]\?", s) + m = re.fullmatch(r"\s*\[(.+?)\]\s+contains\s+\[(.+?)\]\?\s*", s) + if m: + list_name = m.group(1).strip() + item_val = {"kind": "value", "value": m.group(2).strip()} # Item can be a value or a block + + # Create the data_list reporter block + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) # Pass parent_key + + inputs = {"LIST": {"kind": "block", "block": list_block_id}, "ITEM": item_val} + block_id = _register_block("data_listcontainsitem", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 5) Touching object: + m_touch = re.fullmatch(r""" + \s* # leading space + (?:<\s*)? # optional '<' + touching # literal + \s*\[\s* + (?P[^\]]+?) # capture the sprite name + \s*v\]\? # close the [sprite v]? + (?:\s*>)? # optional '>' + """, s, re.IGNORECASE | re.VERBOSE) + if m_touch: + sprite = m_touch.group('sprite').strip() + val = {'mouse-pointer':'_mouse_', 'edge':'_edge_'}.get(sprite, sprite) + + mid = _register_block( + "sensing_touchingobjectmenu", parent_key, True, pick_key_func, all_generated_blocks, # Pass parent_key + fields={"TOUCHINGOBJECTMENU":[val, None]} + ) + bid = _register_block( + "sensing_touchingobject", parent_key, False, pick_key_func, all_generated_blocks, # Pass parent_key + inputs={"TOUCHINGOBJECTMENU":[1, mid]} + ) + all_generated_blocks[mid]["parent"] = bid + return {"kind":"block","block":bid} + + # 6) Touching color: + m = re.search(r"touching color \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR": [1, [9, m.group(1)]]} # Color input is special, often a list [type, value] + block_id = _register_block("sensing_touchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + return {"kind": "block", "block": block_id} + + # 7) Color is touching color: + m = re.search(r"color \[(#[0-9A-Fa-f]{6})\] is touching \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR1": [1, [9, m.group(1)]], "COLOR2": [1, [9, m.group(2)]]} + block_id = _register_block("sensing_coloristouchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + return {"kind": "block", "block": block_id} + + # 8) Key pressed: + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", s) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", parent_key, True, pick_key_func, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) # Pass parent_key + + inputs = {"KEY_OPTION": [1, menu_block_id]} + block_id = _register_block("sensing_keypressed", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + all_generated_blocks[menu_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 9) Mouse down?: mouse down? + if s == "mouse down?": + block_id = _register_block("sensing_mousedown", parent_key, False, pick_key_func, all_generated_blocks) # Pass parent_key + return {"kind": "block", "block": block_id} + + val_obj = parse_reporter_or_value(unparen(stmt), parent_key, pick_key_func, all_generated_blocks) + if val_obj: + return val_obj + + raise ValueError(f"Can't parse condition: {stmt}") + +################################################################################################################################################################# +#--------------------------------------------------[Regular Expression which detect the block type used (single logic on single line)]--------------------------- +################################################################################################################################################################# + +def classify(line): + """ + Classifies a pseudo-code line into its corresponding Scratch opcode and block type. + Order of checks matters: more specific patterns should come before more general ones. + """ + l = line.lower().strip() + + # Ignore comments + if l.startswith("//"): return None, None + + # Hat Blocks (most specific first) + if re.match(r"when green flag click(ed)?", l): return "event_whenflagclicked", "hat" + if re.match(r"when (.+?) key press(ed)?", l): return "event_whenkeypressed", "hat" + if re.match(r"when this sprite click(ed)?", l): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if re.match(r"when i start as a clo(ne)?", l): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + if l.startswith("procedure "): return "procedures_definition", "hat" # For "procedure moveBall" + + # Motion Blocks + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + # IMPORTANT: More specific glide block before less specific one + # if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + # if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("glide ") and (" secs to x:" in l or " seconds to x:" in l): return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and (" secs to " in l or " seconds to " in l): return "motion_glideto", "stack" + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if re.match(r"set x to\s*\(.+\)", l): return "motion_setx", "stack" # Specific for set x + if l.startswith("change y by"): return "motion_changeyby", "stack" + if re.match(r"set y to\s*\(.+\)", l): return "motion_sety", "stack" # Specific for set y + #if re.match(r"if on edge, bounce( off edge)?", l): return "motion_ifonedgebounce", "stack" + if re.match(r"if on edge,\s*bounc(e)?(\s+off\s+edge)?", l.strip(), re.IGNORECASE): return "motion_ifonedgebounce", "stack" + if re.match(r"bounce off edg(e)?", l): return "motion_ifonedgebounce", "stack" # Alias + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + + + # Looks Blocks + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if re.match(r"next costum(e)?", l): return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + # Updated regex for change/set effect by/to + if re.match(r"change\s*(\[.+?v\]|\(.+?\))?\s*effect by", l): return "looks_changeeffectby", "stack" + if re.match(r"set\s*(\[.+?v\]|\(.+?\))?\s*effect to", l): return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + # Sound Blocks + if re.match(r"play sound (.+?) until do(ne)?", l): return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + # Event Blocks (broadcasts) + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + # Control Blocks + if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + # Updated regex for stop block to handle different options + if re.match(r"stop \[(all|this script|other scripts in sprite)\s*v\]", l): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + # Data Blocks + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + # Updated regex for delete of list + if re.match(r"delete \((.+?)\) of \[([^\]]+)\s*v\]", l): return "data_deleteoflist", "stack" + if l.startswith("delete all of [" ): return "data_deletealloflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + # Sensing Blocks + if re.match(r"ask (.+?) and wai(t)?", l): return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom Blocks (procedures_call) - specific rule for "call" + if l.startswith("call "): + return "procedures_call", "stack" + + # Custom Blocks (procedures_call) - LAST RESORT (generic match) + # This should be the very last check for stack-type blocks to avoid conflicts. + # It tries to match anything that looks like a function call with or without arguments. + custom_block_match = re.match(r"([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", l) + if custom_block_match: + # Before returning, ensure it's not a known simple reporter or variable name + # that might have been missed or is being used standalone. + # This is a heuristic; a full parser would be more robust. + potential_name = custom_block_match.group(1).strip() + if potential_name not in ["x position", "y position", "direction", "mouse x", "mouse y", "loudness", "timer", "days since 2000", "username", "answer", "size", "volume"] and \ + not re.fullmatch(r"\[[^\]]+\]", potential_name) and \ + not re.fullmatch(r"\[[^\]]+\]\s*v", potential_name): + return "procedures_call", "stack" + + + raise ValueError(f"Unknown statement: {line!r}") + +################################################################################################################################################################# +#--------------------------------------------------[create the updated skelton json with the Nesting and Staking logic]------------------------------------------ +################################################################################################################################################################# + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key -> block_data (pre-generated block definitions) + • opcode_keys: dict of opcode -> list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... list of block dictionaries ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + all_generated_blocks = {} # Initialize as empty, blocks will be added as they are parsed + + # Stack stores (indent, owner_block_id, last_block_in_current_linear_chain_id) + # owner_block_id: The ID of the C-block or Hat block that owns the current substack. + # last_block_in_current_linear_chain_id: The ID of the last block added to the *current linear sequence* within this substack. + stack = [(-1, None, None)] # Sentinel: (indent, owner_block_id, last_block_in_current_linear_chain_id) + + top_level_script_keys = [] + + lines = pseudo_code.splitlines() + i = 0 + while i < len(lines): + raw_line = lines[i] + stripped_line = raw_line.strip() + + # Skip empty lines and comments + if not stripped_line or stripped_line.startswith("//"): + i += 1 + continue + + current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2 + + # Handle 'else' and 'end' first, as they control scope + if stripped_line.lower() == "else": + # Pop the 'then' substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # The 'if-else' block (popped_owner_key) is the owner. + # Push a new scope for the 'else' substack, with the same owner. + stack.append((current_indent, popped_owner_key, None)) # New scope for 'else' part, no last block yet + i += 1 + continue + + if stripped_line.lower() == "end": + # Pop the current substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # If the substack was empty, ensure the input is [2, None]. + if popped_owner_key: + owner_block = all_generated_blocks[popped_owner_key] + if owner_block["block_shape"] == "C-Block" or owner_block["op_code"] == "procedures_definition": + if owner_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in owner_block["inputs"] and \ + owner_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in owner_block["inputs"]: + # If SUBSTACK is already set, this 'end' closes the SUBSTACK2 (else part) + if not owner_block["inputs"].get("SUBSTACK2"): # Only set if not already set by a block + owner_block["inputs"]["SUBSTACK2"] = [2, None] + elif not owner_block["inputs"].get("SUBSTACK"): # Only set if not already set by a block + owner_block["inputs"]["SUBSTACK"] = [2, None] + + i += 1 + continue + + # Adjust stack based on indentation for regular blocks + # Pop scopes whose indentation is greater than or equal to the current line's indentation + while len(stack) > 1 and stack[-1][0] >= current_indent: + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None # Terminate the chain + + # Get the current active scope from the stack + current_scope_indent, current_owner_block_id, last_block_in_current_chain = stack[-1] + + # Classify the statement and create the block + stmt_for_parse = stripped_line.rstrip("then").strip() + opcode, ntype = classify(stmt_for_parse) + + if opcode is None: # Should not happen if classify is robust + i += 1 + continue + + # Create the new block (and register it in all_generated_blocks) + # _register_block now only sets parent for shadow/input blocks; main block parent/next/topLevel set here. + key = _register_block(opcode, None, False, pick_key, all_generated_blocks) + info = all_generated_blocks[key] + + # Set parent, next, and topLevel for the main script blocks + if ntype == "hat": + info["parent"] = None + info["topLevel"] = True + top_level_script_keys.append(key) + # info["next"] = None # REMOVED: This was causing the hat block's 'next' to be None + # Push a new scope for the children of this hat block. + stack.append((current_indent, key, None)) # New scope: owner is this hat, no last block yet + else: # Stack block or C-block (that is part of a linear sequence) + if last_block_in_current_chain: + # This block's parent is the previous block in the chain + info["parent"] = last_block_in_current_chain + all_generated_blocks[last_block_in_current_chain]["next"] = key + else: + # This is the first block in a new linear chain (e.g., first block inside a forever loop) + # Its parent is the owner of the current scope (the C-block or Hat block) + info["parent"] = current_owner_block_id + + # If the owner is a C-block or procedure definition, link its SUBSTACK input + if current_owner_block_id and (all_generated_blocks[current_owner_block_id]["block_shape"] == "C-Block" or all_generated_blocks[current_owner_block_id]["op_code"] == "procedures_definition"): + owner_block = all_generated_blocks[current_owner_block_id] + if owner_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in owner_block["inputs"] and \ + owner_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in owner_block["inputs"]: + owner_block["inputs"]["SUBSTACK2"] = [2, key] + else: + owner_block["inputs"]["SUBSTACK"] = [2, key] + elif current_owner_block_id and all_generated_blocks[current_owner_block_id]["block_shape"] == "Hat Block": + # If the owner is a Hat block, this is its first child + all_generated_blocks[current_owner_block_id]["next"] = key + + info["topLevel"] = False + info["next"] = None # Default, will be overwritten if there's a next block + + # If it's a C-block or define block, it also starts a new inner scope + if ntype == "c_block" or opcode == "procedures_definition": + # Update the current scope's last_block_in_current_chain to this C-block + stack[-1] = (current_scope_indent, current_owner_block_id, key) + # Push a new scope for the C-block's substack + stack.append((current_indent, key, None)) # New scope: owner is this C-block, no last block yet + else: + # For regular stack blocks, just update the last_block_in_current_chain for the current scope + stack[-1] = (current_scope_indent, current_owner_block_id, key) + + # Parse inputs and fields (this part remains largely the same, but ensure parse_reporter_or_value/parse_condition + # are passed the *newly created block's ID* as the parent_key for nested inputs) + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if opcode == "motion_movesteps": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["STEPS"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_turnright" or opcode == "motion_turnleft": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DEGREES"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_gotoxy": + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_glidesecstoxy": + #m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt_for_parse, re.IGNORECASE) + m_secs = re.search(r"glide\s*\(\s*(-?\d+(?:\.\d+)?)\s*\)\s*(?:sec(?:s|onds)?)\b ", stmt_for_parse, re.IGNORECASE) + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_secs: info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_pointindirection": + m = re.search(r"direction\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DIRECTION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_changexby", "motion_changeyby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DX" if opcode == "motion_changexby" else "DY"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_setx", "motion_sety"]: + m = re.search(r"(?:set x to|set y to)\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["X" if opcode == "motion_setx" else "Y"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_changesizeby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_setsizeto": + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SIZE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_changeeffectby", "sound_changeeffectby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE" if opcode == "looks_changeeffectby" else "VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE" if opcode == "looks_seteffectto" else "VOLUME"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["NUM"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "control_wait": + m = re.search(r"wait\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DURATION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "control_repeat": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["TIMES"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "data_changevariableby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "data_deleteoflist": + m = re.search(r"delete\s*\((.+?)\)\s*of", stmt_for_parse, re.IGNORECASE) + if m: + val_str = m.group(1).strip() + if val_str.isdigit(): + info["inputs"]["INDEX"] = {"kind": "value", "value": int(val_str)} + else: + info["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + elif opcode == "data_insertatlist": + m_item = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt_for_parse, re.IGNORECASE) + m_index = re.search(r"at\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + elif opcode == "data_replaceitemoflist": + m_index = re.search(r"replace item\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + m_item = re.search(r"with\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + elif opcode == "event_whengreaterthan": + m = re.search(r">\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + + # String inputs + elif opcode == "looks_sayforsecs": + m = re.search(r"say\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_say": + m = re.search(r"say\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = parse_reporter_or_value(m.group(1).strip(), key, pick_key, all_generated_blocks) + elif opcode == "looks_thinkforsecs": + m = re.search(r"think\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_think": + m = re.search(r"think\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "sensing_askandwait": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["QUESTION"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["ITEM"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_setvariableto": + m_var = re.search(r"set\s*\[([^\]]+)\s*v\]\s*to\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m_var: + var_name = m_var.group(1).strip() + value_str = m_var.group(2).strip() + info["fields"]["VARIABLE"] = [var_name, None] + info["inputs"]["VALUE"] = parse_reporter_or_value(value_str, key, pick_key, all_generated_blocks) + + # Dropdown/Menu inputs (UPDATED) + elif opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "random position": option_val = "_random_" + elif option == "mouse-pointer": option_val = "_mouse_" + else: option_val = option + + menu_block_id = _register_block("motion_goto_menu", key, True, pick_key, all_generated_blocks, fields={"TO": [option_val, None]}) + info["inputs"]["TO"] = [1, menu_block_id] + elif opcode == "motion_glideto": + #m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + #m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*(?:\[([^\]]+)\s*v\]|\((.+?)\))", stmt_for_parse, re.IGNORECASE) + m_secs = re.search(r"glide\s*\(\s*(-?\d+(?:\.\d\s*\)\s*(?:secs|seconds)\sto\s*(?:\[([^\]]+)\s*v\]|\((.+?)\))", stmt_for_parse, re.IGNORECASE) + if m_secs: + info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + # Use group 3 for [random position v] or group 4 for (random position) + option = (m_secs.group(3) or m_secs.group(4)).strip() + if option == "random position": option_val = "_random_" + elif option == "mouse-pointer": option_val = "_mouse_" + else: option_val = option + + menu_block_id = _register_block("motion_glideto_menu", key, True, pick_key, all_generated_blocks, fields={"TO": [option_val, None]}) + info["inputs"]["TO"] = [1, menu_block_id] + elif opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option_val = "_mouse_" + else: option_val = option + + menu_block_id = _register_block("motion_pointtowards_menu", key, True, pick_key, all_generated_blocks, fields={"TOWARDS": [option_val, None]}) + info["inputs"]["TOWARDS"] = [1, menu_block_id] + elif opcode == "sensing_keypressed": + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", key, True, pick_key, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) + info["inputs"]["KEY_OPTION"] = [1, menu_block_id] + elif opcode == "sensing_touchingobject": + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option_val = "_mouse_" + elif option == "edge": option_val = "_edge_" + else: option_val = option + + menu_block_id = _register_block("sensing_touchingobjectmenu", key, True, pick_key, all_generated_blocks, fields={"TOUCHINGOBJECTMENU": [option_val, None]}) + info["inputs"]["TOUCHINGOBJECTMENU"] = [1, menu_block_id] + elif opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "myself": option_val = "_myself_" + else: option_val = option + + menu_block_id = _register_block("control_create_clone_of_menu", key, True, pick_key, all_generated_blocks, fields={"CLONE_OPTION": [option_val, None]}) + info["inputs"]["CLONE_OPTION"] = [1, menu_block_id] + elif opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sound_sounds_menu", key, True, pick_key, all_generated_blocks, fields={"SOUND_MENU": [option, None]}) + info["inputs"]["SOUND_MENU"] = [1, menu_block_id] + elif opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("looks_costume", key, True, pick_key, all_generated_blocks, fields={"COSTUME": [option, None]}) + info["inputs"]["COSTUME"] = [1, menu_block_id] + elif opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait"]: + m = re.search(r"switch backdrop to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("looks_backdrops", key, True, pick_key, all_generated_blocks, fields={"BACKDROP": [option, None]}) + info["inputs"]["BACKDROP"] = [1, menu_block_id] + elif opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + # Broadcast input doesn't use a separate menu block in definitions, it's a direct menu field in the input. + # So, it should be [1, [11, "message1", "id"]] or [1, [12, "message1"]] + # For now, let's keep it simple as [1, [11, option, None]] or similar if the definition allows. + # The `all_block_definitions` has `[1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]` + # Let's use that format, but without the specific ID for now. + info["inputs"]["BROADCAST_INPUT"] = [1, [11, option, None]] # Or just [1, [12, option]] if it's a simple string + + # Conditional inputs (Boolean blocks) + elif opcode in ["control_if", "control_if_else", "control_wait_until", "control_repeat_until"]: + #cond_match = re.search(r"<(.*?)>", stmt_for_parse) + cond_match = extract_condition_balanced(stmt_for_parse) + print(f"[THE CONDA MATCH]------------->{cond_match}") + if cond_match: + # Pass current block's key as parent for nested condition + info["inputs"]["CONDITION"] = parse_condition(cond_match.strip(), key, pick_key, all_generated_blocks) + elif opcode in ["operator_and", "operator_or", "operator_not", "operator_contains", + "sensing_touchingcolor", "sensing_coloristouchingcolor", "sensing_mousedown"]: + pass + + + # Fields parsing + if "VARIABLE" in info["fields"]: + m = re.search(r"\[([^\]]+)\s*v\]", stmt_for_parse) + if m: + var_name = m.group(1).strip() + info["fields"]["VARIABLE"] = [var_name, None] + if "LIST" in info["fields"]: + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["LIST"] = [m.group(1), None] + if "STOP_OPTION" in info["fields"]: + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STOP_OPTION"] = [m.group(1), None] + if "STYLE" in info["fields"]: + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STYLE"] = [m.group(1), None] + if "DRAG_MODE" in info["fields"]: + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["DRAG_MODE"] = [m.group(1), None] + if "EFFECT" in info["fields"] and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["EFFECT"] = [m.group(1).upper(), None] + if "NUMBER_NAME" in info["fields"] and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["NUMBER_NAME"] = [m.group(1), None] + if "FRONT_BACK" in info["fields"] and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FRONT_BACK"] = [m.group(1), None] + if "FORWARD_BACKWARD" in info["fields"] and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + if "OPERATOR" in info["fields"] and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["OPERATOR"] = [m.group(1).upper(), None] + if "CURRENTMENU" in info["fields"] and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + if "PROPERTY" in info["fields"] and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt_for_parse, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + info["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + if "WHENGREATERTHANMENU" in info["fields"] and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + if "KEY_OPTION" in info["fields"] and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["KEY_OPTION"] = [m.group(1), None] + if "BACKDROP" in info["fields"] and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BACKDROP"] = [m.group(1), None] + if "BROADCAST_OPTION" in info["fields"] and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + # Custom block specific parsing + if opcode == "procedures_definition": + proc_def_match = re.match(r"(?:define|procedure)\s+([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))?", stmt_for_parse, re.IGNORECASE) + if proc_def_match: + proc_name = proc_def_match.group(1).strip() + args_str = proc_def_match.group(2) + info["procedure_name"] = proc_name + info["is_custom_definition"] = True + + mutation_block = { + "tagName": "mutation", + "children": [], + "proccode": proc_name, + "argumentids": [], + "argumentnames": [], + "argumentdefaults": [], + "warp": False # Assuming non-warp by default + } + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for arg in args: + arg_id = f"%s" # Scratch uses %s for string args, %n for number args + # For simplicity, we'll just use a generic ID for now, or match Scratch's pattern + # For the plan, we just need the names and order. + mutation_block["argumentids"].append(arg_id) + mutation_block["argumentnames"].append(arg) + mutation_block["argumentdefaults"].append("") + + info["mutation"] = mutation_block + + elif opcode == "procedures_call": + call_match = re.match(r"(?:call\s+)?([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", stmt_for_parse, re.IGNORECASE) + if call_match: + custom_block_name = call_match.group(1).strip() + args_str = call_match.group(2) + info["custom_block_name"] = custom_block_name + + info["mutation"] = { + "tagName": "mutation", + "children": [], + "proccode": custom_block_name, + "argumentids": [], + "argumentnames": [], + "warp": False + } + + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for idx, arg_val_str in enumerate(args): + arg_input_name = f"argument_name_{idx+1}" + info["mutation"]["argumentids"].append(arg_input_name) # Use the input name as argument ID + info["mutation"]["argumentnames"].append(f"arg{idx+1}") # Placeholder name for mutation + + info["inputs"][arg_input_name] = parse_reporter_or_value(arg_val_str, key, pick_key, all_generated_blocks) # Pass current block's key + + i += 1 # Move to the next line + + # Final pass to ensure last blocks have next: None (already handled by stack pops) + # The build_script_flow function will correctly traverse the linked list. + while len(stack) > 1: # Keep the initial sentinel + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + #print(f"[ALL OPCODE BLCOKS KEY 2]: {all_generated_blocks}") + # with open("all_generated_blocks.json", "w") as f: + # json.dump(all_generated_blocks, f, indent=2) + # Construct the final flow output based on the collected top-level keys + # Recursively build the block structure for each top-level script + def build_script_flow(current_block_key, visited=None): + if visited is None: + visited = set() + + script_flow = [] + current_iter_key = current_block_key + + while current_iter_key: + # Detect cyclic reference + if current_iter_key in visited: + script_flow.append({ + "block_key": current_iter_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected, stopping recursion" + }) + break + + visited.add(current_iter_key) + block = all_generated_blocks.get(current_iter_key) + if not block: + break # Should not happen if keys are correct + + output_block = { + "block_key": block["id"], + "opcode": block["op_code"], + "type": block["block_shape"].replace(" Block", "").lower().replace("c-", "c_"), + "inputs": {}, + "fields": {}, + "shadow": block.get("shadow"), + "topLevel": block.get("topLevel"), + "parent": block.get("parent"), + "next": block.get("next") + } + + # Handle all input types + for inp_name, inp_val in block.get("inputs", {}).items(): + if inp_name in ["SUBSTACK", "SUBSTACK2"]: + if inp_val and len(inp_val) > 1 and inp_val[1] in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(inp_val[1], visited.copy()) + else: + output_block["inputs"][inp_name] = [] + elif inp_name == "PROCCONTAINER" and block.get("is_custom_definition"): + output_block["inputs"][inp_name] = inp_val + elif isinstance(inp_val, dict) and inp_val.get("kind") == "block": + # Recursively build nested reporter/boolean blocks + nested_block_key = inp_val["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val # Keep original if not found (shouldn't happen) + elif isinstance(inp_val, dict) and inp_val.get("kind") == "nested_reporter": + nested_block_key = inp_val["reporter"]["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val + else: + output_block["inputs"][inp_name] = inp_val + + for field_name, field_val in block.get("fields", {}).items(): + output_block["fields"][field_name] = field_val + + if block.get("custom_block_name"): + output_block["custom_block_name"] = block["custom_block_name"] + + if block.get("procedure_name"): + output_block["procedure_name"] = block["procedure_name"] + output_block["is_custom_definition"] = True + if "mutation" in block: # Include mutation for custom definitions + output_block["mutation"] = block["mutation"] + + + script_flow.append(output_block) + + # Proceed to the next block in sequence + next_key = block.get("next") + if next_key in visited: + script_flow.append({ + "block_key": next_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected in 'next' pointer" + }) + break + + current_iter_key = next_key + + return script_flow + + final_flow_output = [] + for key in top_level_script_keys: + final_flow_output.extend(build_script_flow(key)) + return {"flow": final_flow_output} + +################################################################################################################################################################# +#--------------------------------------------------[Security key id generation for the better understanding of keys]--------------------------------------------- +################################################################################################################################################################# + +def generate_secure_token(length=20): + charset = string.ascii_letters + string.digits + "!@#$%^&*()[]{}=+-_~" + return ''.join(secrets.choice(charset) for _ in range(length)) + +################################################################################################################################################################# +#--------------------------------------------------[Processed the two Skelton as input and generate refined skelton json]---------------------------------------- +################################################################################################################################################################# + +def process_scratch_blocks(all_generated_blocks, generated_output_json): + + processed_blocks = {} + + # Initialize dictionaries to store and reuse generated unique IDs + # This prevents creating multiple unique IDs for the same variable/broadcast across different blocks + variable_id_map = defaultdict(lambda: generate_secure_token(20)) + broadcast_id_map = defaultdict(lambda: generate_secure_token(20)) + + for block_id, gen_block_data in generated_output_json.items(): + processed_block = {} + all_gen_block_data = all_generated_blocks.get(block_id, {}) + + # Copy and update fields, inputs, next, parent, shadow, topLevel, mutation, and opcode + processed_block["opcode"] = all_gen_block_data.get("op_code", gen_block_data.get("op_code")) + processed_block["inputs"] = {} + processed_block["fields"] = {} + processed_block["shadow"] = all_gen_block_data.get("shadow", gen_block_data.get("shadow")) + processed_block["topLevel"] = all_gen_block_data.get("topLevel", gen_block_data.get("topLevel")) + processed_block["parent"] = all_gen_block_data.get("parent", gen_block_data.get("parent")) + processed_block["next"] = all_gen_block_data.get("next", gen_block_data.get("next")) + if "mutation" in all_gen_block_data: + processed_block["mutation"] = all_gen_block_data["mutation"] + + # Process inputs + if "inputs" in all_gen_block_data: + for input_name, input_data in all_gen_block_data["inputs"].items(): + if input_name in ["SUBSTACK", "CONDITION"]: + # These should always be type 2 + if isinstance(input_data, list) and len(input_data) == 2: + processed_block["inputs"][input_name] = [2, input_data[1]] + elif isinstance(input_data, dict) and input_data.get("kind") == "block": + processed_block["inputs"][input_name] = [2, input_data.get("block")] + else: # Fallback for unexpected formats, try to use the original if possible + processed_block["inputs"][input_name] = gen_block_data["inputs"].get(input_name, [2, None]) + + elif isinstance(input_data, dict): + if input_data.get("kind") == "value": + # Case 1: Direct value input + processed_block["inputs"][input_name] = [ + 1, + [ + 4, + str(input_data.get("value", "")) + ] + ] + elif input_data.get("kind") == "block": + # Case 3: Nested block input + existing_shadow_value = "" + if input_name in gen_block_data.get("inputs", {}) and \ + isinstance(gen_block_data["inputs"][input_name], list) and \ + len(gen_block_data["inputs"][input_name]) > 2 and \ + isinstance(gen_block_data["inputs"][input_name][2], list) and \ + len(gen_block_data["inputs"][input_name][2]) > 1: + existing_shadow_value = gen_block_data["inputs"][input_name][2][1] + + processed_block["inputs"][input_name] = [ + 3, + input_data.get("block", ""), + [ + 10, # Assuming 10 for number/string shadow + existing_shadow_value + ] + ] + elif input_data.get("kind") == "menu": + # Handle menu inputs like in event_broadcast + menu_option = input_data.get("option", "") + + # Generate or retrieve a unique ID for the broadcast message + broadcast_id = broadcast_id_map[menu_option] # Use defaultdict for unique IDs + + processed_block["inputs"][input_name] = [ + 1, + [ + 11, # This is typically the code for menu dropdowns + menu_option, + broadcast_id + ] + ] + elif isinstance(input_data, list): + # For cases like TOUCHINGOBJECTMENU, where input_data is a list [1, "block_id"] + processed_block["inputs"][input_name] = input_data + + + # Process fields + if "fields" in all_gen_block_data: + for field_name, field_value in all_gen_block_data["fields"].items(): + if field_name == "VARIABLE" and isinstance(field_value, list) and len(field_value) > 0: + # Generate or retrieve a unique ID for the variable + variable_name = field_value[0] + unique_id = variable_id_map[variable_name] # Use defaultdict for unique IDs + + processed_block["fields"][field_name] = [ + variable_name, + unique_id + ] + elif field_name == "STOP_OPTION": + processed_block["fields"][field_name] = [ + field_value[0], + None + ] + elif field_name == "TOUCHINGOBJECTMENU": + referenced_menu_block_id = all_gen_block_data["inputs"].get("TOUCHINGOBJECTMENU", [None, None])[1] + if referenced_menu_block_id and referenced_menu_block_id in all_generated_blocks: + menu_block = all_generated_blocks[referenced_menu_block_id] + menu_value = menu_block.get("fields", {}).get("TOUCHINGOBJECTMENU", ["", None])[0] + processed_block["fields"][field_name] = [menu_value, None] + else: + processed_block["fields"][field_name] = [field_value[0], None] + else: + processed_block["fields"][field_name] = field_value + + # Remove unwanted keys from the processed block + keys_to_remove = ["functionality", "block_shape", "id", "block_name", "block_type"] + for key in keys_to_remove: + if key in processed_block: + del processed_block[key] + + processed_blocks[block_id] = processed_block + return processed_blocks +################################################################################################################################################################# +#--------------------------------------------------[Unique secret key for skelton json to make sure it donot overwrite each other]------------------------------- +################################################################################################################################################################# + +def rename_blocks(block_json: dict, opcode_count: dict) -> tuple[dict, dict]: + """ + Replace each block key in block_json and each identifier in opcode_count + with a newly generated secure token. + + Args: + block_json: Mapping of block_key -> block_data. + opcode_count: Mapping of opcode -> list of block_keys. + + Returns: + A tuple of (new_block_json, new_opcode_count) with updated keys. + """ + # Step 1: Generate a secure token mapping for every existing block key + token_map = {} + for old_key in block_json.keys(): + # Ensure uniqueness in the unlikely event of a collision + while True: + new_key = generate_secure_token() + if new_key not in token_map.values(): + break + token_map[old_key] = new_key + + # Step 2: Rebuild block_json with new keys + new_block_json = {} + for old_key, block in block_json.items(): + new_key = token_map[old_key] + new_block_json[new_key] = block.copy() + + # Update parent and next references + if 'parent' in block and block['parent'] in token_map: + new_block_json[new_key]['parent'] = token_map[block['parent']] + if 'next' in block and block['next'] in token_map: + new_block_json[new_key]['next'] = token_map[block['next']] + + # Update inputs if they reference blocks + for inp_key, inp_val in block.get('inputs', {}).items(): + if isinstance(inp_val, list) and len(inp_val) == 2: + idx, ref = inp_val + if idx in (2, 3) and isinstance(ref, str) and ref in token_map: + new_block_json[new_key]['inputs'][inp_key] = [idx, token_map[ref]] + + # Step 3: Update opcode count map + new_opcode_count = {} + for opcode, key_list in opcode_count.items(): + new_opcode_count[opcode] = [token_map.get(k, k) for k in key_list] + + return new_block_json, new_opcode_count + +################################################################################################################################################################# +#--------------------------------------------------[Helper function to add Variables and Broadcasts [USed in main app file for main projectjson]]---------------- +################################################################################################################################################################# + +def variable_intialization(project_data): + """ + Updates variable and broadcast definitions in a Scratch project JSON, + populating the 'variables' and 'broadcasts' sections of the Stage target + and extracting initial values for variables. + + Args: + project_data (dict): The loaded JSON data of the Scratch project. + + Returns: + dict: The updated project JSON data. + """ + + stage_target = None + for target in project_data['targets']: + if target.get('isStage'): + stage_target = target + break + + if stage_target is None: + print("Error: Stage target not found in the project data.") + return project_data + + # Ensure 'variables' and 'broadcasts' exist in the Stage target + if "variables" not in stage_target: + stage_target["variables"] = {} + if "broadcasts" not in stage_target: + stage_target["broadcasts"] = {} + + # Helper function to recursively find and update variable/broadcast fields + def process_dict(obj): + if isinstance(obj, dict): + # Check for "data_setvariableto" opcode to extract initial values + if obj.get("opcode") == "data_setvariableto": + variable_field = obj.get("fields", {}).get("VARIABLE") + value_input = obj.get("inputs", {}).get("VALUE") + + if variable_field and isinstance(variable_field, list) and len(variable_field) == 2: + var_name = variable_field[0] + var_id = variable_field[1] + + initial_value = "" + if value_input and isinstance(value_input, list) and len(value_input) > 1 and \ + isinstance(value_input[1], list) and len(value_input[1]) > 1: + # Extract value from various formats, e.g., [1, [10, "0"]] or [3, [12, "score", "id"], [10, "0"]] + if value_input[1][0] == 10: # Direct value like [10, "0"] + initial_value = str(value_input[1][1]) + 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 + initial_value = str(value_input[2][1]) + elif isinstance(value_input[1], (str, int, float)): # For direct number/string inputs + initial_value = str(value_input[1]) + + + # Add/update the variable in the Stage's 'variables' with its initial value + stage_target["variables"][var_id] = [var_name, initial_value] + + + for key, value in obj.items(): + # Process variable definitions in 'fields' (for blocks that define variables like 'show variable') + if key == "VARIABLE" and isinstance(value, list) and len(value) == 2: + var_name = value[0] + var_id = value[1] + # Only add if not already defined with an initial value from set_variableto + if var_id not in stage_target["variables"]: + stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet + elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different + stage_target["variables"][var_id][0] = var_name + + + # Process broadcast definitions in 'inputs' (BROADCAST_INPUT) + elif key == "BROADCAST_INPUT" and isinstance(value, list) and len(value) == 2 and \ + isinstance(value[1], list) and len(value[1]) == 3 and value[1][0] == 11: + broadcast_name = value[1][1] + broadcast_id = value[1][2] + # Add/update the broadcast in the Stage's 'broadcasts' + stage_target["broadcasts"][broadcast_id] = broadcast_name + + # Process broadcast definitions in 'fields' (BROADCAST_OPTION) + elif key == "BROADCAST_OPTION" and isinstance(value, list) and len(value) == 2: + broadcast_name = value[0] + broadcast_id = value[1] + # Add/update the broadcast in the Stage's 'broadcasts' + stage_target["broadcasts"][broadcast_id] = broadcast_name + + # Recursively call for nested dictionaries or lists + process_dict(value) + elif isinstance(obj, list): + for i, item in enumerate(obj): + # Process variable references in 'inputs' (like [12, "score", "id"]) + if isinstance(item, list) and len(item) == 3 and item[0] == 12: + var_name = item[1] + var_id = item[2] + # Only add if not already defined with an initial value from set_variableto + if var_id not in stage_target["variables"]: + stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet + elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different + stage_target["variables"][var_id][0] = var_name + + process_dict(item) + + # Iterate through all targets to process their blocks + for target in project_data['targets']: + if "blocks" in target: + for block_id, block_data in target["blocks"].items(): + process_dict(block_data) + + return project_data + +def deduplicate_variables(project_data): + """ + Removes duplicate variable entries in the 'variables' dictionary of the Stage target, + prioritizing entries with non-empty values. + + Args: + project_data (dict): The loaded JSON data of the Scratch project. + + Returns: + dict: The updated project JSON data with deduplicated variables. + """ + + stage_target = None + for target in project_data['targets']: + if target.get('isStage'): + stage_target = target + break + + if stage_target is None: + print("Error: Stage target not found in the project data.") + return project_data + + if "variables" not in stage_target: + return project_data # No variables to deduplicate + + # Use a temporary dictionary to store the preferred variable entry by name + # Format: {variable_name: [variable_id, variable_name, variable_value]} + resolved_variables = {} + + for var_id, var_info in stage_target["variables"].items(): + var_name = var_info[0] + var_value = var_info[1] + + if var_name not in resolved_variables: + # If the variable name is not yet seen, add it + resolved_variables[var_name] = [var_id, var_name, var_value] + else: + # If the variable name is already seen, decide which one to keep + existing_id, existing_name, existing_value = resolved_variables[var_name] + + # Prioritize the entry with a non-empty value + if var_value != "" and existing_value == "": + resolved_variables[var_name] = [var_id, var_name, var_value] + # If both have non-empty values, or both are empty, keep the current one (arbitrary choice, but consistent) + # The current logic will effectively keep the last one encountered that has a value, + # or the very last one if all are empty. + elif var_value != "" and existing_value != "": + # If there are multiple non-empty values for the same variable name + # this keeps the one from the most recent iteration. + # For the given example, this will correctly keep "5". + resolved_variables[var_name] = [var_id, var_name, var_value] + elif var_value == "" and existing_value == "": + # If both are empty, just keep the current one (arbitrary) + resolved_variables[var_name] = [var_id, var_name, var_value] + + + # Reconstruct the 'variables' dictionary using the resolved entries + new_variables_dict = {} + for var_name, var_data in resolved_variables.items(): + var_id_to_keep = var_data[0] + var_name_to_keep = var_data[1] + var_value_to_keep = var_data[2] + new_variables_dict[var_id_to_keep] = [var_name_to_keep, var_value_to_keep] + + stage_target["variables"] = new_variables_dict + + return project_data + +def variable_adder_main(project_data): + try: + declare_variable_json= variable_intialization(project_data) + except Exception as e: + print(f"Error error in the variable initialization opcodes: {e}") + try: + processed_json= deduplicate_variables(declare_variable_json) + return + except Exception as e: + print(f"Error error in the variable initialization opcodes: {e}") + +################################################################################################################################################################# +#--------------------------------------------------[Helper main function]---------------------------------------------------------------------------------------- +################################################################################################################################################################# + +def block_builder(opcode_count,pseudo_code): + try: + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(opcode_count, all_block_definitions) + except Exception as e: + print(f"Error generating blocks from opcodes: {e}") + return {} + try: + all_generated_blocks = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code) + except Exception as e: + print(f"Error generating plan from blocks: {e}") + return {} + try: + processed_blocks= process_scratch_blocks(all_generated_blocks, generated_output_json) + except Exception as e: + print(f"Error processing Scratch blocks: {e}") + return {} + renamed_blocks, renamed_counts = rename_blocks(processed_blocks, initial_opcode_occurrences) + return renamed_blocks + +################################################################################################################################################################# +#--------------------------------------------------[Example use of the function here]---------------------------------------------------------------------------- +################################################################################################################################################################# + +# initial_opcode_counts = [ +# { +# "opcode": "event_whenflagclicked", +# "count": 1 +# }, +# { +# "opcode": "data_setvariableto", +# "count": 2 +# }, +# { +# "opcode": "data_showvariable", +# "count": 2 +# }, +# { +# "opcode": "event_broadcast", +# "count": 1 +# } +# ] +# pseudo_code=""" +# when green flag clicked +# set [score v] to (0) +# set [lives v] to (3) +# show variable [score v] +# show variable [lives v] +# broadcast [Game Start v] +# """ + +# generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) +# all_generated_blocks = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code) +# processed_blocks= process_scratch_blocks(all_generated_blocks, generated_output_json) +# print(all_generated_blocks) +# print("--------------\n\n") +# print(processed_blocks) +# print("--------------\n\n") +# print(initial_opcode_occurrences) \ No newline at end of file diff --git a/utils/block_var_setter.py b/utils/block_var_setter.py new file mode 100644 index 0000000000000000000000000000000000000000..52a10e87b8f5b72cd52540882ce765e4271155c1 --- /dev/null +++ b/utils/block_var_setter.py @@ -0,0 +1,290 @@ +import json + +def update_scratch_project_data(project_data): + """ + Updates variable and broadcast definitions in a Scratch project JSON, + populating the 'variables' and 'broadcasts' sections of the Stage target + and extracting initial values for variables. + + Args: + project_data (dict): The loaded JSON data of the Scratch project. + + Returns: + dict: The updated project JSON data. + """ + + stage_target = None + for target in project_data['targets']: + if target.get('isStage'): + stage_target = target + break + + if stage_target is None: + print("Error: Stage target not found in the project data.") + return project_data + + # Ensure 'variables' and 'broadcasts' exist in the Stage target + if "variables" not in stage_target: + stage_target["variables"] = {} + if "broadcasts" not in stage_target: + stage_target["broadcasts"] = {} + + # Helper function to recursively find and update variable/broadcast fields + def process_dict(obj): + if isinstance(obj, dict): + # Check for "data_setvariableto" opcode to extract initial values + if obj.get("opcode") == "data_setvariableto": + variable_field = obj.get("fields", {}).get("VARIABLE") + value_input = obj.get("inputs", {}).get("VALUE") + + if variable_field and isinstance(variable_field, list) and len(variable_field) == 2: + var_name = variable_field[0] + var_id = variable_field[1] + + initial_value = "" + if value_input and isinstance(value_input, list) and len(value_input) > 1 and \ + isinstance(value_input[1], list) and len(value_input[1]) > 1: + # Extract value from various formats, e.g., [1, [10, "0"]] or [3, [12, "score", "id"], [10, "0"]] + if value_input[1][0] == 10: # Direct value like [10, "0"] + initial_value = str(value_input[1][1]) + 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 + initial_value = str(value_input[2][1]) + elif isinstance(value_input[1], (str, int, float)): # For direct number/string inputs + initial_value = str(value_input[1]) + + + # Add/update the variable in the Stage's 'variables' with its initial value + stage_target["variables"][var_id] = [var_name, initial_value] + + + for key, value in obj.items(): + # Process variable definitions in 'fields' (for blocks that define variables like 'show variable') + if key == "VARIABLE" and isinstance(value, list) and len(value) == 2: + var_name = value[0] + var_id = value[1] + # Only add if not already defined with an initial value from set_variableto + if var_id not in stage_target["variables"]: + stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet + elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different + stage_target["variables"][var_id][0] = var_name + + + # Process broadcast definitions in 'inputs' (BROADCAST_INPUT) + elif key == "BROADCAST_INPUT" and isinstance(value, list) and len(value) == 2 and \ + isinstance(value[1], list) and len(value[1]) == 3 and value[1][0] == 11: + broadcast_name = value[1][1] + broadcast_id = value[1][2] + # Add/update the broadcast in the Stage's 'broadcasts' + stage_target["broadcasts"][broadcast_id] = broadcast_name + + # Process broadcast definitions in 'fields' (BROADCAST_OPTION) + elif key == "BROADCAST_OPTION" and isinstance(value, list) and len(value) == 2: + broadcast_name = value[0] + broadcast_id = value[1] + # Add/update the broadcast in the Stage's 'broadcasts' + stage_target["broadcasts"][broadcast_id] = broadcast_name + + # Recursively call for nested dictionaries or lists + process_dict(value) + elif isinstance(obj, list): + for i, item in enumerate(obj): + # Process variable references in 'inputs' (like [12, "score", "id"]) + if isinstance(item, list) and len(item) == 3 and item[0] == 12: + var_name = item[1] + var_id = item[2] + # Only add if not already defined with an initial value from set_variableto + if var_id not in stage_target["variables"]: + stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet + elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different + stage_target["variables"][var_id][0] = var_name + + process_dict(item) + + # Iterate through all targets to process their blocks + for target in project_data['targets']: + if "blocks" in target: + for block_id, block_data in target["blocks"].items(): + process_dict(block_data) + + return project_data + +def deduplicate_variables(project_data): + """ + Removes duplicate variable entries in the 'variables' dictionary of the Stage target, + prioritizing entries with non-empty values. + + Args: + project_data (dict): The loaded JSON data of the Scratch project. + + Returns: + dict: The updated project JSON data with deduplicated variables. + """ + + stage_target = None + for target in project_data['targets']: + if target.get('isStage'): + stage_target = target + break + + if stage_target is None: + print("Error: Stage target not found in the project data.") + return project_data + + if "variables" not in stage_target: + return project_data # No variables to deduplicate + + # Use a temporary dictionary to store the preferred variable entry by name + # Format: {variable_name: [variable_id, variable_name, variable_value]} + resolved_variables = {} + + for var_id, var_info in stage_target["variables"].items(): + var_name = var_info[0] + var_value = var_info[1] + + if var_name not in resolved_variables: + # If the variable name is not yet seen, add it + resolved_variables[var_name] = [var_id, var_name, var_value] + else: + # If the variable name is already seen, decide which one to keep + existing_id, existing_name, existing_value = resolved_variables[var_name] + + # Prioritize the entry with a non-empty value + if var_value != "" and existing_value == "": + resolved_variables[var_name] = [var_id, var_name, var_value] + # If both have non-empty values, or both are empty, keep the current one (arbitrary choice, but consistent) + # The current logic will effectively keep the last one encountered that has a value, + # or the very last one if all are empty. + elif var_value != "" and existing_value != "": + # If there are multiple non-empty values for the same variable name + # this keeps the one from the most recent iteration. + # For the given example, this will correctly keep "5". + resolved_variables[var_name] = [var_id, var_name, var_value] + elif var_value == "" and existing_value == "": + # If both are empty, just keep the current one (arbitrary) + resolved_variables[var_name] = [var_id, var_name, var_value] + + + # Reconstruct the 'variables' dictionary using the resolved entries + new_variables_dict = {} + for var_name, var_data in resolved_variables.items(): + var_id_to_keep = var_data[0] + var_name_to_keep = var_data[1] + var_value_to_keep = var_data[2] + new_variables_dict[var_id_to_keep] = [var_name_to_keep, var_value_to_keep] + + stage_target["variables"] = new_variables_dict + + return project_data + +# Example usage with your provided JSON: +if __name__ == "__main__": + json_data = { + "targets": [ + {"isStage": True, "name": "Stage", "variables": {},"lists": {}, "broadcasts": {}, "blocks": { + "event_whenflagclicked_1": { + "opcode": "event_whenflagclicked", + "inputs": {}, + "fields": {}, + "shadow": False, + "topLevel": True, + "parent": None, + "next": "data_setvariableto_1" + }, + "data_setvariableto_1": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": { + "VARIABLE": [ + "score", + "$ELgBAKpb[&l+DX$EMK4" + ] + }, + "shadow": False, + "topLevel": False, + "parent": "event_whenflagclicked_1", + "next": "data_setvariableto_2" + }, + "data_setvariableto_2": { + "opcode": "data_setvariableto", + "inputs": { + "VALUE": [ + 1, + [ + 4, + "3" + ] + ] + }, + "fields": { + "VARIABLE": [ + "lives", + "Gb]1jA+H{h_6z1^Fn!-a" + ] + }, + "shadow": False, + "topLevel": False, + "parent": "data_setvariableto_1", + "next": "data_showvariable_1" + }, + "data_showvariable_1": { + "opcode": "data_showvariable", + "inputs": {}, + "fields": { + "VARIABLE": [ + "score", + "$ELgBAKpb[&l+DX$EMK4" + ] + }, + "shadow": False, + "topLevel": False, + "parent": "data_setvariableto_2", + "next": "data_showvariable_2" + }, + "data_showvariable_2": { + "opcode": "data_showvariable", + "inputs": {}, + "fields": { + "VARIABLE": [ + "lives", + "Gb]1jA+H{h_6z1^Fn!-a" + ] + }, + "shadow": False, + "topLevel": False, + "parent": "data_showvariable_1", + "next": "event_broadcast_1" + }, + "event_broadcast_1": { + "opcode": "event_broadcast", + "inputs": { + "BROADCAST_INPUT": [ + 1, + [ + 11, + "Game Start", + "hN1d@^S}e)H~8(qp)rGN" + ] + ] + }, + "fields": {}, + "shadow": False, + "topLevel": False, + "parent": "data_showvariable_2", + "next": None + } + }, "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}, + {"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"}, + {"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"}} + + + updated_json_data = update_scratch_project_data(json_data) + updated_json_data = deduplicate_variables(json_data) + print(json.dumps(updated_json_data, indent=1)) \ No newline at end of file diff --git a/utils/generated_output_json.json b/utils/generated_output_json.json new file mode 100644 index 0000000000000000000000000000000000000000..ecbdb275afb4788491b50434dc8a72e935ceb0c4 --- /dev/null +++ b/utils/generated_output_json.json @@ -0,0 +1,317 @@ +{ + "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": false, + "id": "event_whenflagclicked_1", + "parent": null, + "next": null, + "sub_stacks": {} + }, + "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": [ + 1, + [ + 4, + "0" + ] + ], + "Y": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "id": "motion_gotoxy_1", + "parent": null, + "next": null, + "sub_stacks": {} + }, + "motion_xposition_1": { + "block_name": "(x position)", + "block_type": "Motion", + "block_shape": "Reporter Block", + "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": false, + "id": "motion_xposition_1", + "parent": null, + "next": null, + "sub_stacks": {} + }, + "motion_setx_1": { + "block_name": "set x to ()", + "block_type": "Motion", + "block_shape": "Stack Block", + "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": { + "X": [ + 1, + [ + 4, + "0" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "id": "motion_setx_1", + "parent": null, + "next": null, + "sub_stacks": {} + }, + "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": {}, + "fields": {}, + "shadow": false, + "topLevel": false, + "sub_stacks": { + "SUBSTACK": [ + 2, + null + ] + }, + "id": "control_forever_1", + "parent": null, + "next": null + }, + "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": [ + 2, + null + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "sub_stacks": { + "SUBSTACK": [ + 2, + null + ] + }, + "id": "control_if_1", + "parent": null, + "next": null + }, + "control_stop_1": { + "block_name": "stop [v]", + "block_type": "Control", + "block_shape": "Cap Block", + "op_code": "control_stop", + "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.", + "inputs": {}, + "fields": { + "STOP_OPTION": [ + "all", + null + ] + }, + "shadow": false, + "topLevel": false, + "mutation": { + "tagName": "mutation", + "children": [], + "hasnext": "false" + }, + "id": "control_stop_1", + "parent": null, + "next": null, + "sub_stacks": {} + }, + "operator_lt_1": { + "block_name": "<() < ()>", + "block_type": "operator", + "block_shape": "Boolean Block", + "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": { + "OPERAND1": [ + 1, + [ + 10, + "" + ] + ], + "OPERAND2": [ + 1, + [ + 10, + "50" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "id": "operator_lt_1", + "parent": null, + "next": null, + "sub_stacks": {} + }, + "sensing_touchingobject_1": { + "block_name": "", + "block_type": "Sensing", + "op_code": "sensing_touchingobject", + "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": { + "TOUCHINGOBJECTMENU": [ + 1, + "sensing_touchingobjectmenu_1" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "id": "sensing_touchingobject_1", + "parent": null, + "next": null, + "sub_stacks": {} + }, + "sensing_touchingobjectmenu_1": { + "block_name": "touching object menu", + "block_type": "Sensing", + "block_shape": "Reporter Block", + "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, + "fields": { + "TOUCHINGOBJECTMENU": [ + "_mouse_", + null + ] + }, + "shadow": true, + "topLevel": false, + "id": "sensing_touchingobjectmenu_1", + "next": null, + "parent": "sensing_touchingobject_1", + "sub_stacks": {} + }, + "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": [ + 1, + [ + 11, + "message1", + "5O!nei;S$!c!=hCT}0:a" + ] + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false, + "id": "event_broadcast_1", + "parent": null, + "next": null, + "sub_stacks": {} + }, + "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": [ + 1, + [ + 10, + "0" + ] + ] + }, + "fields": { + "VARIABLE": [ + "my variable", + "`jEk@4|i[#Fk?(8x)AV.-my variable" + ] + }, + "shadow": false, + "topLevel": false, + "id": "data_setvariableto_1", + "parent": null, + "next": null, + "sub_stacks": {} + }, + "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": [ + "my variable", + "`jEk@4|i[#Fk?(8x)AV.-my variable" + ] + }, + "shadow": false, + "topLevel": false, + "id": "data_showvariable_1", + "parent": null, + "next": null, + "sub_stacks": {} + }, + "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": [ + "my variable", + "`jEk@4|i[#Fk?(8x)AV.-my variable" + ] + }, + "shadow": false, + "topLevel": false, + "id": "data_showvariable_2", + "parent": null, + "next": null, + "sub_stacks": {} + } +} \ No newline at end of file diff --git a/utils/half_working_plan.py b/utils/half_working_plan.py new file mode 100644 index 0000000000000000000000000000000000000000..9e73e228e57723dc61897569b9d1c9021a48becf --- /dev/null +++ b/utils/half_working_plan.py @@ -0,0 +1,1885 @@ +import json +import copy +import re +from collections import defaultdict + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False + + generated_blocks[main_key] = main_block_data + + # Handle menus + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_key + + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 1 and \ + main_block_data["inputs"][input_name][0] == 1: + + main_block_data["inputs"][input_name][1] = menu_key + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +# Consolidated block definitions from all JSON files +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_distanceto": { # Added sensing_distanceto + "block_name": "(distance to ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto", + "functionality": "Reports the distance from the sprite to the mouse-pointer or another specified sprite.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": False, "topLevel": True + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": [ + { + "name": "PROCCONTAINER", + "type": "block_prototype" + } + ], + "fields": {}, + "shadow": False, + "topLevel": True + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": [], # Inputs are dynamic based on definition + "fields": {}, + "shadow": False, + "topLevel": True + } +} + + +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, pick_key_func): + text = text.strip() + + # Check for numeric literal (including parenthesized numbers like "(0)" or "(10)") + m_num = re.fullmatch(r"\(?\s*(-?\d+(\.\d+)?)\s*\)?", text) + if m_num: + val_str = m_num.group(1) + return {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # Check for string literal (e.g., "[Hello!]") + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + # Check for simple reporters, potentially with outer parentheses + m_simple_reporter = re.fullmatch(r"\((.+?)\)", text) + if m_simple_reporter: + inner_text = m_simple_reporter.group(1).strip() + if inner_text in simple_reporters: + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func(simple_reporters[inner_text]), "inputs": {}}} + # Also check for simple reporters without parentheses (e.g., if passed directly) + if text in simple_reporters: + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func(simple_reporters[text]), "inputs": {}}} + + + # Variable reporter: [score v] or (score) or just "score" + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_variable"), "inputs": {}, "fields": {"VARIABLE": [var_name, None]}}} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + # Ensure it's not a simple reporter already handled, or a number + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_variable"), "inputs": {}, "fields": {"VARIABLE": [potential_var_name, None]}}} + # Handle plain variable names like "score", "number 1", "total score" + if re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_ ]*", text): # Allow spaces for "number 1" etc. + # Exclude known simple reporters that don't have 'v' or parentheses + if text not in simple_reporters: + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_variable"), "inputs": {}, "fields": {"VARIABLE": [text, None]}}} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_list"), "inputs": {}, "fields": {"LIST": [list_name, None]}}} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + max_val = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_random"), "inputs": {"FROM": min_val, "TO": max_val}}} + + # (join ()()) (operator_join) - handle both [] and () for inputs + m = re.search(r"join \((.+?)\) \((.+?)\)", text) # Try (val) (val) + if not m: + m = re.search(r"join \[(.+?)\] \[(.+?)\]", text) # Try [val] [val] + if m: + str1 = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + str2 = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_join"), "inputs": {"STRING1": str1, "STRING2": str2}}} + + # letter () of () (operator_letterof) - handle both [] and () for inputs + m = re.search(r"letter \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + string_val = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_letterof"), "inputs": {"LETTER": index, "STRING": string_val}}} + + # (length of ()) (operator_length) - handle both [] and () for inputs + m = re.search(r"length of \((.+?)\)", text) + if not m: + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + list_or_string_val = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_length"), "inputs": {"STRING": list_or_string_val}}} + + + # (() mod ()) (operator_mod) + m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + if m: + num1 = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + num2 = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_mod"), "inputs": {"NUM1": num1, "NUM2": num2}}} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_round"), "inputs": {"NUM": num}}} + + # (() of ()) (operator_mathop) - handle variable for function type + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) # e.g. [sqrt v] of ((x pos) * (x pos)) + if m: + func_type = m.group(1).strip() + value = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_mathop"), "inputs": {"NUM": value}, "fields": {"OPERATOR": [func_type.upper(), None]}}} + # Also handle direct string for function type (e.g., "abs of (x)") + m = re.search(r"([a-zA-Z]+)\s*of\s*\((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_mathop"), "inputs": {"NUM": value}, "fields": {"OPERATOR": [func_type.upper(), None]}}} + + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + # This regex is designed to handle nested parentheses correctly. + # It looks for an opening parenthesis, then non-parenthesis characters or balanced parentheses, + # followed by an operator, and then the second operand. + # This is a simplified approach; a full-fledged parser would use a stack. + arithmetic_match = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + if not arithmetic_match: + # Try to match without outer parentheses for the operands, but still with an operator + arithmetic_match = re.search(r"(.+?)\s*([+\-*/])\s*(.+)", text) + + if arithmetic_match: + op1_str = arithmetic_match.group(1).strip() + operator_symbol = arithmetic_match.group(2).strip() + op2_str = arithmetic_match.group(3).strip() + + op1 = parse_reporter_or_value(op1_str, pick_key_func) + op2 = parse_reporter_or_value(op2_str, pick_key_func) + + opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func(opcode_map[operator_symbol]), "inputs": {"NUM1": op1, "NUM2": op2}}} + + + # (costume ()) (looks_costumenumbername) - handle with or without 'v' + m = re.search(r"costume \((.+?)\)", text) + if not m: + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("looks_costumenumbername"), "inputs": {}, "fields": {"NUMBER_NAME": [option, None]}}} + + # (backdrop ()) (looks_backdropnumbername) - handle with or without 'v' + m = re.search(r"backdrop \((.+?)\)", text) + if not m: + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("looks_backdropnumbername"), "inputs": {}, "fields": {"NUMBER_NAME": [option, None]}}} + + # (distance to ()) (sensing_distanceto) - handle with or without 'v' + m = re.search(r"distance to \((.+?)\)", text) + if not m: + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("sensing_distanceto"), "inputs": {}, "fields": {"TARGET": [target_val, None]}}} + + # (current ()) (sensing_current) - handle with or without 'v' + m = re.search(r"current \((.+?)\)", text) + if not m: + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("sensing_current"), "inputs": {}, "fields": {"CURRENTMENU": [unit.upper(), None]}}} + + # (() of ()) (sensing_of) - handle both variable and non-variable properties, and objects + # Updated regex to correctly capture the property and object, including nested reporters for property + m = re.search(r"\((.+?)\)\s*of\s*\((.+?)\)", text) # (prop) of (obj) + if not m: + m = re.search(r"\((.+?)\)\s*of\s*\[([^\]]+)\s*v\]", text) # (prop) of [obj v] + if m: + prop_str = m.group(1).strip() + obj = m.group(2).strip() + + # Map common property names to their internal Scratch representation + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + property_value = prop_map.get(prop_str, prop_str) # Use mapped value or original string + + # The object can be a sprite name or "_stage_" + obj_kind = "menu" + if obj.lower() == "stage": obj_val = "_stage_" + elif obj.lower() == "myself": obj_val = "_myself_" + else: obj_val = obj # Assume it's a sprite name + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("sensing_of"), "inputs": {"OBJECT": {"kind": obj_kind, "option": obj_val}}, "fields": {"PROPERTY": [property_value, None]}}} + + # (item (index) of [list v]) (data_itemoflist) - handle with or without 'v' and parentheses for index + m = re.search(r"item \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + list_name = m.group(2).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_itemoflist"), "inputs": {"INDEX": index}, "fields": {"LIST": [list_name, None]}}} + + # (item # of [item] in [list v]) (data_itemnumoflist) - handle with or without 'v' and parentheses for item + m = re.search(r"item # of \((.+?)\) in \((.+?)\)", text) + if not m: + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + list_name = m.group(2).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_itemnumoflist"), "inputs": {"ITEM": item}, "fields": {"LIST": [list_name, None]}}} + + + raise ValueError(f"Can't parse reporter or value: {text}") + +def parse_condition(stmt, pick_key_func): + # Helper to peel off all outer parentheses from either side + def strip_parens(s: str) -> str: + s = s.strip() + # remove any leading '(' + while s.startswith("("): + s = s[1:].strip() + # remove any trailing ')' + while s.endswith(")"): + s = s[:-1].strip() + return s + + # 1) Normalize: strip any number of outer parentheses + stmt_norm = stmt.strip() + while stmt_norm.startswith("(") and stmt_norm.endswith(")"): + stmt_norm = stmt_norm[1:-1].strip() + + # 2) Re-wrap in angle brackets so existing <…> regexes match + stmt_for_regex = f"<{stmt_norm}>" + stmt_lower = stmt_for_regex.lower() + + # <() < ()> (operator_lt) + m = re.search(r"<\s*(.+?)\s*<\s*(.+?)\s*>", stmt_lower) + if m: + op1_text = strip_parens(m.group(1)) + op2_text = strip_parens(m.group(2)) + operand1 = parse_reporter_or_value(op1_text, pick_key_func) + operand2 = parse_reporter_or_value(op2_text, pick_key_func) + return { + "block": pick_key_func("operator_lt"), + "inputs": {"OPERAND1": operand1, "OPERAND2": operand2} + } + + # <() = ()> (operator_equals) + m = re.search(r"<\s*(.+?)\s*=\s*(.+?)\s*>", stmt_lower) + if m: + op1_text = strip_parens(m.group(1)) + op2_text = strip_parens(m.group(2)) + operand1 = parse_reporter_or_value(op1_text, pick_key_func) + operand2 = parse_reporter_or_value(op2_text, pick_key_func) + return { + "block": pick_key_func("operator_equals"), + "inputs": {"OPERAND1": operand1, "OPERAND2": operand2} + } + + # <() > ()> (operator_gt) + m = re.search(r"<\s*(.+?)\s*>\s*(.+?)\s*>", stmt_lower) + if m: + op1_text = strip_parens(m.group(1)) + op2_text = strip_parens(m.group(2)) + operand1 = parse_reporter_or_value(op1_text, pick_key_func) + operand2 = parse_reporter_or_value(op2_text, pick_key_func) + return { + "block": pick_key_func("operator_gt"), + "inputs": {"OPERAND1": operand1, "OPERAND2": operand2} + } + + # <<> and <>> (operator_and) + m = re.search(r"<\s*(.+?)\s*and\s*(.+?)\s*>", stmt_lower) + if m: + cond1 = parse_condition(strip_parens(m.group(1)), pick_key_func) + cond2 = parse_condition(strip_parens(m.group(2)), pick_key_func) + return { + "block": pick_key_func("operator_and"), + "inputs": {"OPERAND1": cond1, "OPERAND2": cond2} + } + + # <<> or <>> (operator_or) + m = re.search(r"<\s*(.+?)\s*or\s*(.+?)\s*>", stmt_lower) + if m: + cond1 = parse_condition(strip_parens(m.group(1)), pick_key_func) + cond2 = parse_condition(strip_parens(m.group(2)), pick_key_func) + return { + "block": pick_key_func("operator_or"), + "inputs": {"OPERAND1": cond1, "OPERAND2": cond2} + } + + # > (operator_not) + m = re.search(r"", stmt_lower) + if m: + cond = parse_condition(strip_parens(m.group(1)), pick_key_func) + return { + "block": pick_key_func("operator_not"), + "inputs": {"OPERAND": cond} + } + + # <() contains ()?> (operator_contains) + m = re.search(r"<\s*\[([^\]]+)\]\s*contains\s*\[([^\]]+)\]\s*\?>", stmt_lower) + if m: + str1 = strip_parens(m.group(1)) + str2 = strip_parens(m.group(2)) + string1 = parse_reporter_or_value(f"[{str1}]", pick_key_func) + string2 = parse_reporter_or_value(f"[{str2}]", pick_key_func) + return { + "block": pick_key_func("operator_contains"), + "inputs": {"STRING1": string1, "STRING2": string2} + } + + # (sensing_touchingobject) + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_lower) + if m: + option_val = { + "mouse-pointer": "_mouse_", + "edge": "_edge_" + }.get(m.group(1).strip(), m.group(1).strip()) + return { + "block": pick_key_func("sensing_touchingobject"), + "inputs": {"TOUCHINGOBJECTMENU": {"kind": "menu", "option": option_val}} + } + + # (sensing_touchingcolor) + m = re.search(r"touching color \(#[0-9a-fA-F]{6}\)\?", stmt_lower) + if m: + color_value = m.group(0)[15:-2].strip() + return { + "block": pick_key_func("sensing_touchingcolor"), + "inputs": {"COLOR": {"kind": "color", "value": color_value}} + } + + # (sensing_coloristouchingcolor) + m = re.search(r"color \(#[0-9a-fA-F]{6}\) is touching \(#[0-9a-fA-F]{6}\)\?", stmt_lower) + if m: + colors = re.findall(r"(#[0-9a-fA-F]{6})", m.group(0)) + return { + "block": pick_key_func("sensing_coloristouchingcolor"), + "inputs": {"COLOR1": {"kind": "color", "value": colors[0]}, + "COLOR2": {"kind": "color", "value": colors[1]}} + } + + # (sensing_keypressed) + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_lower) + if m: + key_option = m.group(1).strip() + return { + "block": pick_key_func("sensing_keypressed"), + "inputs": {"KEY_OPTION": {"kind": "menu", "option": key_option}} + } + + # (sensing_mousedown) + if stmt_lower == "": + return {"block": pick_key_func("sensing_mousedown"), "inputs": {}} + + # <[my list v] contains ()?> (data_listcontainsitem) + m = re.search(r"\[([^\]]+)\s*v\] contains \[(.+?)\]\?", stmt_lower) + if m: + return { + "block": pick_key_func("data_listcontainsitem"), + "inputs": {"LIST": {"kind": "list_variable", "name": strip_parens(m.group(1))}, + "ITEM": {"kind": "value", "value": strip_parens(m.group(2))}} + } + + raise ValueError(f"Can't parse condition: {stmt}") + +def classify(line): + """ + Classifies a pseudo-code line into its corresponding Scratch opcode and block type. + Order of checks matters: more specific patterns should come before more general ones. + """ + l = line.lower().strip() + + # Ignore comments + if l.startswith("//"): return None, None + + # Hat Blocks (most specific first) + if re.match(r"when green flag click(ed)?", l): return "event_whenflagclicked", "hat" + if re.match(r"when (.+?) key press(ed)?", l): return "event_whenkeypressed", "hat" + if re.match(r"when this sprite click(ed)?", l): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if re.match(r"when i start as a clo(ne)?", l): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + if l.startswith("procedure "): return "procedures_definition", "hat" # For "procedure moveBall" + + # Motion Blocks + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + # IMPORTANT: More specific glide block before less specific one + if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if re.match(r"set x to\s*\(.+\)", l): return "motion_setx", "stack" # Specific for set x + if l.startswith("change y by"): return "motion_changeyby", "stack" + if re.match(r"set y to\s*\(.+\)", l): return "motion_sety", "stack" # Specific for set y + if re.match(r"if on edge, bounce( off edge)?", l): return "motion_ifonedgebounce", "stack" + if re.match(r"bounce off edg(e)?", l): return "motion_ifonedgebounce", "stack" # Alias + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + + + # Looks Blocks + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if re.match(r"next costum(e)?", l): return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + # Updated regex for change/set effect by/to + if re.match(r"change\s*(\[.+?v\]|\(.+?\))?\s*effect by", l): return "looks_changeeffectby", "stack" + if re.match(r"set\s*(\[.+?v\]|\(.+?\))?\s*effect to", l): return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + # Sound Blocks + if re.match(r"play sound (.+?) until do(ne)?", l): return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + # Event Blocks (broadcasts) + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + # Control Blocks + if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + # Updated regex for stop block to handle different options + if re.match(r"stop \[(all|this script|other scripts in sprite)\s*v\]", l): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + # Data Blocks + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + # Updated regex for delete of list + if re.match(r"delete \((.+?)\) of \[([^\]]+)\s*v\]", l): return "data_deleteoflist", "stack" + if l.startswith("delete all of [" ): return "data_deletealloflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + # Sensing Blocks + if re.match(r"ask (.+?) and wai(t)?", l): return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom Blocks (procedures_call) - specific rule for "call" + if l.startswith("call "): + return "procedures_call", "stack" + + # Custom Blocks (procedures_call) - LAST RESORT (generic match) + # This should be the very last check for stack-type blocks to avoid conflicts. + # It tries to match anything that looks like a function call with or without arguments. + custom_block_match = re.match(r"([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", l) + if custom_block_match: + # Before returning, ensure it's not a known simple reporter or variable name + # that might have been missed or is being used standalone. + # This is a heuristic; a full parser would be more robust. + potential_name = custom_block_match.group(1).strip() + if potential_name not in ["x position", "y position", "direction", "mouse x", "mouse y", "loudness", "timer", "days since 2000", "username", "answer", "size", "volume"] and \ + not re.fullmatch(r"\[[^\]]+\]", potential_name) and \ + not re.fullmatch(r"\[[^\]]+\]\s*v", potential_name): + return "procedures_call", "stack" + + + raise ValueError(f"Unknown statement: {line!r}") + + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key -> block_data (pre-generated block definitions) + • opcode_keys: dict of opcode -> list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... list of block dictionaries ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + # This will store all generated blocks by their keys, including parent/next links + all_generated_blocks = {} + + # Stack stores (indent, parent_block_key, last_block_key_in_this_chain) + # parent_block_key: The key of the C-block or Hat block that owns the current chain. + # last_block_key_in_this_chain: The key of the last block added to the current chain (for 'next' linking). + stack = [(-1, None, None)] # Initial: top-level scope (indent -1, no parent, no last block) + + # This will store the keys of the top-level scripts (Hat blocks) in order + top_level_script_keys = [] + + lines = pseudo_code.splitlines() + i = 0 + while i < len(lines): + raw_line = lines[i] + stripped_line = raw_line.strip() + + # Skip empty lines and comments + if not stripped_line or stripped_line.startswith("//"): + i += 1 + continue + + current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2 + + # Handle 'else' and 'end' keywords first, as they control scope + if stripped_line.lower() == "else": + # Pop the 'then' substack's scope + popped_indent, popped_parent_key, popped_last_key_in_chain = stack.pop() + if popped_last_key_in_chain: + all_generated_blocks[popped_last_key_in_chain]["next"] = None + + # Link the 'then' substack to its parent 'if-else' block + if popped_parent_key and all_generated_blocks[popped_parent_key]["opcode"] == "control_if_else": + if all_generated_blocks[popped_parent_key].get("_first_substack_block_key"): + all_generated_blocks[popped_parent_key]["inputs"]["SUBSTACK"] = [2, all_generated_blocks[popped_parent_key]["_first_substack_block_key"]] + else: + all_generated_blocks[popped_parent_key]["inputs"]["SUBSTACK"] = [2, None] + del all_generated_blocks[popped_parent_key]["_first_substack_block_key"] + + # Now, push a new scope for the 'else' substack + stack.append((current_indent, popped_parent_key, None)) # New chain for SUBSTACK2 + all_generated_blocks[popped_parent_key]["_parsing_substack2"] = True # Flag for SUBSTACK2 + i += 1 + continue + + if stripped_line.lower() == "end": + # Pop the current substack's scope + popped_indent, popped_parent_key, popped_last_key_in_chain = stack.pop() + if popped_last_key_in_chain: + all_generated_blocks[popped_last_key_in_chain]["next"] = None + + # Link the finished substack to its parent C-block's input + if popped_parent_key: + parent_block = all_generated_blocks[popped_parent_key] + if parent_block.get("_parsing_substack2"): # If we just finished the 'else' part + if parent_block.get("_first_substack2_block_key"): + parent_block["inputs"]["SUBSTACK2"] = [2, parent_block["_first_substack2_block_key"]] + else: + parent_block["inputs"]["SUBSTACK2"] = [2, None] + del parent_block["_parsing_substack2"] + if "_first_substack2_block_key" in parent_block: + del parent_block["_first_substack2_block_key"] + else: # Finished a regular substack (if, forever, repeat, or define) + if parent_block.get("_first_substack_block_key"): + parent_block["inputs"]["SUBSTACK"] = [2, parent_block["_first_substack_block_key"]] + else: + parent_block["inputs"]["SUBSTACK"] = [2, None] + if "_first_substack_block_key" in parent_block: + del parent_block["_first_substack_block_key"] + i += 1 + continue + + # Pop from stack if indentation decreases (for non-else/end lines) + while stack and stack[-1][0] >= current_indent: + # If the current line is a new hat block at the same level as a previous hat block, + # we need to ensure the previous hat block's chain is terminated. + if stack[-1][1] is None and current_indent == 0: # Top-level hat block context + popped_indent, popped_parent_key, popped_last_key_in_chain = stack.pop() + if popped_last_key_in_chain: + all_generated_blocks[popped_last_key_in_chain]["next"] = None + else: + break # Only pop if indentation actually decreases or it's a new top-level script + + # Get the current scope from the stack + current_scope_indent, current_parent_key, last_block_key_in_chain = stack[-1] + + # Classify the statement + stmt_for_parse = stripped_line.rstrip("then").strip() # Only strip 'then' here + opcode, ntype = classify(stmt_for_parse) + + if opcode is None: # Should not happen if classify is robust, but for safety + i += 1 + continue + + key = pick_key(opcode) + # Get a fresh copy of block definition from the global definitions + info = copy.deepcopy(all_block_definitions[opcode]) + + info["id"] = key # Add ID for easier debugging/tracking + info["topLevel"] = False # Default to false + info["next"] = None # Default next to None + info["parent"] = current_parent_key # Set parent based on current scope + + # Link 'next' for the previous block in the current chain + if last_block_key_in_chain: + all_generated_blocks[last_block_key_in_chain]["next"] = key + + # If this is the very first block in a new chain (substack or top-level script) + if ntype == "hat": # Top-level hat block + info["topLevel"] = True + top_level_script_keys.append(key) # Add to top-level flow + # For hat blocks, their direct children are the start of their script chain. + # We push a new scope for this. + stack.append((current_indent, key, None)) # New scope starts, current block is the parent + elif current_parent_key is not None: # Inside a substack (C-block or define block) + parent_block = all_generated_blocks[current_parent_key] + if parent_block.get("_parsing_substack2"): # If parsing the 'else' branch + if "_first_substack2_block_key" not in parent_block: + parent_block["_first_substack2_block_key"] = key + elif "_first_substack_block_key" not in parent_block: + parent_block["_first_substack_block_key"] = key + + # Parse inputs and fields + info["inputs"] = {} + info["fields"] = {} + + # --- Input Parsing Logic (adapted from original generate_plan) --- + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if opcode == "motion_movesteps": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["STEPS"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_turnright" or opcode == "motion_turnleft": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DEGREES"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_gotoxy": + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_glidesecstoxy": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt_for_parse, re.IGNORECASE) + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_secs: info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_pointindirection": + m = re.search(r"direction\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DIRECTION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_changexby", "motion_changeyby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DX" if opcode == "motion_changexby" else "DY"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_setx", "motion_sety"]: + m = re.search(r"(?:set x to|set y to)\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["X" if opcode == "motion_setx" else "Y"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_changesizeby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_setsizeto": + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SIZE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_changeeffectby", "sound_changeeffectby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE" if opcode == "looks_changeeffectby" else "VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE" if opcode == "looks_seteffectto" else "VOLUME"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["NUM"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "control_wait": + m = re.search(r"wait\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DURATION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "control_repeat": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["TIMES"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "data_changevariableby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "data_deleteoflist": + m = re.search(r"delete\s*\((.+?)\)\s*of", stmt_for_parse, re.IGNORECASE) + if m: + val_str = m.group(1).strip() + if val_str.isdigit(): + info["inputs"]["INDEX"] = {"kind": "value", "value": int(val_str)} + else: # "all", "last", "random" + info["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + elif opcode == "data_insertatlist": + m_item = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt_for_parse, re.IGNORECASE) + m_index = re.search(r"at\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + elif opcode == "data_replaceitemoflist": + m_index = re.search(r"replace item\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + m_item = re.search(r"with\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + elif opcode == "event_whengreaterthan": + m = re.search(r">\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + + # String inputs + elif opcode == "looks_sayforsecs": + m = re.search(r"say\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_say": + m = re.search(r"say\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = parse_reporter_or_value(m.group(1).strip(), pick_key) + elif opcode == "looks_thinkforsecs": + m = re.search(r"think\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_think": + m = re.search(r"think\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "sensing_askandwait": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["QUESTION"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["ITEM"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_setvariableto": + m_var = re.search(r"set\s*\[([^\]]+)\s*v\]\s*to\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m_var: + var_name = m_var.group(1).strip() + value_str = m_var.group(2).strip() + info["fields"]["VARIABLE"] = [var_name, None] + info["inputs"]["VALUE"] = parse_reporter_or_value(value_str, pick_key) + + # Dropdown/Menu inputs + elif opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_glideto": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m_secs: + info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + option = m_secs.group(2).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TOWARDS"] = {"kind": "menu", "option": option} + elif opcode == "sensing_keypressed": # For boolean block + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["KEY_OPTION"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "sensing_touchingobject": # For boolean block + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + elif option == "edge": option = "_edge_" + info["inputs"]["TOUCHINGOBJECTMENU"] = {"kind": "menu", "option": option} + elif opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "myself": option = "_myself_" + info["inputs"]["CLONE_OPTION"] = {"kind": "menu", "option": option} + elif opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SOUND_MENU"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["COSTUME"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait"]: + m = re.search(r"switch backdrop to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BACKDROP"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BROADCAST_INPUT"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "event_whenbroadcastreceived": + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BROADCAST_OPTION"] = {"kind": "menu", "option": m.group(1).strip()} + + # Conditional inputs (Boolean blocks) + elif opcode in ["control_if", "control_if_else", "control_wait_until", "control_repeat_until"]: + cond_match = re.search(r"<(.*?)>", stmt_for_parse) + if cond_match: + info["inputs"]["CONDITION"] = parse_condition(cond_match.group(1).strip(), pick_key) + elif opcode in ["operator_and", "operator_or", "operator_not", "operator_contains", + "sensing_touchingcolor", "sensing_coloristouchingcolor", "sensing_mousedown"]: + # These are handled by parse_condition directly, which returns the nested structure + # No need to re-parse inputs here, as they are part of the condition structure + pass # Inputs are set when parse_condition is called for the parent block + + + # Fields parsing + if "VARIABLE" in info["fields"]: + m = re.search(r"\[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["VARIABLE"] = [m.group(1), None] + if "LIST" in info["fields"]: + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["LIST"] = [m.group(1), None] + if "STOP_OPTION" in info["fields"]: + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STOP_OPTION"] = [m.group(1), None] + if "STYLE" in info["fields"]: + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STYLE"] = [m.group(1), None] + if "DRAG_MODE" in info["fields"]: + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["DRAG_MODE"] = [m.group(1), None] + if "EFFECT" in info["fields"] and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["EFFECT"] = [m.group(1).upper(), None] + if "NUMBER_NAME" in info["fields"] and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["NUMBER_NAME"] = [m.group(1), None] + if "FRONT_BACK" in info["fields"] and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FRONT_BACK"] = [m.group(1), None] + if "FORWARD_BACKWARD" in info["fields"] and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + if "OPERATOR" in info["fields"] and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["OPERATOR"] = [m.group(1).upper(), None] + if "CURRENTMENU" in info["fields"] and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + if "PROPERTY" in info["fields"] and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt_for_parse, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + info["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + if "WHENGREATERTHANMENU" in info["fields"] and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + if "KEY_OPTION" in info["fields"] and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["KEY_OPTION"] = [m.group(1), None] + if "BACKDROP" in info["fields"] and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BACKDROP"] = [m.group(1), None] + if "BROADCAST_OPTION" in info["fields"] and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + # Custom block specific parsing + if opcode == "procedures_definition": + proc_def_match = re.match(r"(?:define|procedure)\s+([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))?", stmt_for_parse, re.IGNORECASE) + if proc_def_match: + proc_name = proc_def_match.group(1).strip() + args_str = proc_def_match.group(2) + info["procedure_name"] = proc_name + info["is_custom_definition"] = True + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + info["inputs"]["PROCCONTAINER"] = { + "kind": "block_prototype", + "name": proc_name, + "arguments": [{"name": arg, "type": "any"} for arg in args] + } + # For a define block, its children form its body, so push a new scope + stack.append((current_indent, key, None)) + + elif opcode == "procedures_call": + call_match = re.match(r"(?:call\s+)?([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", stmt_for_parse, re.IGNORECASE) + if call_match: + custom_block_name = call_match.group(1).strip() + args_str = call_match.group(2) + info["custom_block_name"] = custom_block_name + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for idx, arg_val_str in enumerate(args): + info["inputs"][f"argument_name_{idx+1}"] = parse_reporter_or_value(arg_val_str, pick_key) + + # Add the newly created block to the global collection + all_generated_blocks[key] = info + + # If it's a C-block, push a new scope for its substack + if ntype == "c_block": + stack.append((current_indent, key, None)) # New scope for substack + + # Update the last block key in the current chain (for the parent scope) + # This needs to be done *after* pushing a new scope for C-blocks/Hat blocks + # so that the new block becomes the parent of the *next* blocks in the new scope. + # For the current scope, the new block is the last one in its chain. + if stack: # Ensure stack is not empty (shouldn't be if logic is correct) + # Find the top-most active scope that this block belongs to + # This is tricky because C-blocks/Hat blocks start new scopes. + # The `last_block_key_in_chain` on the stack should always refer to the *previous* block + # in the *current* chain being built. + + # Re-evaluate how `last_block_key_in_chain` is updated. + # When a block is added, it becomes the `last_block_key_in_chain` for its *parent's* scope. + # If it starts a new scope (C-block, Hat, Define), then for *that new scope*, `last_block_key_in_chain` is None. + + # Correct update of stack[-1] after adding `info` to `all_generated_blocks`: + # The current block `key` is now the `last_block_key_in_chain` for its *parent's* scope. + # The parent's scope is `stack[-1]` *before* pushing a new scope for C-blocks/Hat blocks. + # So, we update the `last_block_key_in_chain` of the current active scope. + + # This needs to be done carefully to avoid modifying the just-pushed scope. + # Let's use a temporary variable for the current scope's last key. + + # The stack top should always represent the *current* chain being built. + # When a new block is created, it extends the chain of the current stack top. + + # If a new scope was pushed (C-block, Hat, Define): + # The block `key` is the parent of the new scope. + # The `last_block_key_in_chain` for the *parent's* scope (the one *before* the push) should be updated. + # This means the `stack[-2]` (if it exists) needs to be updated. + + # If no new scope was pushed (Stack, Cap, Reporter): + # The block `key` is the new `last_block_key_in_chain` for the current scope (`stack[-1]`). + + # This is the most complex part of the line-by-line parser. + # Let's simplify the stack update logic: + # The current block `key` becomes the `last_block_key_in_chain` for the scope it was added to. + stack[-1] = (stack[-1][0], stack[-1][1], key) # Update the last_block_key_in_chain for the current scope + + i += 1 # Move to the next line + + # Final pass to ensure all 'next' pointers for the last blocks in any chain are None + # and to link the substacks for C-blocks that were not closed by an 'end' keyword (e.g., end of file) + while stack: + popped_indent, popped_parent_key, popped_last_key_in_chain = stack.pop() + if popped_last_key_in_chain: + all_generated_blocks[popped_last_key_in_chain]["next"] = None + + if popped_parent_key: + parent_block = all_generated_blocks[popped_parent_key] + if parent_block.get("_parsing_substack2"): + if parent_block.get("_first_substack2_block_key"): + parent_block["inputs"]["SUBSTACK2"] = [2, parent_block["_first_substack2_block_key"]] + else: + parent_block["inputs"]["SUBSTACK2"] = [2, None] + del parent_block["_parsing_substack2"] + if "_first_substack2_block_key" in parent_block: + del parent_block["_first_substack2_block_key"] + else: # Regular substack or define block body + if parent_block.get("_first_substack_block_key"): + parent_block["inputs"]["SUBSTACK"] = [2, parent_block["_first_substack_block_key"]] + else: + parent_block["inputs"]["SUBSTACK"] = [2, None] + if "_first_substack_block_key" in parent_block: + del parent_block["_first_substack_block_key"] + + # Construct the final flow output based on the collected top-level keys + final_flow_output = [] + + # Recursively build the block structure for each top-level script + def build_script_flow(current_block_key): + script_flow = [] + current_iter_key = current_block_key + while current_iter_key: + block = all_generated_blocks.get(current_iter_key) + if not block: + break # Should not happen if keys are correct + + # Create a simplified block representation for the output + output_block = { + "block_key": block["id"], + "type": block["block_shape"].replace(" Block", "").lower().replace("c-", "c_"), # Normalize type + "inputs": {}, + "fields": {} + } + + # Copy inputs and fields, handling nested substacks for C-blocks + for inp_name, inp_val in block["inputs"].items(): + if inp_name in ["SUBSTACK", "SUBSTACK2"]: + if inp_val and inp_val[1]: # If substack is linked to a block key + output_block["inputs"][inp_name] = build_script_flow(inp_val[1]) # Recursively build substack + else: + output_block["inputs"][inp_name] = [] # Empty substack + elif inp_name == "PROCCONTAINER" and block.get("is_custom_definition"): + # For procedures_definition, PROCCONTAINER holds the prototype info + output_block["inputs"][inp_name] = inp_val + else: + output_block["inputs"][inp_name] = inp_val + + for field_name, field_val in block["fields"].items(): + output_block["fields"][field_name] = field_val + + # Add custom block name if it's a call + if block.get("custom_block_name"): + output_block["custom_block_name"] = block["custom_block_name"] + + # Add procedure_name for definition block + if block.get("procedure_name"): + output_block["procedure_name"] = block["procedure_name"] + output_block["is_custom_definition"] = True + + script_flow.append(output_block) + current_iter_key = block.get("next") + return script_flow + + for key in top_level_script_keys: + final_flow_output.extend(build_script_flow(key)) + + return {"flow": final_flow_output} + +# Example input with opcodes for the initial generation +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_glidesecstoxy","count":1}, # Corrected count for this example + {"opcode":"motion_xposition","count":1}, # Used multiple times in conditions + {"opcode":"motion_setx","count":1}, # Added count for motion_setx + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":1}, # Corrected count for this example + {"opcode":"control_stop","count":1}, # Corrected count for this example + {"opcode":"operator_lt","count":1}, # Used in condition + {"opcode":"sensing_touchingobject","count":1}, # Used in condition, and for if on edge, bounce + {"opcode":"sensing_touchingobjectmenu","count":1}, # This will now be generated as a child of sensing_touchingobject + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":2}, + {"opcode":"data_showvariable","count":2}, +] + +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + +# Example pseudo-code inputs from the JSON files +pseudo_code_examples = [""" +when green flag clicked + go to x: (240) y: (-135) + set [score v] to (1) + set [speed v] to (1) + show variable [score v] + show variable [speed v] + forever + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end +end +""",] + +# Process each example and print the plan +for i, pseudo_code_input in enumerate(pseudo_code_examples): + print(f"\n--- Processing Example {i+1} ---") + print(pseudo_code_input.strip()) + try: + # Regenerate blocks and opcode_occurrences for each run to ensure fresh keys + # This is important because pick_key uses a defaultdict that persists state. + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) + print(json.dumps(plan, indent=2)) + except Exception as e: + print(f"Error processing example: {e}") + import traceback + traceback.print_exc() diff --git a/utils/helper_function.py b/utils/helper_function.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/utils/logs.txt b/utils/logs.txt new file mode 100644 index 0000000000000000000000000000000000000000..23c44b9ddfcbaf64ed8b73baebcd5a3d31417894 --- /dev/null +++ b/utils/logs.txt @@ -0,0 +1,5 @@ +the plan_generator_6 is working well but sometime losses the boolean parent and reportor booleans +the plan genratr_7 works well then the 6 version. +the plan generator_8 works well for parent and child logics. +the plan generator_9 has better but lacks menu features. +the plan generator_10 worke better for parent and as well as menu features. diff --git a/utils/opcode_counter.py b/utils/opcode_counter.py new file mode 100644 index 0000000000000000000000000000000000000000..049e2ffc18b0fc533ff1391cfaacce4e2803a513 --- /dev/null +++ b/utils/opcode_counter.py @@ -0,0 +1,228 @@ +import json +import re +from typing import Any, Dict +import logging + +logger = logging.getLogger(__name__) + +# Dummy data for demonstration. You should replace this with your actual opcode data. +# Each item should have at least an 'opcode' and 'text' field. +hat_block_data = [ + {"opcode": "event_whenflagclicked", "text": "when green flag clicked"}, + {"opcode": "event_whenkeypressed", "text": "when [key] pressed"}, + {"opcode": "event_whenbroadcastreceived", "text": "when I receive [message]"}, +] +boolean_block_data = [ + {"opcode": "operator_gt", "text": "< ( ) > ( ) >"}, + {"opcode": "sensing_touchingobject", "text": ""}, + {"opcode": "operator_equals", "text": "< ( ) = ( ) >"}, +] +c_block_data = [ + {"opcode": "control_forever", "text": "forever"}, + {"opcode": "control_if", "text": "if < > then"}, + {"opcode": "control_repeat", "text": "repeat ( )"}, +] +cap_block_data = [ + {"opcode": "control_stop", "text": "stop [all]"}, +] +reporter_block_data = [ + {"opcode": "motion_xposition", "text": "(x position)"}, + {"opcode": "motion_yposition", "text": "(y position)"}, + {"opcode": "data_variable", "text": "(variable)"}, + {"opcode": "sensing_answer", "text": "(answer)"}, +] +stack_block_data = [ + {"opcode": "motion_gotoxy", "text": "go to x: ( ) y: ( )"}, + {"opcode": "motion_changeyby", "text": "change y by ( )"}, + {"opcode": "motion_setx", "text": "set x to ( )"}, + {"opcode": "motion_glidesecstoxy", "text": "glide ( ) secs to x: ( ) y: ( )"}, + {"opcode": "data_setvariableto", "text": "set [variable] to ( )"}, + {"opcode": "looks_hide", "text": "hide"}, + {"opcode": "looks_show", "text": "show"}, + {"opcode": "event_broadcast", "text": "broadcast [message]"}, +] + +# Combine all block data into a single list for easier lookup +all_opcodes_list = [] +for category_data in [ + hat_block_data, + boolean_block_data, + c_block_data, + cap_block_data, + reporter_block_data, + stack_block_data, +]: + all_opcodes_list.extend(category_data) + + +def extract_json_from_llm_response(response_text: str) -> Dict[str, Any]: + """Extracts JSON from an LLM response string.""" + try: + json_match = re.search(r"```json\n(.*)\n```", response_text, re.DOTALL) + if json_match: + return json.loads(json_match.group(1)) + return json.loads(response_text) # Try parsing directly if no code block + except json.JSONDecodeError as e: + logger.error(f"Failed to decode JSON: {e} from response: {response_text}") + raise + +# Node 9:plan with exact count of the opcode used per logic +def plan_opcode_counter_node(state: Dict[str, Any]) -> Dict[str, Any]: + """ + For each plan in state["action_plan"]["action_overall_flow"], calls the LLM agent + to analyze the `logic` string and return a list of {opcode, count} for each category. + """ + logger.info("=== Running OPCODE COUTER LOGIC with LLM counts ===") + game_description = state.get("description", "No game description provided.") + sprite_name = {target["name"]: target["name"] for target in state["project_json"]["targets"]} # Adjusted for direct use + + action_flow = state.get("action_plan", {}).get("action_overall_flow", {}) + if not action_flow: + logger.warning("No action_overall_flow found; skipping.") + return state + + # Prepare block reference strings for the prompt + hat_description = "Blocks that start a script when an event happens." + hat_opcodes_functionalities = "\n".join([f"- {block['opcode']}: {block['text']}" for block in hat_block_data]) + + boolean_description = "Blocks that report a true or false value and fit into hexagonal inputs." + boolean_opcodes_functionalities = "\n".join([f"- {block['opcode']}: {block['text']}" for block in boolean_block_data]) + + c_description = "Blocks that run scripts inside them repeatedly or conditionally." + c_opcodes_functionalities = "\n".join([f"- {block['opcode']}: {block['text']}" for block in c_block_data]) + + cap_description = "Blocks that end a script." + cap_opcodes_functionalities = "\n".join([f"- {block['opcode']}: {block['text']}" for block in cap_block_data]) + + reporter_description = "Blocks that report a value (number or string) and fit into rounded inputs." + reporter_opcodes_functionalities = "\n".join([f"- {block['opcode']}: {block['text']}" for block in reporter_block_data]) + + stack_description = "Blocks that perform a main action in a script." + stack_opcodes_functionalities = "\n".join([f"- {block['opcode']}: {block['text']}" for block in stack_block_data]) + + refined_flow: Dict[str, Any] = {} + for sprite, sprite_data in action_flow.items(): # Use .items() for direct iteration + refined_plans = [] + for plan in sprite_data.get("plans", []): + logic = plan.get("logic", "") + event = plan.get("event", "") + + # This is where the core change for counting opcodes will happen. + # We will use the 'logic' string to determine the actual opcodes and their counts. + opcode_counts = { + "motion": [], + "control": [], + "operator": [], + "sensing": [], + "looks": [], + "sounds": [], + "events": [], + "data": [], + } + + # Initialize a dictionary to hold counts for each opcode + temp_opcode_counts = {} + + # Add the event block explicitly + if event: + event_opcode = event.replace('v', '').strip() # Clean the event string + temp_opcode_counts[event_opcode] = temp_opcode_counts.get(event_opcode, 0) + 1 + + + # Iterate through all known opcodes and check if their 'text' appears in the logic + for block_info in all_opcodes_list: + opcode = block_info["opcode"] + # Use a more robust regex for matching, accounting for variable names or block inputs + # We need to be careful with common words that are also part of opcodes, e.g., "if" + # A more robust solution might involve parsing the Scratch-like logic more deeply. + # For now, let's try to match the "text" from the block definition. + # Escape special characters in the block text for regex + block_text_escaped = re.escape(block_info["text"]) + + # Replace placeholders like [key], [object], ( ) with regex wildcards + block_text_pattern = block_text_escaped.replace(r"\[key\]", r".*?").replace(r"\[message\]", r".*?").replace(r"\[object\]", r".*?").replace(r"\( \)", r".*?") + block_text_pattern = block_text_pattern.replace(r"\[variable\]", r".*?") + + # For blocks that might have variations in text (e.g., if-then, if-then-else) + if opcode == "control_if": + if_regex = r"if <.+?> then" + if_else_regex = r"if <.+?> then\n.*else" + + if re.search(if_else_regex, logic, re.DOTALL): + temp_opcode_counts["control_if_else"] = temp_opcode_counts.get("control_if_else", 0) + 1 + elif re.search(if_regex, logic, re.DOTALL): + temp_opcode_counts["control_if"] = temp_opcode_counts.get("control_if", 0) + 1 + continue # Skip general matching for control_if + + if opcode == "control_forever" and "forever" in logic: + temp_opcode_counts[opcode] = temp_opcode_counts.get(opcode, 0) + 1 + continue # Skip general matching + + # General regex match for other blocks + # We need to make sure we're not just matching substrings of other blocks + # A simple word boundary or line-by-line check might be better + # For now, a simple count of occurrences of the "text" within the logic + # will be used, but this is a simplification. + count = len(re.findall(block_text_pattern, logic)) + if count > 0: + temp_opcode_counts[opcode] = temp_opcode_counts.get(opcode, 0) + count + + # Fill the opcode_counts for each category based on temp_opcode_counts + def add_to_category(category_list, opcode_name, count): + if count > 0: + category_list.append({"opcode": opcode_name, "count": count}) + + for opcode, count in temp_opcode_counts.items(): + if opcode.startswith("motion_"): + add_to_category(opcode_counts["motion"], opcode, count) + elif opcode.startswith("control_"): + add_to_category(opcode_counts["control"], opcode, count) + elif opcode.startswith("operator_"): + add_to_category(opcode_counts["operator"], opcode, count) + elif opcode.startswith("sensing_"): + add_to_category(opcode_counts["sensing"], opcode, count) + elif opcode.startswith("looks_"): + add_to_category(opcode_counts["looks"], opcode, count) + elif opcode.startswith("sounds_"): + add_to_category(opcode_counts["sounds"], opcode, count) + elif opcode.startswith("event_"): + add_to_category(opcode_counts["events"], opcode, count) + elif opcode.startswith("data_"): + add_to_category(opcode_counts["data"], opcode, count) + + # Assign the new opcode_counts to the plan + plan["opcode_counts"] = opcode_counts + + # The original plan structure also had categories as direct keys. + # You can choose to keep this or remove it, depending on your downstream needs. + # If you want to keep it, you'd populate them based on opcode_counts. + # For simplicity, let's keep the new 'opcode_counts' key as requested. + + # Clear previous lists if you are relying solely on 'opcode_counts' + plan["motion"] = [] + plan["control"] = [] + plan["operator"] = [] + plan["sensing"] = [] + plan["looks"] = [] + plan["sounds"] = [] + plan["events"] = [] + plan["data"] = [] + + # Populate the individual lists based on the newly calculated opcode_counts if needed + for category, opcodes_list in opcode_counts.items(): + for item in opcodes_list: + # Append just the opcode string to the category list + plan[category].extend([item['opcode']] * item['count']) + + + refined_plans.append(plan) + + refined_flow[sprite] = { + "description": sprite_data.get("description", ""), + "plans": refined_plans + } + + state["temporary_node"] = refined_flow + print(f"[OPCODE COUTER LOGIC]: {refined_flow}") + logger.info("=== OPCODE COUTER LOGIC completed ===") + return state \ No newline at end of file diff --git a/utils/opcode_occurrence.py b/utils/opcode_occurrence.py new file mode 100644 index 0000000000000000000000000000000000000000..a496fb9b8eacbbae53ab4747d085db23042f3377 --- /dev/null +++ b/utils/opcode_occurrence.py @@ -0,0 +1,981 @@ +import json +import copy +import re +import re +from collections import defaultdict +#from opcode_occurrence import generated_output_json + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False + + generated_blocks[main_key] = main_block_data + + # Handle menus + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_key + + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 1 and \ + main_block_data["inputs"][input_name][0] == 1: + + main_block_data["inputs"][input_name][1] = menu_key + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, +} + +# # Example input with opcodes for the initial generation +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_glidesecstoxy","count":1}, + {"opcode":"motion_xposition","count":1}, + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":1}, + {"opcode":"control_stop","count":1}, + {"opcode":"operator_lt","count":1}, + {"opcode":"sensing_touchingobject","count":1}, # Changed from sensing_istouching + {"opcode":"sensing_touchingobjectmenu","count":1}, + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":2}, + {"opcode":"data_showvariable","count":2}, +] + +# initial_opcode_counts = [ +# {"opcode": "sound_play", "count": 2}, +# {"opcode": "sound_playuntildone", "count": 2}, +# {"opcode":"motion_goto","count":2}, +# {"opcode":"motion_glideto","count":2}, +# {"opcode":"looks_switchbackdropto","count":2}, +# {"opcode":"looks_switchcostumeto","count":2}, +# {"opcode":"control_create_clone_of","count":2}, +# {"opcode":"sensing_touchingobject","count":2}, +# {"opcode":"sensing_of","count":2}, +# {"opcode":"sensing_keypressed","count":2}, +# {"opcode":"motion_pointtowards","count":2}, +# ] + +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) +#print(generated_output_json) +#print(initial_opcode_occurrences) + + +def build_block_patterns(def_list): + """ + Given a list of block definitions (from one JSON file), + return a dict opcode -> { regex:Compiled, inputs:[names], shape, block_name }. + """ + out = {} + for d in def_list: + # 1) escape the literal text + pattern = re.escape(d['block_name']) + # 2) replace the escaped "()" placeholders with a capture for anything inside + pattern = pattern.replace(r"\(\)", r"\((.+?)\)") + # 3) replace the escaped "<>" placeholders likewise + pattern = pattern.replace(r"\<\>", r"<(.+?)>") + # 4) allow any whitespace where spaces occur + pattern = pattern.replace(r"\ ", r"\s+") + # anchor from start to end, ignore case + regex = re.compile(r"^\s*" + pattern + r"\s*$", re.IGNORECASE) + + inputs = [inp['name'] for inp in (d.get('inputs') or [])] + out[d['op_code']] = { + 'regex': regex, + 'inputs': inputs, + 'shape': d['block_shape'], + 'block_name':d['block_name'], + 'definition':d + } + return out + +def load_all_definitions(): + # Load your JSON files here; adjust paths as needed + hats = json.load(open(r'blocks\hat_blocks.json'))['blocks'] + c_blocks = json.load(open(r'blocks\c_blocks.json'))['blocks'] + reporters = json.load(open(r'blocks\reporter_blocks.json'))['blocks'] + booleans = json.load(open(r'blocks\boolean_blocks.json'))['blocks'] + # you can also load stack_blocks.json, cap_blocks.json, etc. if you have them + merged = hats + c_blocks + reporters + booleans + return build_block_patterns(merged) + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + A truly generic plan generator. + Inputs: + - generated_input: dict of block_key -> block_data + - opcode_keys: dict of opcode -> [block_key,...] + - pseudo_code: multiline string + Returns: { flow: [...] } + """ + all_defs = load_all_definitions() + # pointer into each opcode_keys list + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + if ptrs[opcode] >= len(lst): + raise KeyError(f"No more generated keys for opcode {opcode!r}") + key = lst[ptrs[opcode]] + ptrs[opcode] += 1 + return key + + # Recursively parse an expression fragment like "(x position)" or "" + def parse_expression(expr_text): + expr_text = expr_text.strip() + # Try to match every reporter/boolean pattern + for op, info in all_defs.items(): + m = info['regex'].match(expr_text) + if not m: continue + # Got a match + node = {'op_code': op, 'inputs': {}} + groups = m.groups() + for name, val in zip(info['inputs'], groups): + # decide kind by input type in definition + inp_def = next( + (i for i in info['definition'].get('inputs', []) if i['name']==name), + {} + ) + t = inp_def.get('type','any') + if t in ('number','any'): + try: + node['inputs'][name] = {'kind':'value', 'value': float(val) if '.' in val else int(val)} + except: + node['inputs'][name] = {'kind':'variable', 'name': val.strip()} + elif t in ('dropdown','string','string/number'): + node['inputs'][name] = {'kind':'menu', 'option': val.strip()} + elif t=='boolean': + # nested boolean: recurse + node['inputs'][name] = {'kind':'nested', 'expr': parse_expression(val)} + else: + node['inputs'][name] = {'kind':'nested', 'expr': parse_expression(val)} + return node + # fallback: literal? + lit = re.match(r"^\(?\s*(-?\d+(\.\d+)?)\s*\)?$", expr_text) + if lit: + num = lit.group(1) + return {'literal': True, 'kind':'value', 'value': float(num) if '.' in num else int(num)} + # else treat as raw string + return {'literal':True, 'kind':'variable', 'name':expr_text} + + flow = [] + # stack of (indent, container) + stack = [(-1, flow)] + + for raw in pseudo_code.splitlines(): + if not raw.strip(): continue + indent = (len(raw) - len(raw.lstrip())) // 2 + line = raw.strip() + # drop trailing 'then' or 'end' + line_clean = re.sub(r'\s*(then|end)\s*$', '', line, flags=re.IGNORECASE) + + # find a matching block definition + for opcode, info in all_defs.items(): + if not info['regex'].match(line_clean): + continue + # pop up to correct level + while stack and stack[-1][0] >= indent: + stack.pop() + container = stack[-1][1] + + key = pick_key(opcode) + node = {'block_key': key} + shape = info['shape'] + # determine node type + if 'Hat Block' in shape: + node['type'] = 'hat' + node['description'] = info['block_name'] + node['next'] = [] + target_list = node['next'] + elif 'C-Block' in shape: + node['type'] = 'c_block' + node['description'] = info['block_name'] + node['condition'] = None + node['body'] = [] + target_list = node['body'] + # the first input is usually the boolean condition + if info['inputs']: + cond_name = info['inputs'][0] + # extract the <...> portion + cond_match = re.search(r'<(.+)>', line) + if cond_match: + node['condition'] = parse_expression(cond_match.group(1)) + elif 'Cap Block' in shape: + node['type'] = 'cap' + # e.g. stop [all v] + if 'inputs' in info['definition'] and info['definition']['inputs']: + fld = info['definition']['inputs'][0]['name'] + val = re.search(r'\[\s*([^\]]+)\s*\]', line) + if val: + node['option'] = val.group(1) + target_list = None + else: + node['type'] = 'stack' + # parse any inputs in parentheses or brackets + node['inputs'] = {} + # for each input name in the definition, find its occurrence + for inp_name in info['inputs']: + # look for "(...)" after the input name or anywhere + # brute-force: find all "(...)" then map in order + pass + target_list = None + + container.append(node) + if target_list is not None: + stack.append((indent, target_list)) + break + else: + # no matching opcode — you can log or raise here + raise ValueError(f"Could not classify line: {line!r}") + + return {'flow': flow} + +pseudo_code_input = """ +when green flag clicked + go to x: (240) y: (-135) + set [score v] to (1) + set [speed v] to (1) + show variable [score v] + show variable [speed v] + forever + glide (2) seconds to x: (-240) y: (-135) + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end +end +""" + +# ── USAGE ── +plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) +import json +print(json.dumps(plan, indent=2)) \ No newline at end of file diff --git a/utils/plan_generator.py b/utils/plan_generator.py new file mode 100644 index 0000000000000000000000000000000000000000..7e60c1c0a2f19729d604272dbc206e54f37f21e6 --- /dev/null +++ b/utils/plan_generator.py @@ -0,0 +1,1838 @@ +import json +import copy +import re +from collections import defaultdict + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False + + generated_blocks[main_key] = main_block_data + + # Handle menus + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_key + + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 1 and \ + main_block_data["inputs"][input_name][0] == 1: + + main_block_data["inputs"][input_name][1] = menu_key + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +# Consolidated block definitions from all JSON files +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_distanceto": { # Added sensing_distanceto + "block_name": "(distance to ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto", + "functionality": "Reports the distance from the sprite to the mouse-pointer or another specified sprite.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": False, "topLevel": True + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": [ + { + "name": "PROCCONTAINER", + "type": "block_prototype" + } + ], + "fields": {}, + "shadow": False, + "topLevel": True + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": [], # Inputs are dynamic based on definition + "fields": {}, + "shadow": False, + "topLevel": True + } +} + +def unparen(s): + s = s.strip() + m = re.fullmatch(r"\((.*)\)", s) + return m.group(1) if m else s + +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, pick_key_func): + text = text.strip() + + # Check for numeric literal (including parenthesized numbers like "(0)" or "(10)") + m_num = re.fullmatch(r"\(?\s*(-?\d+(\.\d+)?)\s*\)?", text) + if m_num: + val_str = m_num.group(1) + return {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # Check for string literal (e.g., "[Hello!]") + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + # Check for simple reporters, potentially with outer parentheses + m_simple_reporter = re.fullmatch(r"\((.+?)\)", text) + if m_simple_reporter: + inner_text = m_simple_reporter.group(1).strip() + if inner_text in simple_reporters: + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func(simple_reporters[inner_text]), "inputs": {}}} + # Also check for simple reporters without parentheses (e.g., if passed directly) + if text in simple_reporters: + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func(simple_reporters[text]), "inputs": {}}} + + + # Variable reporter: [score v] or (score) or just "score" + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_variable"), "inputs": {}, "fields": {"VARIABLE": [var_name, None]}}} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + # Ensure it's not a simple reporter already handled, or a number + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_variable"), "inputs": {}, "fields": {"VARIABLE": [potential_var_name, None]}}} + # Handle plain variable names like "score", "number 1", "total score" + if re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_ ]*", text): # Allow spaces for "number 1" etc. + # Exclude known simple reporters that don't have 'v' or parentheses + if text not in simple_reporters: + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_variable"), "inputs": {}, "fields": {"VARIABLE": [text, None]}}} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_list"), "inputs": {}, "fields": {"LIST": [list_name, None]}}} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + max_val = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_random"), "inputs": {"FROM": min_val, "TO": max_val}}} + + # (join ()()) (operator_join) - handle both [] and () for inputs + m = re.search(r"join \((.+?)\) \((.+?)\)", text) # Try (val) (val) + if not m: + m = re.search(r"join \[(.+?)\] \[(.+?)\]", text) # Try [val] [val] + if m: + str1 = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + str2 = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_join"), "inputs": {"STRING1": str1, "STRING2": str2}}} + + # letter () of () (operator_letterof) - handle both [] and () for inputs + m = re.search(r"letter \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + string_val = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_letterof"), "inputs": {"LETTER": index, "STRING": string_val}}} + + # (length of ()) (operator_length) - handle both [] and () for inputs + m = re.search(r"length of \((.+?)\)", text) + if not m: + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + list_or_string_val = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_length"), "inputs": {"STRING": list_or_string_val}}} + + + # (() mod ()) (operator_mod) + m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + if m: + num1 = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + num2 = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_mod"), "inputs": {"NUM1": num1, "NUM2": num2}}} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_round"), "inputs": {"NUM": num}}} + + # (() of ()) (operator_mathop) - handle variable for function type + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) # e.g. [sqrt v] of ((x pos) * (x pos)) + if m: + func_type = m.group(1).strip() + value = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_mathop"), "inputs": {"NUM": value}, "fields": {"OPERATOR": [func_type.upper(), None]}}} + # Also handle direct string for function type (e.g., "abs of (x)") + m = re.search(r"([a-zA-Z]+)\s*of\s*\((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_mathop"), "inputs": {"NUM": value}, "fields": {"OPERATOR": [func_type.upper(), None]}}} + + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + # This regex is designed to handle nested parentheses correctly. + # It looks for an opening parenthesis, then non-parenthesis characters or balanced parentheses, + # followed by an operator, and then the second operand. + # This is a simplified approach; a full-fledged parser would use a stack. + arithmetic_match = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + if not arithmetic_match: + # Try to match without outer parentheses for the operands, but still with an operator + arithmetic_match = re.search(r"(.+?)\s*([+\-*/])\s*(.+)", text) + + if arithmetic_match: + op1_str = arithmetic_match.group(1).strip() + operator_symbol = arithmetic_match.group(2).strip() + op2_str = arithmetic_match.group(3).strip() + + op1 = parse_reporter_or_value(op1_str, pick_key_func) + op2 = parse_reporter_or_value(op2_str, pick_key_func) + + opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func(opcode_map[operator_symbol]), "inputs": {"NUM1": op1, "NUM2": op2}}} + + + # (costume ()) (looks_costumenumbername) - handle with or without 'v' + m = re.search(r"costume \((.+?)\)", text) + if not m: + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("looks_costumenumbername"), "inputs": {}, "fields": {"NUMBER_NAME": [option, None]}}} + + # (backdrop ()) (looks_backdropnumbername) - handle with or without 'v' + m = re.search(r"backdrop \((.+?)\)", text) + if not m: + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("looks_backdropnumbername"), "inputs": {}, "fields": {"NUMBER_NAME": [option, None]}}} + + # (distance to ()) (sensing_distanceto) - handle with or without 'v' + m = re.search(r"distance to \((.+?)\)", text) + if not m: + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("sensing_distanceto"), "inputs": {}, "fields": {"TARGET": [target_val, None]}}} + + # (current ()) (sensing_current) - handle with or without 'v' + m = re.search(r"current \((.+?)\)", text) + if not m: + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("sensing_current"), "inputs": {}, "fields": {"CURRENTMENU": [unit.upper(), None]}}} + + # (() of ()) (sensing_of) - handle both variable and non-variable properties, and objects + # Updated regex to correctly capture the property and object, including nested reporters for property + m = re.search(r"\((.+?)\)\s*of\s*\((.+?)\)", text) # (prop) of (obj) + if not m: + m = re.search(r"\((.+?)\)\s*of\s*\[([^\]]+)\s*v\]", text) # (prop) of [obj v] + if m: + prop_str = m.group(1).strip() + obj = m.group(2).strip() + + # Map common property names to their internal Scratch representation + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + property_value = prop_map.get(prop_str, prop_str) # Use mapped value or original string + + # The object can be a sprite name or "_stage_" + obj_kind = "menu" + if obj.lower() == "stage": obj_val = "_stage_" + elif obj.lower() == "myself": obj_val = "_myself_" + else: obj_val = obj # Assume it's a sprite name + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("sensing_of"), "inputs": {"OBJECT": {"kind": obj_kind, "option": obj_val}}, "fields": {"PROPERTY": [property_value, None]}}} + + # (item (index) of [list v]) (data_itemoflist) - handle with or without 'v' and parentheses for index + m = re.search(r"item \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + list_name = m.group(2).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_itemoflist"), "inputs": {"INDEX": index}, "fields": {"LIST": [list_name, None]}}} + + # (item # of [item] in [list v]) (data_itemnumoflist) - handle with or without 'v' and parentheses for item + m = re.search(r"item # of \((.+?)\) in \((.+?)\)", text) + if not m: + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + list_name = m.group(2).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_itemnumoflist"), "inputs": {"ITEM": item}, "fields": {"LIST": [list_name, None]}}} + + + raise ValueError(f"Can't parse reporter or value: {text}") + +def parse_condition(stmt, pick_key_func): + """ + Parse Scratch-style boolean conditions, handling comparisons (<, =, >), + boolean operators (and, or, not), and other sensing conditions. + """ + s = stmt.lower().strip() + + # 1a) Comparisons with explicit angle wrappers: < (...) op (...) > + m = re.fullmatch( + r"\s*<\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*>\s*", + s, + re.VERBOSE + ) + if m: + left_txt, right_txt = m.group(1), m.group(3) + operand1 = parse_reporter_or_value(unparen(left_txt), pick_key_func) + operand2 = parse_reporter_or_value(unparen(right_txt), pick_key_func) + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + return { + "block": pick_key_func(op_map[m.group('op')]), + "inputs": {"OPERAND1": operand1, "OPERAND2": operand2} + } + + # 1b) Simple comparisons without angle wrappers: A op B + m_simple = re.fullmatch(r"\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*", s) + if m_simple: + left_txt, right_txt = m_simple.group(1), m_simple.group(3) + operand1 = parse_reporter_or_value(unparen(left_txt), pick_key_func) + operand2 = parse_reporter_or_value(unparen(right_txt), pick_key_func) + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + return { + "block": pick_key_func(op_map[m_simple.group('op')]), + "inputs": {"OPERAND1": operand1, "OPERAND2": operand2} + } + + # 2) Boolean AND / OR + m = re.fullmatch(r"\s*<\s*(.+?)\s+(and|or)\s+(.+?)\s*>\s*", s) + if m: + cond1 = parse_condition(m.group(1), pick_key_func) + cond2 = parse_condition(m.group(3), pick_key_func) + op_block = 'operator_and' if m.group(2) == 'and' else 'operator_or' + return { + "block": pick_key_func(op_block), + "inputs": {"OPERAND1": cond1, "OPERAND2": cond2} + } + + # 3) Boolean NOT + m = re.fullmatch(r"\s*<\s*not\s+(.+?)\s*>\s*", s) + if m: + inner = parse_condition(m.group(1), pick_key_func) + return { + "block": pick_key_func("operator_not"), + "inputs": {"OPERAND": inner} + } + + # 4) Contains: <[list v] contains [item]?> + m = re.search(r"\[([^\]]+)\s*v\] contains \[(.+?)\]\?", s) + if m: + from_val = {"kind": "list_variable", "name": m.group(1).strip()} + item_val = {"kind": "value", "value": m.group(2).strip()} + return { + "block": pick_key_func("data_listcontainsitem"), + "inputs": {"LIST": from_val, "ITEM": item_val} + } + + # 5) Touching object: + m = re.search(r"touching \[([^\]]+)\s*v\]\?", s) + if m: + opt = m.group(1).strip() + opt_val = {'mouse-pointer':'_mouse_','edge':'_edge_'}.get(opt, opt) + return { + "block": pick_key_func("sensing_touchingobject"), + "inputs": {"TOUCHINGOBJECTMENU": {"kind": "menu", "option": opt_val}} + } + + # 6) Touching color: + m = re.search(r"touching color \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + return { + "block": pick_key_func("sensing_touchingcolor"), + "inputs": {"COLOR": {"kind": "color", "value": m.group(1)}} + } + + # 7) Color is touching color: + m = re.search(r"color \[(#[0-9A-Fa-f]{6})\] is touching \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + return { + "block": pick_key_func("sensing_coloristouchingcolor"), + "inputs": {"COLOR1": {"kind": "color", "value": m.group(1)}, + "COLOR2": {"kind": "color", "value": m.group(2)}} + } + + # 8) Key pressed: + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", s) + if m: + return { + "block": pick_key_func("sensing_keypressed"), + "inputs": {"KEY_OPTION": {"kind": "menu", "option": m.group(1).strip()}} + } + + # 9) Mouse down?: mouse down? + if s == "mouse down?": + return {"block": pick_key_func("sensing_mousedown"), "inputs": {}} + + raise ValueError(f"Can't parse condition: {stmt}") + +def classify(line): + """ + Classifies a pseudo-code line into its corresponding Scratch opcode and block type. + Order of checks matters: more specific patterns should come before more general ones. + """ + l = line.lower().strip() + + # Ignore comments + if l.startswith("//"): return None, None + + # Hat Blocks (most specific first) + if re.match(r"when green flag click(ed)?", l): return "event_whenflagclicked", "hat" + if re.match(r"when (.+?) key press(ed)?", l): return "event_whenkeypressed", "hat" + if re.match(r"when this sprite click(ed)?", l): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if re.match(r"when i start as a clo(ne)?", l): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + if l.startswith("procedure "): return "procedures_definition", "hat" # For "procedure moveBall" + + # Motion Blocks + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + # IMPORTANT: More specific glide block before less specific one + if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if re.match(r"set x to\s*\(.+\)", l): return "motion_setx", "stack" # Specific for set x + if l.startswith("change y by"): return "motion_changeyby", "stack" + if re.match(r"set y to\s*\(.+\)", l): return "motion_sety", "stack" # Specific for set y + if re.match(r"if on edge, bounce( off edge)?", l): return "motion_ifonedgebounce", "stack" + if re.match(r"bounce off edg(e)?", l): return "motion_ifonedgebounce", "stack" # Alias + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + + + # Looks Blocks + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if re.match(r"next costum(e)?", l): return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + # Updated regex for change/set effect by/to + if re.match(r"change\s*(\[.+?v\]|\(.+?\))?\s*effect by", l): return "looks_changeeffectby", "stack" + if re.match(r"set\s*(\[.+?v\]|\(.+?\))?\s*effect to", l): return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + # Sound Blocks + if re.match(r"play sound (.+?) until do(ne)?", l): return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + # Event Blocks (broadcasts) + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + # Control Blocks + if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + # Updated regex for stop block to handle different options + if re.match(r"stop \[(all|this script|other scripts in sprite)\s*v\]", l): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + # Data Blocks + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + # Updated regex for delete of list + if re.match(r"delete \((.+?)\) of \[([^\]]+)\s*v\]", l): return "data_deleteoflist", "stack" + if l.startswith("delete all of [" ): return "data_deletealloflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + # Sensing Blocks + if re.match(r"ask (.+?) and wai(t)?", l): return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom Blocks (procedures_call) - specific rule for "call" + if l.startswith("call "): + return "procedures_call", "stack" + + # Custom Blocks (procedures_call) - LAST RESORT (generic match) + # This should be the very last check for stack-type blocks to avoid conflicts. + # It tries to match anything that looks like a function call with or without arguments. + custom_block_match = re.match(r"([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", l) + if custom_block_match: + # Before returning, ensure it's not a known simple reporter or variable name + # that might have been missed or is being used standalone. + # This is a heuristic; a full parser would be more robust. + potential_name = custom_block_match.group(1).strip() + if potential_name not in ["x position", "y position", "direction", "mouse x", "mouse y", "loudness", "timer", "days since 2000", "username", "answer", "size", "volume"] and \ + not re.fullmatch(r"\[[^\]]+\]", potential_name) and \ + not re.fullmatch(r"\[[^\]]+\]\s*v", potential_name): + return "procedures_call", "stack" + + + raise ValueError(f"Unknown statement: {line!r}") + + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key -> block_data (pre-generated block definitions) + • opcode_keys: dict of opcode -> list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... list of block dictionaries ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + # This will store all generated blocks by their keys, including parent/next links + all_generated_blocks = {} + + # Stack stores (indent, parent_block_key, first_block_in_this_chain, last_block_in_this_chain) + # parent_block_key: The key of the C-block or Hat block that owns the current chain. + # first_block_in_this_chain: The key of the first block added to the current chain (for linking SUBSTACK). + # last_block_in_this_chain: The key of the last block added to the current chain (for 'next' linking). + stack = [(-1, None, None, None)] # Initial: top-level scope (indent -1, no parent, no first/last block) + + # This will store the keys of the top-level scripts (Hat blocks) in order + top_level_script_keys = [] + + lines = pseudo_code.splitlines() + i = 0 + while i < len(lines): + raw_line = lines[i] + stripped_line = raw_line.strip() + + # Skip empty lines and comments + if not stripped_line or stripped_line.startswith("//"): + i += 1 + continue + + current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2 + + # Handle 'else' and 'end' keywords first, as they control scope + if stripped_line.lower() == "else": + # Pop the 'then' substack's scope + popped_indent, popped_parent_key, first_then_block, last_then_block = stack.pop() + if last_then_block: + all_generated_blocks[last_then_block]["next"] = None + + # Link the 'then' substack to its parent 'if-else' block + if popped_parent_key and all_generated_blocks[popped_parent_key]["op_code"] == "control_if_else": + if first_then_block: + all_generated_blocks[popped_parent_key]["inputs"]["SUBSTACK"] = [2, first_then_block] + else: + all_generated_blocks[popped_parent_key]["inputs"]["SUBSTACK"] = [2, None] + + # Now, push a new scope for the 'else' substack + stack.append((current_indent, popped_parent_key, None, None)) # New chain for SUBSTACK2 + i += 1 + continue + + if stripped_line.lower() == "end": + # Pop the current substack's scope + popped_indent, popped_parent_key, first_substack_block, last_substack_block = stack.pop() + if last_substack_block: + all_generated_blocks[last_substack_block]["next"] = None + + if popped_parent_key: + parent_block = all_generated_blocks[popped_parent_key] + + # Only C-blocks and procedure definitions have SUBSTACK inputs + # Hat blocks (like event_whenflagclicked) do NOT have SUBSTACK inputs. + # Their children are linked via the 'next' pointer from the hat block itself. + if parent_block["block_shape"] == "C-Block" or parent_block["op_code"] == "procedures_definition": + if parent_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in parent_block["inputs"] and \ + parent_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in parent_block["inputs"]: + parent_block["inputs"]["SUBSTACK2"] = [2, first_substack_block] if first_substack_block else [2, None] + else: + parent_block["inputs"]["SUBSTACK"] = [2, first_substack_block] if first_substack_block else [2, None] + # No 'else' needed here for hat blocks, as they don't have SUBSTACK. + + i += 1 + continue + + # Pop from stack if indentation decreases + while stack and stack[-1][0] >= current_indent: + # If we are at the top level (indent 0) and encounter a new hat block, + # we need to finalize the previous top-level script. + if stack[-1][1] is None and current_indent == 0: # This is a top-level script scope + popped_indent, popped_parent_key, first_chain_block, last_chain_block = stack.pop() + if last_chain_block: + all_generated_blocks[last_chain_block]["next"] = None + else: + break # Stop popping if indentation is not strictly decreasing or it's not a new top-level script + + # Get the current active scope from the stack + current_scope_indent, current_parent_key, first_active_block_in_chain, last_active_block_in_chain = stack[-1] + + # Classify the statement + stmt_for_parse = stripped_line.rstrip("then").strip() # Only strip 'then' here + opcode, ntype = classify(stmt_for_parse) + + if opcode is None: # Should not happen if classify is robust, but for safety + i += 1 + continue + + key = pick_key(opcode) + # Get a fresh copy of block definition from the global definitions + info = copy.deepcopy(all_block_definitions[opcode]) + + info["id"] = key # Add ID for easier debugging/tracking + info["topLevel"] = False # Default to false + info["next"] = None # Default next to None + info["parent"] = current_parent_key # Set parent based on current scope + + # Link 'next' for the previous block in the current chain + if last_active_block_in_chain: + all_generated_blocks[last_active_block_in_chain]["next"] = key + + # If this is the very first block in this chain, update the 'first_block_in_chain' + if first_active_block_in_chain is None: + first_active_block_in_chain = key + + # Special handling for Hat blocks (they start new top-level scripts) + if ntype == "hat": + info["topLevel"] = True + top_level_script_keys.append(key) # Add to top-level flow + # For hat blocks, their direct children are the start of their script chain. + # We push a new scope for this. + stack.append((current_indent, key, None, None)) # New scope starts, current block is the parent + elif ntype == "c_block" or opcode == "procedures_definition": + # Push a new scope for the C-block's or define block's substack + stack.append((current_indent, key, None, None)) # New scope: parent is 'key', no first/last yet + + # Update the current scope's last block key. + # This needs to be done *after* pushing a new scope for C-blocks/Hat blocks + # so that the new block becomes the parent of the *next* blocks in the new scope. + # For the current scope, the new block is the last one in its chain. + stack[-1] = (stack[-1][0], stack[-1][1], first_active_block_in_chain, key) + + + # Parse inputs and fields + info["inputs"] = {} + info["fields"] = {} + + # --- Input Parsing Logic (adapted from original generate_plan) --- + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if opcode == "motion_movesteps": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["STEPS"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_turnright" or opcode == "motion_turnleft": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DEGREES"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_gotoxy": + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_glidesecstoxy": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt_for_parse, re.IGNORECASE) + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_secs: info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_pointindirection": + m = re.search(r"direction\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DIRECTION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_changexby", "motion_changeyby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DX" if opcode == "motion_changexby" else "DY"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_setx", "motion_sety"]: + m = re.search(r"(?:set x to|set y to)\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["X" if opcode == "motion_setx" else "Y"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_changesizeby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_setsizeto": + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SIZE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_changeeffectby", "sound_changeeffectby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE" if opcode == "looks_changeeffectby" else "VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE" if opcode == "looks_seteffectto" else "VOLUME"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["NUM"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "control_wait": + m = re.search(r"wait\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DURATION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "control_repeat": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["TIMES"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "data_changevariableby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "data_deleteoflist": + m = re.search(r"delete\s*\((.+?)\)\s*of", stmt_for_parse, re.IGNORECASE) + if m: + val_str = m.group(1).strip() + if val_str.isdigit(): + info["inputs"]["INDEX"] = {"kind": "value", "value": int(val_str)} + else: # "all", "last", "random" + info["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + elif opcode == "data_insertatlist": + m_item = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt_for_parse, re.IGNORECASE) + m_index = re.search(r"at\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + elif opcode == "data_replaceitemoflist": + m_index = re.search(r"replace item\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + m_item = re.search(r"with\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + elif opcode == "event_whengreaterthan": + m = re.search(r">\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + + # String inputs + elif opcode == "looks_sayforsecs": + m = re.search(r"say\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_say": + m = re.search(r"say\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = parse_reporter_or_value(m.group(1).strip(), pick_key) + elif opcode == "looks_thinkforsecs": + m = re.search(r"think\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_think": + m = re.search(r"think\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "sensing_askandwait": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["QUESTION"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["ITEM"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_setvariableto": + m_var = re.search(r"set\s*\[([^\]]+)\s*v\]\s*to\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m_var: + var_name = m_var.group(1).strip() + value_str = m_var.group(2).strip() + info["fields"]["VARIABLE"] = [var_name, None] + info["inputs"]["VALUE"] = parse_reporter_or_value(value_str, pick_key) + + # Dropdown/Menu inputs + elif opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_glideto": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m_secs: + info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m.group(1))} + option = m_secs.group(2).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TOWARDS"] = {"kind": "menu", "option": option} + elif opcode == "sensing_keypressed": # For boolean block + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["KEY_OPTION"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "sensing_touchingobject": # For boolean block + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + elif option == "edge": option = "_edge_" + info["inputs"]["TOUCHINGOBJECTMENU"] = {"kind": "menu", "option": option} + elif opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "myself": option = "_myself_" + info["inputs"]["CLONE_OPTION"] = {"kind": "menu", "option": option} + elif opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SOUND_MENU"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["COSTUME"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait"]: + m = re.search(r"switch backdrop to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BACKDROP"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BROADCAST_INPUT"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "event_whenbroadcastreceived": + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BROADCAST_OPTION"] = {"kind": "menu", "option": m.group(1).strip()} + + # Conditional inputs (Boolean blocks) + elif opcode in ["control_if", "control_if_else", "control_wait_until", "control_repeat_until"]: + cond_match = re.search(r"<(.*?)>", stmt_for_parse) + if cond_match: + info["inputs"]["CONDITION"] = parse_condition(cond_match.group(1).strip(), pick_key) + elif opcode in ["operator_and", "operator_or", "operator_not", "operator_contains", + "sensing_touchingcolor", "sensing_coloristouchingcolor", "sensing_mousedown"]: + # These are handled by parse_condition directly, which returns the nested structure + # No need to re-parse inputs here, as they are part of the condition structure + pass # Inputs are set when parse_condition is called for the parent block + + + # Fields parsing + if "VARIABLE" in info["fields"]: + print(f"DEBUG: Attempting to parse VARIABLE for opcode {opcode}, stmt='{stmt_for_parse}'") + m = re.search(r"\[([^\]]+)\s*v\]", stmt_for_parse) + if m: + var_name = m.group(1).strip() + info["fields"]["VARIABLE"] = [var_name, None] + print(f"DEBUG: Found variable: {var_name}") + else: + print(f"DEBUG: No variable match found for '{stmt_for_parse}'") + if "LIST" in info["fields"]: + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["LIST"] = [m.group(1), None] + if "STOP_OPTION" in info["fields"]: + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STOP_OPTION"] = [m.group(1), None] + if "STYLE" in info["fields"]: + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STYLE"] = [m.group(1), None] + if "DRAG_MODE" in info["fields"]: + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["DRAG_MODE"] = [m.group(1), None] + if "EFFECT" in info["fields"] and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["EFFECT"] = [m.group(1).upper(), None] + if "NUMBER_NAME" in info["fields"] and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["NUMBER_NAME"] = [m.group(1), None] + if "FRONT_BACK" in info["fields"] and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FRONT_BACK"] = [m.group(1), None] + if "FORWARD_BACKWARD" in info["fields"] and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + if "OPERATOR" in info["fields"] and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["OPERATOR"] = [m.group(1).upper(), None] + if "CURRENTMENU" in info["fields"] and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + if "PROPERTY" in info["fields"] and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt_for_parse, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + info["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + if "WHENGREATERTHANMENU" in info["fields"] and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + if "KEY_OPTION" in info["fields"] and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["KEY_OPTION"] = [m.group(1), None] + if "BACKDROP" in info["fields"] and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BACKDROP"] = [m.group(1), None] + if "BROADCAST_OPTION" in info["fields"] and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + # Custom block specific parsing + if opcode == "procedures_definition": + proc_def_match = re.match(r"(?:define|procedure)\s+([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))?", stmt_for_parse, re.IGNORECASE) + if proc_def_match: + proc_name = proc_def_match.group(1).strip() + args_str = proc_def_match.group(2) + info["procedure_name"] = proc_name + info["is_custom_definition"] = True + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + info["inputs"]["PROCCONTAINER"] = { + "kind": "block_prototype", + "name": proc_name, + "arguments": [{"name": arg, "type": "any"} for arg in args] + } + # For a define block, its children form its body, so push a new scope + stack.append((current_indent, key, None, None)) + + elif opcode == "procedures_call": + call_match = re.match(r"(?:call\s+)?([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", stmt_for_parse, re.IGNORECASE) + if call_match: + custom_block_name = call_match.group(1).strip() + args_str = call_match.group(2) + info["custom_block_name"] = custom_block_name + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for idx, arg_val_str in enumerate(args): + info["inputs"][f"argument_name_{idx+1}"] = parse_reporter_or_value(arg_val_str, pick_key) + + # Add the newly created block to the global collection + all_generated_blocks[key] = info + + # If it's a C-block, push a new scope for its substack + if ntype == "c_block": + stack.append((current_indent, key, None, None)) # New scope for substack + + i += 1 # Move to the next line + + # Final pass to ensure all 'next' pointers for the last blocks in any chain are None + # and to link any remaining substacks + while stack: + popped_indent, popped_parent_key, first_substack_block, last_substack_block = stack.pop() + if last_substack_block: + all_generated_blocks[last_substack_block]["next"] = None + + if popped_parent_key: + parent_block = all_generated_blocks[popped_parent_key] + + # Only C-blocks and procedure definitions have SUBSTACK inputs + if parent_block["block_shape"] == "C-Block" or parent_block["op_code"] == "procedures_definition": + if parent_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in parent_block["inputs"] and \ + parent_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in parent_block["inputs"]: + parent_block["inputs"]["SUBSTACK2"] = [2, first_substack_block] if first_substack_block else [2, None] + else: + parent_block["inputs"]["SUBSTACK"] = [2, first_substack_block] if first_substack_block else [2, None] + # No 'else' needed here for hat blocks, as they don't have SUBSTACK. + + print(f"[ALL OPCODE BLCOKS KEY 2]: {all_generated_blocks}") + # Construct the final flow output based on the collected top-level keys + # Recursively build the block structure for each top-level script + def build_script_flow(current_block_key, visited=None): + if visited is None: + visited = set() + + script_flow = [] + current_iter_key = current_block_key + + while current_iter_key: + # Detect cyclic reference + if current_iter_key in visited: + script_flow.append({ + "block_key": current_iter_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected, stopping recursion" + }) + break + + visited.add(current_iter_key) + block = all_generated_blocks.get(current_iter_key) + if not block: + break # Should not happen if keys are correct + + output_block = { + "block_key": block["id"], + "opcode": block["op_code"], + "type": block["block_shape"].replace(" Block", "").lower().replace("c-", "c_"), + "inputs": {}, + "fields": {} + } + + # Handle all input types + for inp_name, inp_val in block.get("inputs", {}).items(): + if inp_name in ["SUBSTACK", "SUBSTACK2"]: + if inp_val and len(inp_val) > 1 and inp_val[1] in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(inp_val[1], visited.copy()) + else: + output_block["inputs"][inp_name] = [] + elif inp_name == "PROCCONTAINER" and block.get("is_custom_definition"): + output_block["inputs"][inp_name] = inp_val + else: + output_block["inputs"][inp_name] = inp_val + + for field_name, field_val in block.get("fields", {}).items(): + output_block["fields"][field_name] = field_val + + if block.get("custom_block_name"): + output_block["custom_block_name"] = block["custom_block_name"] + + if block.get("procedure_name"): + output_block["procedure_name"] = block["procedure_name"] + output_block["is_custom_definition"] = True + + script_flow.append(output_block) + + # Proceed to the next block in sequence + next_key = block.get("next") + if next_key in visited: + script_flow.append({ + "block_key": next_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected in 'next' pointer" + }) + break + + current_iter_key = next_key + + return script_flow + + final_flow_output = [] + for key in top_level_script_keys: + final_flow_output.extend(build_script_flow(key)) + return {"flow": final_flow_output} + +# Example input with opcodes for the initial generation +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_glidesecstoxy","count":1}, # Corrected count for this example + {"opcode":"motion_xposition","count":1}, # Used multiple times in conditions + {"opcode":"motion_setx","count":1}, # Added count for motion_setx + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":2}, # Corrected count for this example + {"opcode":"control_stop","count":1}, # Corrected count for this example + {"opcode":"operator_lt","count":1}, # Used in condition + {"opcode":"sensing_touchingobject","count":1}, # Used in condition, and for if on edge, bounce + {"opcode":"sensing_touchingobjectmenu","count":1}, # This will now be generated as a child of sensing_touchingobject + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":2}, + {"opcode":"data_showvariable","count":2}, +] + +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + +# Example pseudo-code inputs from the JSON files +pseudo_code_examples = [""" +when green flag clicked + go to x: (240) y: (-135) + set [score v] to (1) + set [speed v] to (1) + show variable [score v] + show variable [speed v] + forever + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end +end +""",] + +# Process each example and print the plan +for i, pseudo_code_input in enumerate(pseudo_code_examples): + print(f"\n--- Processing Example {i+1} ---") + print(pseudo_code_input.strip()) + try: + # Regenerate blocks and opcode_occurrences for each run to ensure fresh keys + # This is important because pick_key uses a defaultdict that persists state. + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) + print(json.dumps(plan, indent=2)) + except Exception as e: + print(f"Error processing example: {e}") + import traceback + traceback.print_exc() diff --git a/utils/plan_generator_10.py b/utils/plan_generator_10.py new file mode 100644 index 0000000000000000000000000000000000000000..128f3ea7dfa5c19e0b184659609f498a060620c1 --- /dev/null +++ b/utils/plan_generator_10.py @@ -0,0 +1,2175 @@ +import json +import copy +import re +from collections import defaultdict + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False + + # Ensure inputs and fields are dictionaries, even if they were None or list in definition + if "inputs" not in main_block_data or not isinstance(main_block_data["inputs"], dict): + main_block_data["inputs"] = {} + if "fields" not in main_block_data or not isinstance(main_block_data["fields"], dict): + main_block_data["fields"] = {} + + generated_blocks[main_key] = main_block_data + + # Handle menus + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_key + + # Ensure inputs and fields are dictionaries for menu blocks too + if "inputs" not in menu_block_data or not isinstance(menu_block_data["inputs"], dict): + menu_block_data["inputs"] = {} + if "fields" not in menu_block_data or not isinstance(menu_block_data["fields"], dict): + menu_block_data["fields"] = {} + + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 1 and \ + main_block_data["inputs"][input_name][0] == 1: + + main_block_data["inputs"][input_name][1] = menu_key + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +# Consolidated block definitions from all JSON files +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_distanceto": { # Added sensing_distanceto + "block_name": "(distance to ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto", + "functionality": "Reports the distance from the sprite to the mouse-pointer or another specified sprite.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": False, "topLevel": True + }, + "sensing_distanceto_menu": { # Added sensing_distanceto_menu (though its use is now commented out in parse_reporter_or_value) + "block_name": "distance to menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto_menu", + "functionality": "Menu for distance to block.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input] ", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True + } +} + +def unparen(s): + s = s.strip() + # keep peeling off *all* matching outer parens + while True: + m = re.fullmatch(r"\((.*)\)", s) + if not m: + break + s = m.group(1).strip() + return s + +def _register_block(opcode, parent_key, is_shadow, pick_key_func, all_blocks_dict, inputs=None, fields=None): + """ + Helper to create and register a block in the all_blocks_dict. + It uses pick_key_func to get a unique ID. + """ + key = pick_key_func(opcode) + block_data = copy.deepcopy(all_block_definitions[opcode]) + block_data["id"] = key + block_data["parent"] = parent_key # Set parent directly here if known + block_data["next"] = None + block_data["topLevel"] = not is_shadow # Shadow blocks are not top-level + block_data["shadow"] = is_shadow + + # Ensure inputs and fields are dictionaries + if "inputs" not in block_data or not isinstance(block_data["inputs"], dict): + block_data["inputs"] = {} + if "fields" not in block_data or not isinstance(block_data["fields"], dict): + block_data["fields"] = {} + + if inputs: + block_data["inputs"].update(inputs) + if fields: + block_data["fields"].update(fields) + + all_blocks_dict[key] = block_data + return key + +def _auto_balance(text): + # if there are more "(" than ")", append the missing ")" + diff = text.count("(") - text.count(")") + if diff > 0: + text = text + ")"*diff + # same for square brackets + diff = text.count("[") - text.count("]") + if diff > 0: + text = text + "]"*diff + return text + +def strip_outer_angle_brackets(text): + """ + Strip exactly one balanced pair of outer <...> brackets, only if they wrap the whole string. + """ + text = text.strip() + if text.startswith("<") and text.endswith(">"): + depth = 0 + for i, char in enumerate(text): + if char == '<': + depth += 1 + elif char == '>': + depth -= 1 + if depth == 0 and i == len(text) - 1: + return text[1:-1].strip() + # If we exit the loop and depth is 0, it means the outer brackets were balanced and wrapped the whole string + if depth == 0: + return text[1:-1].strip() + return text + +def extract_condition_balanced(stmt): + # 1. Remove "if" and "then" + stmt = stmt.strip() + if stmt.lower().startswith("if "): + stmt = stmt[3:].strip() + if stmt.lower().startswith("repeat until"): + stmt = stmt[12:].strip() + if stmt.lower().startswith("wait until "): + stmt = stmt[11:].strip() + if stmt.lower().endswith(" then"): + stmt = stmt[:-5].strip() + + # Helper to detect and strip single outer balanced angle brackets + def unwrap_balanced(s): + if s.startswith("<") and s.endswith(">"): + depth = 0 + for i in range(len(s)): + if s[i] == "<": + depth += 1 + elif s[i] == ">": + depth -= 1 + if depth == 0 and i < len(s) - 1: + return s # Early balance → not a single outer wrapper + if depth == 0: + return s[1:-1].strip() + return s + + # Recursively simplify things like > to not + def simplify(s): + s = unwrap_balanced(s) + s = s.strip() + + # Match > pattern + m = re.fullmatch(r"not\s*<(.+)>", s, re.IGNORECASE) + if m: + inner = m.group(1).strip() + inner = simplify(inner) + return f"not <{inner}>" + + # Match comparison operators like <(x position) < (100)> + # This part might be redundant if the main parser handles it, but good for internal consistency + m_comp = re.fullmatch(r"<\s*\(([^<>]+?)\)\s*([<>=])\s*\(([^<>]+?)\)\s*>", stmt) + if m_comp: + return f"({m_comp.group(1).strip()}) {m_comp.group(2)} ({m_comp.group(3).strip()})" + + return s + + return simplify(stmt) + +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, parent_key, pick_key_func, all_generated_blocks): + text = _auto_balance(text.strip()) + text = unparen(text.strip()) + # Check for numeric literal (including parenthesized numbers like "(0)" or "(10)") + m_num = re.fullmatch(r"\(?\s*(-?\d+(\.\d+)?)\s*\)?", text) + if m_num: + val_str = m_num.group(1) + return {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # Variable reporter: [score v], [health v], etc. + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, + fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + + # Now catch other bracketed values as literal strings + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + # Check for simple reporters, potentially with outer parentheses + m_simple_reporter = re.fullmatch(r"\((.+?)\)", text) + if m_simple_reporter: + inner_text = m_simple_reporter.group(1).strip() + if inner_text in simple_reporters: + block_id = _register_block(simple_reporters[inner_text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + # Also check for simple reporters without parentheses (e.g., if passed directly) + if text in simple_reporters: + block_id = _register_block(simple_reporters[text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + + # Variable reporter: [score v] or (score) or just "score" + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + # Ensure it's not a simple reporter already handled, or a number + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [potential_var_name, None]}) + return {"kind": "block", "block": block_id} + # Handle plain variable names like "score", "number 1", "total score" + if re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_ ]*", text): # Allow spaces for "number 1" etc. + # Exclude known simple reporters that don't have 'v' or parentheses + if text not in simple_reporters: + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [text, None]}) + return {"kind": "block", "block": block_id} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + return {"kind": "block", "block": block_id} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + max_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + inputs = {"FROM": min_val_obj, "TO": max_val_obj} + block_id = _register_block("operator_random", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if min_val_obj.get("kind") == "block": all_generated_blocks[min_val_obj["block"]]["parent"] = block_id + if max_val_obj.get("kind") == "block": all_generated_blocks[max_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (join ()()) (operator_join) - handle both [] and () for inputs + + #m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s+(\[.+?\]|\(.+?\))", text) # Try (val) (val) + m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s*(\[.+?\]|\(.+?\))", text) + if m: + part1_txt = m.group(1).strip() + part2_txt = m.group(2).strip() + str1_obj = parse_reporter_or_value(part1_txt, None, pick_key_func, all_generated_blocks) + str2_obj = parse_reporter_or_value(part2_txt, None, pick_key_func, all_generated_blocks) + inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + block_id = _register_block("operator_join", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs) + # set parents if nested blocks + for obj in (str1_obj, str2_obj): + if obj.get("kind") == "block": + all_generated_blocks[obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # letter () of () (operator_letterof) - handle both [] and () for inputs + m = re.search(r"letter \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + string_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"LETTER": index_obj, "STRING": string_val_obj} + block_id = _register_block("operator_letterof", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + if string_val_obj.get("kind") == "block": all_generated_blocks[string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (length of ()) (operator_length) - handle both [] and () for inputs + #m = re.search(r"length of \((.+?)\)", text) + m = re.search(r"length of\s*(?:\((.+?)\)|\[(.+?)\])", text) + if not m: + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + arg_txt = (m.group(1) or m.group(2)).strip() + list_or_string_val_obj = parse_reporter_or_value(arg_txt, None, pick_key_func, all_generated_blocks) + #list_or_string_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"STRING": list_or_string_val_obj} + block_id = _register_block("operator_length", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if list_or_string_val_obj.get("kind") == "block": all_generated_blocks[list_or_string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (() mod ()) (operator_mod) + # m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + m = re.search(r"\[([^\]]+)\s*v\]\s*mod\s*\(?\s*(.+?)\s*\)?", text) + if m: + num1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + num2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM1": num1_obj, "NUM2": num2_obj} + block_id = _register_block("operator_mod", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num1_obj.get("kind") == "block": all_generated_blocks[num1_obj["block"]]["parent"] = block_id + if num2_obj.get("kind") == "block": all_generated_blocks[num2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": num_obj} + block_id = _register_block("operator_round", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num_obj.get("kind") == "block": all_generated_blocks[num_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (() of ()) (operator_mathop) - handle variable for function type + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) # e.g. [sqrt v] of ((x pos) * (x pos)) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + # Also handle direct string for function type (e.g., "abs of (x)") + m = re.search(r"([a-zA-Z]+)\s*of\s*\((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + # This regex is designed to handle nested parentheses correctly. + # It looks for an opening parenthesis, then non-parenthesis characters or balanced parentheses, + # followed by an operator, and then the second operand. + # This is a simplified approach; a full-fledged parser would use a stack. + # arithmetic_match = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + arithmetic_match = re.search(r"\(?\s*(.+?)\s*\)?\s*([\+\-\*/])\s*\(?\s*(.+?)\s*\)?", text) + if not arithmetic_match: + # Try to match without outer parentheses for the operands, but still with an operator + arithmetic_match = re.search(r"(.+?)\s*([+\-*/])\s*(.+)", text) + + if arithmetic_match: + op1_str = arithmetic_match.group(1).strip() + operator_symbol = arithmetic_match.group(2).strip() + op2_str = arithmetic_match.group(3).strip() + + op1_obj = parse_reporter_or_value(op1_str, None, pick_key_func, all_generated_blocks) + op2_obj = parse_reporter_or_value(op2_str, None, pick_key_func, all_generated_blocks) + + opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + inputs = {"NUM1": op1_obj, "NUM2": op2_obj} + block_id = _register_block(opcode_map[operator_symbol], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if op1_obj.get("kind") == "block": all_generated_blocks[op1_obj["block"]]["parent"] = block_id + if op2_obj.get("kind") == "block": all_generated_blocks[op2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (costume ()) (looks_costumenumbername) - handle with or without 'v' + m = re.search(r"costume \((.+?)\)", text) + if not m: + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_costumenumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (backdrop ()) (looks_backdropnumbername) - handle with or without 'v' + m = re.search(r"backdrop \((.+?)\)", text) + if not m: + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_backdropnumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (distance to ()) (sensing_distanceto) - handle with or without 'v' + m = re.search(r"distance to \((.+?)\)", text) + if not m: + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + + # This block has a dropdown FIELD, not an input that links to a shadow block + fields = {"TARGET": [target_val, None]} + block_id = _register_block("sensing_distanceto", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (current ()) (sensing_current) - handle with or without 'v' + m = re.search(r"current \((.+?)\)", text) + if not m: + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + fields = {"CURRENTMENU": [unit.upper(), None]} + block_id = _register_block("sensing_current", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (() of ()) (sensing_of) - Corrected logic + #m = re.search(r"\((.+?)\)\s*of\s*(?:\((.+?)\)|\[(.+?)\s*v\])", text) + m = re.search(r"\((.+?)\)\s*of\s*(?:\((.+?)\)|\[(.+?)\s*v\])", text) + if m: + prop_str = m.group(1).strip() + obj = (m.group(2) or m.group(3)).strip() + + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + property_value = prop_map.get(prop_str, prop_str) + + if obj.lower() == "stage": obj_val = "_stage_" + elif obj.lower() == "myself": obj_val = "_myself_" + else: obj_val = obj + + # Create the sensing_of_object_menu shadow block + object_menu_id = _register_block("sensing_of_object_menu", parent_key, True, pick_key_func, all_generated_blocks, fields={"OBJECT": [obj_val, None]}) + + # Create the main sensing_of block + inputs = {"OBJECT": [1, object_menu_id]} + fields = {"PROPERTY": [property_value, None]} # PROPERTY is a field of the main block + + block_id = _register_block("sensing_of", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + all_generated_blocks[object_menu_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item (index) of [list v]) (data_itemoflist) + m = re.search(r"item \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + # Create data_list shadow block for the list name + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + + inputs = {"INDEX": index_obj, "LIST": [1, list_block_id]} + fields = {} # No fields in data_itemoflist itself for the list name + block_id = _register_block("data_itemoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item # of [item] in [list v]) (data_itemnumoflist) + m = re.search(r"item # of \((.+?)\) in \((.+?)\)", text) + if not m: + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + # Create data_list shadow block for the list name + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + + inputs = {"ITEM": item_obj, "LIST": [1, list_block_id]} + fields = {} # No fields in data_itemnumoflist itself for the list name + block_id = _register_block("data_itemnumoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if item_obj.get("kind") == "block": all_generated_blocks[item_obj["block"]]["parent"] = block_id + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + raise ValueError(f"Can't parse reporter or value: {text}") + +def parse_condition(stmt, parent_key, pick_key_func, all_generated_blocks): + """ + Parse Scratch-style boolean conditions, handling comparisons (<, =, >), + boolean operators (and, or, not), and other sensing conditions. + """ + s = stmt.strip() + s = extract_condition_balanced(s) + s = s.lower() + + print(f"the stmt was this {stmt} and parsed was this {s}") + # 1) Boolean NOT: `not <...>` + #m_not = re.fullmatch(r'not\s*<\s*(.+?)\s*>', s, re.IGNORECASE) + #m_not = re.fullmatch(r"\s*<\s*not\s+(.+?)\s*>\s*", s, re.IGNORECASE) + m_not = re.fullmatch(r"\s*(?:<\s*)?not\s+(.+?)(?:\s*>)?\s*",s, re.IGNORECASE) + if m_not: + inner = m_not.group(1).strip() + print(f"[2]the stmt was this {stmt} and parsed was this {s}") + inner_obj = parse_condition(inner, parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + bid = _register_block("operator_not", parent_key, False, pick_key_func, all_generated_blocks, # Pass parent_key + inputs={"OPERAND": inner_obj}) + if inner_obj.get("kind") == "block": + all_generated_blocks[inner_obj["block"]]["parent"] = bid + return {"kind": "block", "block": bid} + + # 2) Boolean AND / OR + #m_andor = re.fullmatch(r"<\s*(.+?)\s+(and|or)\s+(.+?)\s*>", s, re.IGNORECASE) + m_andor = re.fullmatch(r"\s*(.+?)\s+(and|or)\s+(.+?)\s*", s, re.IGNORECASE) + if m_andor: + cond1_obj = parse_condition(m_andor.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + cond2_obj = parse_condition(m_andor.group(3).strip(), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_block = 'operator_and' if m_andor.group(2).lower() == 'and' else 'operator_or' + print(f"The cond1: {cond1_obj} and the cond2: {cond2_obj} [for testing]") + inputs = {"OPERAND1": cond1_obj, "OPERAND2": cond2_obj} + block_id = _register_block(op_block, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + if cond1_obj.get("kind") == "block": all_generated_blocks[cond1_obj["block"]]["parent"] = block_id + if cond2_obj.get("kind") == "block": all_generated_blocks[cond2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # 1a) Comparisons with explicit angle wrappers: < (...) op (...) > + m = re.fullmatch( + r"\s*<\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*>\s*", + s, + re.VERBOSE + ) + if m: + left_txt, right_txt = m.group(1), m.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + # Set parents for nested inputs + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 1b) Simple comparisons without angle wrappers: A op B + m_simple = re.fullmatch(r"\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*", s) + if m_simple: + left_txt, right_txt = m_simple.group(1), m_simple.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m_simple.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 4) Contains: <[list v] contains [item]?> + #m = re.search(r"\[([^\]]+)\s*v\] contains \[(.+?)\]\?", s) + m = re.fullmatch(r"\s*\[(.+?)\]\s+contains\s+\[(.+?)\]\?\s*", s) + if m: + list_name = m.group(1).strip() + item_val = {"kind": "value", "value": m.group(2).strip()} # Item can be a value or a block + + # Create the data_list reporter block + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) # Pass parent_key + + inputs = {"LIST": {"kind": "block", "block": list_block_id}, "ITEM": item_val} + block_id = _register_block("data_listcontainsitem", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 5) Touching object: + m_touch = re.fullmatch(r""" + \s* # leading space + (?:<\s*)? # optional '<' + touching # literal + \s*\[\s* + (?P[^\]]+?) # capture the sprite name + \s*v\]\? # close the [sprite v]? + (?:\s*>)? # optional '>' + """, s, re.IGNORECASE | re.VERBOSE) + if m_touch: + sprite = m_touch.group('sprite').strip() + val = {'mouse-pointer':'_mouse_', 'edge':'_edge_'}.get(sprite, sprite) + + mid = _register_block( + "sensing_touchingobjectmenu", parent_key, True, pick_key_func, all_generated_blocks, # Pass parent_key + fields={"TOUCHINGOBJECTMENU":[val, None]} + ) + bid = _register_block( + "sensing_touchingobject", parent_key, False, pick_key_func, all_generated_blocks, # Pass parent_key + inputs={"TOUCHINGOBJECTMENU":[1, mid]} + ) + all_generated_blocks[mid]["parent"] = bid + return {"kind":"block","block":bid} + + # 6) Touching color: + m = re.search(r"touching color \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR": [1, [9, m.group(1)]]} # Color input is special, often a list [type, value] + block_id = _register_block("sensing_touchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + return {"kind": "block", "block": block_id} + + # 7) Color is touching color: + m = re.search(r"color \[(#[0-9A-Fa-f]{6})\] is touching \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR1": [1, [9, m.group(1)]], "COLOR2": [1, [9, m.group(2)]]} + block_id = _register_block("sensing_coloristouchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + return {"kind": "block", "block": block_id} + + # 8) Key pressed: + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", s) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", parent_key, True, pick_key_func, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) # Pass parent_key + + inputs = {"KEY_OPTION": [1, menu_block_id]} + block_id = _register_block("sensing_keypressed", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + all_generated_blocks[menu_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 9) Mouse down?: mouse down? + if s == "mouse down?": + block_id = _register_block("sensing_mousedown", parent_key, False, pick_key_func, all_generated_blocks) # Pass parent_key + return {"kind": "block", "block": block_id} + + val_obj = parse_reporter_or_value(unparen(stmt), parent_key, pick_key_func, all_generated_blocks) + if val_obj: + return val_obj + + raise ValueError(f"Can't parse condition: {stmt}") + +def classify(line): + """ + Classifies a pseudo-code line into its corresponding Scratch opcode and block type. + Order of checks matters: more specific patterns should come before more general ones. + """ + l = line.lower().strip() + + # Ignore comments + if l.startswith("//"): return None, None + + # Hat Blocks (most specific first) + if re.match(r"when green flag click(ed)?", l): return "event_whenflagclicked", "hat" + if re.match(r"when (.+?) key press(ed)?", l): return "event_whenkeypressed", "hat" + if re.match(r"when this sprite click(ed)?", l): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if re.match(r"when i start as a clo(ne)?", l): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + if l.startswith("procedure "): return "procedures_definition", "hat" # For "procedure moveBall" + + # Motion Blocks + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + # IMPORTANT: More specific glide block before less specific one + if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if re.match(r"set x to\s*\(.+\)", l): return "motion_setx", "stack" # Specific for set x + if l.startswith("change y by"): return "motion_changeyby", "stack" + if re.match(r"set y to\s*\(.+\)", l): return "motion_sety", "stack" # Specific for set y + #if re.match(r"if on edge, bounce( off edge)?", l): return "motion_ifonedgebounce", "stack" + if re.match(r"if on edge,\s*bounc(e)?(\s+off\s+edge)?", l.strip(), re.IGNORECASE): return "motion_ifonedgebounce", "stack" + if re.match(r"bounce off edg(e)?", l): return "motion_ifonedgebounce", "stack" # Alias + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + + + # Looks Blocks + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if re.match(r"next costum(e)?", l): return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + # Updated regex for change/set effect by/to + if re.match(r"change\s*(\[.+?v\]|\(.+?\))?\s*effect by", l): return "looks_changeeffectby", "stack" + if re.match(r"set\s*(\[.+?v\]|\(.+?\))?\s*effect to", l): return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + # Sound Blocks + if re.match(r"play sound (.+?) until do(ne)?", l): return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + # Event Blocks (broadcasts) + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + # Control Blocks + if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + # Updated regex for stop block to handle different options + if re.match(r"stop \[(all|this script|other scripts in sprite)\s*v\]", l): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + # Data Blocks + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + # Updated regex for delete of list + if re.match(r"delete \((.+?)\) of \[([^\]]+)\s*v\]", l): return "data_deleteoflist", "stack" + if l.startswith("delete all of [" ): return "data_deletealloflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + # Sensing Blocks + if re.match(r"ask (.+?) and wai(t)?", l): return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom Blocks (procedures_call) - specific rule for "call" + if l.startswith("call "): + return "procedures_call", "stack" + + # Custom Blocks (procedures_call) - LAST RESORT (generic match) + # This should be the very last check for stack-type blocks to avoid conflicts. + # It tries to match anything that looks like a function call with or without arguments. + custom_block_match = re.match(r"([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", l) + if custom_block_match: + # Before returning, ensure it's not a known simple reporter or variable name + # that might have been missed or is being used standalone. + # This is a heuristic; a full parser would be more robust. + potential_name = custom_block_match.group(1).strip() + if potential_name not in ["x position", "y position", "direction", "mouse x", "mouse y", "loudness", "timer", "days since 2000", "username", "answer", "size", "volume"] and \ + not re.fullmatch(r"\[[^\]]+\]", potential_name) and \ + not re.fullmatch(r"\[[^\]]+\]\s*v", potential_name): + return "procedures_call", "stack" + + + raise ValueError(f"Unknown statement: {line!r}") + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key -> block_data (pre-generated block definitions) + • opcode_keys: dict of opcode -> list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... list of block dictionaries ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + all_generated_blocks = {} # Initialize as empty, blocks will be added as they are parsed + + # Stack stores (indent, owner_block_id, last_block_in_current_linear_chain_id) + # owner_block_id: The ID of the C-block or Hat block that owns the current substack. + # last_block_in_current_linear_chain_id: The ID of the last block added to the *current linear sequence* within this substack. + stack = [(-1, None, None)] # Sentinel: (indent, owner_block_id, last_block_in_current_linear_chain_id) + + top_level_script_keys = [] + + lines = pseudo_code.splitlines() + i = 0 + while i < len(lines): + raw_line = lines[i] + stripped_line = raw_line.strip() + + # Skip empty lines and comments + if not stripped_line or stripped_line.startswith("//"): + i += 1 + continue + + current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2 + + # Handle 'else' and 'end' first, as they control scope + if stripped_line.lower() == "else": + # Pop the 'then' substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # The 'if-else' block (popped_owner_key) is the owner. + # Push a new scope for the 'else' substack, with the same owner. + stack.append((current_indent, popped_owner_key, None)) # New scope for 'else' part, no last block yet + i += 1 + continue + + if stripped_line.lower() == "end": + # Pop the current substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # If the substack was empty, ensure the input is [2, None]. + if popped_owner_key: + owner_block = all_generated_blocks[popped_owner_key] + if owner_block["block_shape"] == "C-Block" or owner_block["op_code"] == "procedures_definition": + if owner_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in owner_block["inputs"] and \ + owner_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in owner_block["inputs"]: + # If SUBSTACK is already set, this 'end' closes the SUBSTACK2 (else part) + if not owner_block["inputs"].get("SUBSTACK2"): # Only set if not already set by a block + owner_block["inputs"]["SUBSTACK2"] = [2, None] + elif not owner_block["inputs"].get("SUBSTACK"): # Only set if not already set by a block + owner_block["inputs"]["SUBSTACK"] = [2, None] + + i += 1 + continue + + # Adjust stack based on indentation for regular blocks + # Pop scopes whose indentation is greater than or equal to the current line's indentation + while len(stack) > 1 and stack[-1][0] >= current_indent: + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None # Terminate the chain + + # Get the current active scope from the stack + current_scope_indent, current_owner_block_id, last_block_in_current_chain = stack[-1] + + # Classify the statement and create the block + stmt_for_parse = stripped_line.rstrip("then").strip() + opcode, ntype = classify(stmt_for_parse) + + if opcode is None: # Should not happen if classify is robust + i += 1 + continue + + # Create the new block (and register it in all_generated_blocks) + # _register_block now only sets parent for shadow/input blocks; main block parent/next/topLevel set here. + key = _register_block(opcode, None, False, pick_key, all_generated_blocks) + info = all_generated_blocks[key] + + # Set parent, next, and topLevel for the main script blocks + if ntype == "hat": + info["parent"] = None + info["topLevel"] = True + top_level_script_keys.append(key) + # info["next"] = None # REMOVED: This was causing the hat block's 'next' to be None + # Push a new scope for the children of this hat block. + stack.append((current_indent, key, None)) # New scope: owner is this hat, no last block yet + else: # Stack block or C-block (that is part of a linear sequence) + if last_block_in_current_chain: + # This block's parent is the previous block in the chain + info["parent"] = last_block_in_current_chain + all_generated_blocks[last_block_in_current_chain]["next"] = key + else: + # This is the first block in a new linear chain (e.g., first block inside a forever loop) + # Its parent is the owner of the current scope (the C-block or Hat block) + info["parent"] = current_owner_block_id + + # If the owner is a C-block or procedure definition, link its SUBSTACK input + if current_owner_block_id and (all_generated_blocks[current_owner_block_id]["block_shape"] == "C-Block" or all_generated_blocks[current_owner_block_id]["op_code"] == "procedures_definition"): + owner_block = all_generated_blocks[current_owner_block_id] + if owner_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in owner_block["inputs"] and \ + owner_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in owner_block["inputs"]: + owner_block["inputs"]["SUBSTACK2"] = [2, key] + else: + owner_block["inputs"]["SUBSTACK"] = [2, key] + elif current_owner_block_id and all_generated_blocks[current_owner_block_id]["block_shape"] == "Hat Block": + # If the owner is a Hat block, this is its first child + all_generated_blocks[current_owner_block_id]["next"] = key + + info["topLevel"] = False + info["next"] = None # Default, will be overwritten if there's a next block + + # If it's a C-block or define block, it also starts a new inner scope + if ntype == "c_block" or opcode == "procedures_definition": + # Update the current scope's last_block_in_current_chain to this C-block + stack[-1] = (current_scope_indent, current_owner_block_id, key) + # Push a new scope for the C-block's substack + stack.append((current_indent, key, None)) # New scope: owner is this C-block, no last block yet + else: + # For regular stack blocks, just update the last_block_in_current_chain for the current scope + stack[-1] = (current_scope_indent, current_owner_block_id, key) + + # Parse inputs and fields (this part remains largely the same, but ensure parse_reporter_or_value/parse_condition + # are passed the *newly created block's ID* as the parent_key for nested inputs) + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if opcode == "motion_movesteps": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["STEPS"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_turnright" or opcode == "motion_turnleft": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DEGREES"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_gotoxy": + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_glidesecstoxy": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt_for_parse, re.IGNORECASE) + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_secs: info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_pointindirection": + m = re.search(r"direction\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DIRECTION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_changexby", "motion_changeyby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DX" if opcode == "motion_changexby" else "DY"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_setx", "motion_sety"]: + m = re.search(r"(?:set x to|set y to)\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["X" if opcode == "motion_setx" else "Y"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_changesizeby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_setsizeto": + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SIZE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_changeeffectby", "sound_changeeffectby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE" if opcode == "looks_changeeffectby" else "VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE" if opcode == "looks_seteffectto" else "VOLUME"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["NUM"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "control_wait": + m = re.search(r"wait\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DURATION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "control_repeat": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["TIMES"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "data_changevariableby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "data_deleteoflist": + m = re.search(r"delete\s*\((.+?)\)\s*of", stmt_for_parse, re.IGNORECASE) + if m: + val_str = m.group(1).strip() + if val_str.isdigit(): + info["inputs"]["INDEX"] = {"kind": "value", "value": int(val_str)} + else: + info["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + elif opcode == "data_insertatlist": + m_item = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt_for_parse, re.IGNORECASE) + m_index = re.search(r"at\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + elif opcode == "data_replaceitemoflist": + m_index = re.search(r"replace item\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + m_item = re.search(r"with\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + elif opcode == "event_whengreaterthan": + m = re.search(r">\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + + # String inputs + elif opcode == "looks_sayforsecs": + m = re.search(r"say\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_say": + m = re.search(r"say\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = parse_reporter_or_value(m.group(1).strip(), key, pick_key, all_generated_blocks) + elif opcode == "looks_thinkforsecs": + m = re.search(r"think\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_think": + m = re.search(r"think\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "sensing_askandwait": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["QUESTION"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["ITEM"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_setvariableto": + m_var = re.search(r"set\s*\[([^\]]+)\s*v\]\s*to\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m_var: + var_name = m_var.group(1).strip() + value_str = m_var.group(2).strip() + info["fields"]["VARIABLE"] = [var_name, None] + info["inputs"]["VALUE"] = parse_reporter_or_value(value_str, key, pick_key, all_generated_blocks) + + # Dropdown/Menu inputs (UPDATED) + elif opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "random position": option_val = "_random_" + elif option == "mouse-pointer": option_val = "_mouse_" + else: option_val = option + + menu_block_id = _register_block("motion_goto_menu", key, True, pick_key, all_generated_blocks, fields={"TO": [option_val, None]}) + info["inputs"]["TO"] = [1, menu_block_id] + elif opcode == "motion_glideto": + #m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*(?:\[([^\]]+)\s*v\]|\((.+?)\))", stmt_for_parse, re.IGNORECASE) + if m_secs: + info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + # Use group 3 for [random position v] or group 4 for (random position) + option = (m_secs.group(3) or m_secs.group(4)).strip() + if option == "random position": option_val = "_random_" + elif option == "mouse-pointer": option_val = "_mouse_" + else: option_val = option + + menu_block_id = _register_block("motion_glideto_menu", key, True, pick_key, all_generated_blocks, fields={"TO": [option_val, None]}) + info["inputs"]["TO"] = [1, menu_block_id] + elif opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option_val = "_mouse_" + else: option_val = option + + menu_block_id = _register_block("motion_pointtowards_menu", key, True, pick_key, all_generated_blocks, fields={"TOWARDS": [option_val, None]}) + info["inputs"]["TOWARDS"] = [1, menu_block_id] + elif opcode == "sensing_keypressed": + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", key, True, pick_key, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) + info["inputs"]["KEY_OPTION"] = [1, menu_block_id] + elif opcode == "sensing_touchingobject": + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option_val = "_mouse_" + elif option == "edge": option_val = "_edge_" + else: option_val = option + + menu_block_id = _register_block("sensing_touchingobjectmenu", key, True, pick_key, all_generated_blocks, fields={"TOUCHINGOBJECTMENU": [option_val, None]}) + info["inputs"]["TOUCHINGOBJECTMENU"] = [1, menu_block_id] + elif opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "myself": option_val = "_myself_" + else: option_val = option + + menu_block_id = _register_block("control_create_clone_of_menu", key, True, pick_key, all_generated_blocks, fields={"CLONE_OPTION": [option_val, None]}) + info["inputs"]["CLONE_OPTION"] = [1, menu_block_id] + elif opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sound_sounds_menu", key, True, pick_key, all_generated_blocks, fields={"SOUND_MENU": [option, None]}) + info["inputs"]["SOUND_MENU"] = [1, menu_block_id] + elif opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("looks_costume", key, True, pick_key, all_generated_blocks, fields={"COSTUME": [option, None]}) + info["inputs"]["COSTUME"] = [1, menu_block_id] + elif opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait"]: + m = re.search(r"switch backdrop to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("looks_backdrops", key, True, pick_key, all_generated_blocks, fields={"BACKDROP": [option, None]}) + info["inputs"]["BACKDROP"] = [1, menu_block_id] + elif opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + # Broadcast input doesn't use a separate menu block in definitions, it's a direct menu field in the input. + # So, it should be [1, [11, "message1", "id"]] or [1, [12, "message1"]] + # For now, let's keep it simple as [1, [11, option, None]] or similar if the definition allows. + # The `all_block_definitions` has `[1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]` + # Let's use that format, but without the specific ID for now. + info["inputs"]["BROADCAST_INPUT"] = [1, [11, option, None]] # Or just [1, [12, option]] if it's a simple string + + # Conditional inputs (Boolean blocks) + elif opcode in ["control_if", "control_if_else", "control_wait_until", "control_repeat_until"]: + #cond_match = re.search(r"<(.*?)>", stmt_for_parse) + cond_match = extract_condition_balanced(stmt_for_parse) + print(f"[THE CONDA MATCH]------------->{cond_match}") + if cond_match: + # Pass current block's key as parent for nested condition + info["inputs"]["CONDITION"] = parse_condition(cond_match.strip(), key, pick_key, all_generated_blocks) + elif opcode in ["operator_and", "operator_or", "operator_not", "operator_contains", + "sensing_touchingcolor", "sensing_coloristouchingcolor", "sensing_mousedown"]: + pass + + + # Fields parsing + if "VARIABLE" in info["fields"]: + m = re.search(r"\[([^\]]+)\s*v\]", stmt_for_parse) + if m: + var_name = m.group(1).strip() + info["fields"]["VARIABLE"] = [var_name, None] + if "LIST" in info["fields"]: + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["LIST"] = [m.group(1), None] + if "STOP_OPTION" in info["fields"]: + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STOP_OPTION"] = [m.group(1), None] + if "STYLE" in info["fields"]: + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STYLE"] = [m.group(1), None] + if "DRAG_MODE" in info["fields"]: + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["DRAG_MODE"] = [m.group(1), None] + if "EFFECT" in info["fields"] and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["EFFECT"] = [m.group(1).upper(), None] + if "NUMBER_NAME" in info["fields"] and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["NUMBER_NAME"] = [m.group(1), None] + if "FRONT_BACK" in info["fields"] and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FRONT_BACK"] = [m.group(1), None] + if "FORWARD_BACKWARD" in info["fields"] and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + if "OPERATOR" in info["fields"] and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["OPERATOR"] = [m.group(1).upper(), None] + if "CURRENTMENU" in info["fields"] and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + if "PROPERTY" in info["fields"] and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt_for_parse, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + info["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + if "WHENGREATERTHANMENU" in info["fields"] and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + if "KEY_OPTION" in info["fields"] and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["KEY_OPTION"] = [m.group(1), None] + if "BACKDROP" in info["fields"] and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BACKDROP"] = [m.group(1), None] + if "BROADCAST_OPTION" in info["fields"] and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + # Custom block specific parsing + if opcode == "procedures_definition": + proc_def_match = re.match(r"(?:define|procedure)\s+([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))?", stmt_for_parse, re.IGNORECASE) + if proc_def_match: + proc_name = proc_def_match.group(1).strip() + args_str = proc_def_match.group(2) + info["procedure_name"] = proc_name + info["is_custom_definition"] = True + + mutation_block = { + "tagName": "mutation", + "children": [], + "proccode": proc_name, + "argumentids": [], + "argumentnames": [], + "argumentdefaults": [], + "warp": False # Assuming non-warp by default + } + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for arg in args: + arg_id = f"%s" # Scratch uses %s for string args, %n for number args + # For simplicity, we'll just use a generic ID for now, or match Scratch's pattern + # For the plan, we just need the names and order. + mutation_block["argumentids"].append(arg_id) + mutation_block["argumentnames"].append(arg) + mutation_block["argumentdefaults"].append("") + + info["mutation"] = mutation_block + + elif opcode == "procedures_call": + call_match = re.match(r"(?:call\s+)?([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", stmt_for_parse, re.IGNORECASE) + if call_match: + custom_block_name = call_match.group(1).strip() + args_str = call_match.group(2) + info["custom_block_name"] = custom_block_name + + info["mutation"] = { + "tagName": "mutation", + "children": [], + "proccode": custom_block_name, + "argumentids": [], + "argumentnames": [], + "warp": False + } + + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for idx, arg_val_str in enumerate(args): + arg_input_name = f"argument_name_{idx+1}" + info["mutation"]["argumentids"].append(arg_input_name) # Use the input name as argument ID + info["mutation"]["argumentnames"].append(f"arg{idx+1}") # Placeholder name for mutation + + info["inputs"][arg_input_name] = parse_reporter_or_value(arg_val_str, key, pick_key, all_generated_blocks) # Pass current block's key + + i += 1 # Move to the next line + + # Final pass to ensure last blocks have next: None (already handled by stack pops) + # The build_script_flow function will correctly traverse the linked list. + while len(stack) > 1: # Keep the initial sentinel + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + print(f"[ALL OPCODE BLCOKS KEY 2]: {all_generated_blocks}") + with open("all_generated_blocks.json", "w") as f: + json.dump(all_generated_blocks, f, indent=2) + # Construct the final flow output based on the collected top-level keys + # Recursively build the block structure for each top-level script + def build_script_flow(current_block_key, visited=None): + if visited is None: + visited = set() + + script_flow = [] + current_iter_key = current_block_key + + while current_iter_key: + # Detect cyclic reference + if current_iter_key in visited: + script_flow.append({ + "block_key": current_iter_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected, stopping recursion" + }) + break + + visited.add(current_iter_key) + block = all_generated_blocks.get(current_iter_key) + if not block: + break # Should not happen if keys are correct + + output_block = { + "block_key": block["id"], + "opcode": block["op_code"], + "type": block["block_shape"].replace(" Block", "").lower().replace("c-", "c_"), + "inputs": {}, + "fields": {}, + "shadow": block.get("shadow"), + "topLevel": block.get("topLevel"), + "parent": block.get("parent"), + "next": block.get("next") + } + + # Handle all input types + for inp_name, inp_val in block.get("inputs", {}).items(): + if inp_name in ["SUBSTACK", "SUBSTACK2"]: + if inp_val and len(inp_val) > 1 and inp_val[1] in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(inp_val[1], visited.copy()) + else: + output_block["inputs"][inp_name] = [] + elif inp_name == "PROCCONTAINER" and block.get("is_custom_definition"): + output_block["inputs"][inp_name] = inp_val + elif isinstance(inp_val, dict) and inp_val.get("kind") == "block": + # Recursively build nested reporter/boolean blocks + nested_block_key = inp_val["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val # Keep original if not found (shouldn't happen) + elif isinstance(inp_val, dict) and inp_val.get("kind") == "nested_reporter": + nested_block_key = inp_val["reporter"]["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val + else: + output_block["inputs"][inp_name] = inp_val + + for field_name, field_val in block.get("fields", {}).items(): + output_block["fields"][field_name] = field_val + + if block.get("custom_block_name"): + output_block["custom_block_name"] = block["custom_block_name"] + + if block.get("procedure_name"): + output_block["procedure_name"] = block["procedure_name"] + output_block["is_custom_definition"] = True + if "mutation" in block: # Include mutation for custom definitions + output_block["mutation"] = block["mutation"] + + + script_flow.append(output_block) + + # Proceed to the next block in sequence + next_key = block.get("next") + if next_key in visited: + script_flow.append({ + "block_key": next_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected in 'next' pointer" + }) + break + + current_iter_key = next_key + + return script_flow + + final_flow_output = [] + for key in top_level_script_keys: + final_flow_output.extend(build_script_flow(key)) + return {"flow": final_flow_output} + +# Example input with opcodes for the initial generation + +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_xposition","count":1}, + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":2}, + {"opcode":"control_stop","count":1}, + {"opcode":"operator_lt","count":1}, + {"opcode":"sensing_touchingobject","count":1}, + #{"opcode":"sensing_touchingobjectmenu","count":1}, + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":2}, + {"opcode":"data_showvariable","count":2}, +] + +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) +with open("generated_output_json.json", "w") as f: + json.dump(generated_output_json, f, indent=2) + +pseudo_code_examples = [""" +when green flag clicked +go to [random position v] +""",] + +pseudo_code_examples = [""" +when green flag clicked + set [score v] to (1) + go to x: (240) y: (-135) + set [speed v] to (1) + show variable [score v] + show variable [speed v] + forever + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end +end +""",] +# set [score v] to ((backdrop) of [Stage v]) + # glide (1) secs to ([random position v]) + # point towards [mouse-pointer v] + # if then + # broadcast [shoot v] + # end + # create clone of [myself v] + # play sound [Meow v] until done + # set [score v] to ([Stage v]) + # switch costume to [costume1 v] + # switch backdrop to [backdrop1 v] +txt="" +trace="" +# Process each example and print the plan +for i, pseudo_code_input in enumerate(pseudo_code_examples): + print(f"\n--- Processing Example {i+1} ---") + #print(pseudo_code_input.strip()) + try: + # Regenerate blocks and opcode_occurrences for each run to ensure fresh keys + # This is important because pick_key uses a defaultdict that persists state. + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) + #print(json.dumps(plan, indent=2)) + txt += str(plan) + " \n" + except Exception as e: + #print(f"Error processing example: {e}") + import traceback + #traceback.print_exc() + trace += str(e) +" "+str(pseudo_code_input)+" \n" + +with open("all_analysis.txt", "w", encoding="utf-8") as f: + f.write(txt) + +with open("all_analysis_trace.txt", "w", encoding="utf-8") as f: + f.write(trace) diff --git a/utils/plan_generator_11.py b/utils/plan_generator_11.py new file mode 100644 index 0000000000000000000000000000000000000000..44070668a7c44892e985eef93c3cd7dbe0e49c8a --- /dev/null +++ b/utils/plan_generator_11.py @@ -0,0 +1,3066 @@ +import json +import copy +import re +from collections import defaultdict + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + # Initialize parent, next, topLevel to None/False. These will be set correctly in generate_plan. + main_block_data["id"] = main_key # Ensure ID is present + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = False # Default to False, Hat blocks will override + main_block_data["shadow"] = False + + # Ensure inputs and fields are dictionaries, even if they were None or list in definition + if "inputs" not in main_block_data or not isinstance(main_block_data["inputs"], dict): + main_block_data["inputs"] = {} + if "fields" not in main_block_data or not isinstance(main_block_data["fields"], dict): + main_block_data["fields"] = {} + if "sub_stacks" not in main_block_data or not isinstance(main_block_data["sub_stacks"], dict): + main_block_data["sub_stacks"] = {} + + + generated_blocks[main_key] = main_block_data + + # Handle menus (shadow blocks) + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["id"] = menu_key # Ensure ID is present + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None # Shadow blocks never have a 'next' + menu_block_data["parent"] = main_key # Parent is the main block it's an input for + + # Ensure inputs and fields are dictionaries for menu blocks too + if "inputs" not in menu_block_data or not isinstance(menu_block_data["inputs"], dict): + menu_block_data["inputs"] = {} + if "fields" not in menu_block_data or not isinstance(menu_block_data["fields"], dict): + menu_block_data["fields"] = {} + if "sub_stacks" not in menu_block_data or not isinstance(menu_block_data["sub_stacks"], dict): + menu_block_data["sub_stacks"] = {} + + # Link the main block's input to the shadow block's key + # This assumes the input structure is [type_code, block_id_or_value] + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 0: # Check for at least type_code + # Assuming type code 1 for value input that takes a block + main_block_data["inputs"][input_name][1] = menu_key + else: # If input not defined as list, or empty, set it + main_block_data["inputs"][input_name] = [1, menu_key] # Default to value input type + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +# Consolidated block definitions from all JSON files +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_distanceto": { # Added sensing_distanceto + "block_name": "(distance to ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto", + "functionality": "Reports the distance from the sprite to the mouse-pointer or another specified sprite.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": False, "topLevel": True + }, + "sensing_distanceto_menu": { # Added sensing_distanceto_menu (though its use is now commented out in parse_reporter_or_value) + "block_name": "distance to menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto_menu", + "functionality": "Menu for distance to block.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, "sub_stacks": {"SUBSTACK": [2, None]} + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, "sub_stacks": {"SUBSTACK": [2, None]} + }, + "control_if": { + "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": [2, None]}, "fields": {}, "shadow": False, "topLevel": True, "sub_stacks": {"SUBSTACK": [2, None]} + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True, "sub_stacks": {"SUBSTACK": [2, None], "SUBSTACK2": [2, None]} + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input] ", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True, "sub_stacks": {"SUBSTACK": [2, None]} + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True, + "sub_stacks": {"SUBSTACK": [2, None]} # Custom blocks can have a substack + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True + } +} + +def unparen(s): + s = s.strip() + # keep peeling off *all* matching outer parens + while True: + m = re.fullmatch(r"\((.*)\)", s) + if not m: + break + s = m.group(1).strip() + return s + +def _register_block(opcode, parent_key, is_shadow, pick_key_func, all_blocks_dict, inputs=None, fields=None, sub_stacks=None): + """ + Helper to create and register a block in the all_blocks_dict. + It uses pick_key_func to get a unique ID. + Parent, next, and topLevel are NOT set here for main blocks; they are handled in generate_plan. + For shadow blocks, parent is set here. + """ + key = pick_key_func(opcode) + block_data = copy.deepcopy(all_block_definitions[opcode]) + block_data["id"] = key + block_data["parent"] = parent_key if is_shadow else None # Only set parent for shadow blocks here + block_data["next"] = None # Default, will be set in generate_plan + block_data["topLevel"] = not is_shadow # Shadow blocks are not top-level + block_data["shadow"] = is_shadow + + # Ensure inputs, fields, and sub_stacks are dictionaries + if "inputs" not in block_data or not isinstance(block_data["inputs"], dict): + block_data["inputs"] = {} + if "fields" not in block_data or not isinstance(block_data["fields"], dict): + block_data["fields"] = {} + if "sub_stacks" not in block_data or not isinstance(block_data["sub_stacks"], dict): + block_data["sub_stacks"] = {} + + if inputs: + for inp_name, inp_val in inputs.items(): + # If the input is a block, ensure it's stored as a block ID reference + if isinstance(inp_val, dict) and inp_val.get("kind") == "block": + block_data["inputs"][inp_name] = [2, inp_val["block"]] # Type 2 for block input + elif isinstance(inp_val, dict) and inp_val.get("kind") == "value": + # For literal values, store as type 1 and the value itself + # Need to map to Scratch's internal value types if necessary, e.g., [4, "10"] for number + # For simplicity, we'll use a generic type 1 and the value for now. + # A more robust solution would map `type` from parse_reporter_or_value to Scratch's internal codes. + block_data["inputs"][inp_name] = [1, str(inp_val["value"])] # Store as string for now + else: + block_data["inputs"][inp_name] = inp_val # Fallback for other formats + + if fields: + block_data["fields"].update(fields) + + if sub_stacks: + block_data["sub_stacks"].update(sub_stacks) + + all_blocks_dict[key] = block_data + return key + +def _auto_balance(text): + # if there are more "(" than ")", append the missing ")" + diff = text.count("(") - text.count(")") + if diff > 0: + text = text + ")"*diff + # same for square brackets + diff = text.count("[") - text.count("]") + if diff > 0: + text = text + "]"*diff + return text + +def strip_outer_angle_brackets(text): + """ + Strip exactly one balanced pair of outer <...> brackets, only if they wrap the whole string. + """ + text = text.strip() + if text.startswith("<") and text.endswith(">"): + depth = 0 + for i, char in enumerate(text): + if char == '<': + depth += 1 + elif char == '>': + depth -= 1 + if depth == 0 and i == len(text) - 1: + return text[1:-1].strip() + # If we exit the loop and depth is 0, it means the outer brackets were balanced and wrapped the whole string + if depth == 0: + return text[1:-1].strip() + return text + +def extract_condition_balanced(stmt): + # 1. Remove "if" and "then" + stmt = stmt.strip() + if stmt.lower().startswith("if "): + stmt = stmt[3:].strip() + if stmt.lower().startswith("repeat until"): + stmt = stmt[12:].strip() + if stmt.lower().startswith("wait until "): + stmt = stmt[11:].strip() + if stmt.lower().endswith(" then"): + stmt = stmt[:-5].strip() + + # Helper to detect and strip single outer balanced angle brackets + def unwrap_balanced(s): + if s.startswith("<") and s.endswith(">"): + depth = 0 + for i in range(len(s)): + if s[i] == "<": + depth += 1 + elif s[i] == ">": + depth -= 1 + if depth == 0 and i < len(s) - 1: + return s # Early balance → not a single outer wrapper + if depth == 0: + return s[1:-1].strip() + return s + + # Recursively simplify things like > to not + def simplify(s): + s = unwrap_balanced(s) + s = s.strip() + + # Match > pattern + m = re.fullmatch(r"not\s*<(.+)>", s, re.IGNORECASE) + if m: + inner = m.group(1).strip() + inner = simplify(inner) + return f"not <{inner}>" + + # Match comparison operators like <(x position) < (100)> + # This part might be redundant if the main parser handles it, but good for internal consistency + m_comp = re.fullmatch(r"<\s*\(([^<>]+?)\)\s*([<>=])\s*\(([^<>]+?)\)\s*>", stmt) + if m_comp: + return f"({m_comp.group(1).strip()}) {m_comp.group(2)} ({m_comp.group(3).strip()})" + + return s + + return simplify(stmt) + +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, parent_key, pick_key_func, all_generated_blocks): + #print (f"text recived at parse_reporter_or_value _auto_balance here:----------------->: {text}") + text = _auto_balance(text.strip()) + #text = unparen(text.strip()) + print (f"text recived at parse_reporter_or_value unparen here:----------------->: {text}") + text = strip_outer_angle_brackets(text) + # Check for numeric literal (including parenthesized numbers like "(0)" or "(10)") + print (f"text recived at parse_reporter_or_value here:----------------->: {text}") + + m_num = re.fullmatch(r"\(?\s*(-?\d+(\.\d+)?)\s*\)?", text) + if m_num: + val_str = m_num.group(1) + return {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # Variable reporter: [score v], [health v], etc. + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, + fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + + # Now catch other bracketed values as literal strings + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + # Check for simple reporters, potentially with outer parentheses + m_simple_reporter = re.fullmatch(r"\((.+?)\)", text) + if m_simple_reporter: + inner_text = m_simple_reporter.group(1).strip() + if inner_text in simple_reporters: + block_id = _register_block(simple_reporters[inner_text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + # Also check for simple reporters without parentheses (e.g., if passed directly) + if text in simple_reporters: + block_id = _register_block(simple_reporters[text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + + # Variable reporter: [score v] or (score) or just "score" + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + # Ensure it's not a simple reporter already handled, or a number + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [potential_var_name, None]}) + return {"kind": "block", "block": block_id} + # Handle plain variable names like "score", "number 1", "total score" + if re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_ ]*", text): # Allow spaces for "number 1" etc. + # Exclude known simple reporters that don't have 'v' or parentheses + if text not in simple_reporters: + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [text, None]}) + return {"kind": "block", "block": block_id} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + return {"kind": "block", "block": block_id} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) # Parent will be set later + max_val_obj = parse_reporter_or_value(m.group(2).strip(), parent_key, pick_key_func, all_generated_blocks) # Parent will be set later + inputs = {"FROM": min_val_obj, "TO": max_val_obj} + block_id = _register_block("operator_random", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if min_val_obj.get("kind") == "block": all_generated_blocks[min_val_obj["block"]]["parent"] = block_id + if max_val_obj.get("kind") == "block": all_generated_blocks[max_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (join ()()) (operator_join) - handle both [] and () for inputs + + #m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s+(\[.+?\]|\(.+?\))", text) # Try (val) (val) + m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s*(\[.+?\]|\(.+?\))", text) + if m: + part1_txt = m.group(1).strip() + part2_txt = m.group(2).strip() + str1_obj = parse_reporter_or_value(part1_txt, parent_key, pick_key_func, all_generated_blocks) + str2_obj = parse_reporter_or_value(part2_txt, parent_key, pick_key_func, all_generated_blocks) + inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + block_id = _register_block("operator_join", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs) + # set parents if nested blocks + for obj in (str1_obj, str2_obj): + if obj.get("kind") == "block": + all_generated_blocks[obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # letter () of () (operator_letterof) - handle both [] and () for inputs + m = re.search(r"letter \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + string_val_obj = parse_reporter_or_value(m.group(2).strip(), parent_key, pick_key_func, all_generated_blocks) + inputs = {"LETTER": index_obj, "STRING": string_val_obj} + block_id = _register_block("operator_letterof", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + if string_val_obj.get("kind") == "block": all_generated_blocks[string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (length of ()) (operator_length) - handle both [] and () for inputs + #m = re.search(r"length of \((.+?)\)", text) + m = re.search(r"length of\s*(?:\((.+?)\)|\[(.+?)\])", text) + if not m: + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + arg_txt = (m.group(1) or m.group(2)).strip() + list_or_string_val_obj = parse_reporter_or_value(arg_txt, parent_key, pick_key_func, all_generated_blocks) + #list_or_string_val_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + inputs = {"STRING": list_or_string_val_obj} + block_id = _register_block("operator_length", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if list_or_string_val_obj.get("kind") == "block": all_generated_blocks[list_or_string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (() mod ()) (operator_mod) + # m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + m = re.search(r"\[([^\]]+)\s*v\]\s*mod\s*\(?\s*(.+?)\s*\)?", text) + if m: + num1_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + num2_obj = parse_reporter_or_value(m.group(2).strip(), parent_key, pick_key_func, all_generated_blocks) + inputs = {"NUM1": num1_obj, "NUM2": num2_obj} + block_id = _register_block("operator_mod", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num1_obj.get("kind") == "block": all_generated_blocks[num1_obj["block"]]["parent"] = block_id + if num2_obj.get("kind") == "block": all_generated_blocks[num2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + inputs = {"NUM": num_obj} + block_id = _register_block("operator_round", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num_obj.get("kind") == "block": all_generated_blocks[num_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (() of ()) (operator_mathop) - handle variable for function type + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) # e.g. [sqrt v] of ((x pos) * (x pos)) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), parent_key, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + # Also handle direct string for function type (e.g., "abs of (x)") + m = re.search(r"([a-zA-Z]+)\s*of\s*\((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), parent_key, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + # This regex is designed to handle nested parentheses correctly. + # It looks for an opening parenthesis, then non-parenthesis characters or balanced parentheses, + # followed by an operator, and then the second operand. + # This is a simplified approach; a full-fledged parser would use a stack. + # arithmetic_match = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + # arithmetic_match = re.search(r"\(?\s*(.+?)\s*\)?\s*([\+\-\*/])\s*\(?\s*(.+?)\s*\)?", text) + # if not arithmetic_match: + # # Try to match without outer parentheses for the operands, but still with an operator + # arithmetic_match = re.search(r"(.+?)\s*([+\-*/])\s*(.+)", text) + + # if arithmetic_match: + # op1_str = arithmetic_match.group(1).strip() + # operator_symbol = arithmetic_match.group(2).strip() + # op2_str = arithmetic_match.group(3).strip() + # print() + # op1_obj = parse_reporter_or_value(op1_str, parent_key, pick_key_func, all_generated_blocks) + # op2_obj = parse_reporter_or_value(op2_str, parent_key, pick_key_func, all_generated_blocks) + + # opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + # inputs = {"NUM1": op1_obj, "NUM2": op2_obj} + # block_id = _register_block(opcode_map[operator_symbol], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # if op1_obj.get("kind") == "block": all_generated_blocks[op1_obj["block"]]["parent"] = block_id + # if op2_obj.get("kind") == "block": all_generated_blocks[op2_obj["block"]]["parent"] = block_id + # return {"kind": "block", "block": block_id} + _strip_outer = re.compile(r"^\s*(?P[\(\[]+)\s*(?P.*\S\s*(?P=open)\s*$", re.VERBOSE) + _expr = re.compile(r"^\s*(?P[^\s\(\)\[\]]+)\s*(?P[+\-*/])\s*(?P[^\s\(\)\[\]]+)\s*$", re.VERBOSE) + #m_art = re.compile(r"\(?\s*([^\s()]+)\s*\)?\s*([+\-*/])\s*\(?\s*([^\s()]+)\s*\)?", re.VERBOSE) + if text: + expr = text + # strip *all* matching layers of () or [] + while True: + m = _strip_outer.match(expr) + if not m: + break + expr = m.group("inner") + + # now match the core "left op right" + m = _expr.match(expr) + if not m: + return None + + left_str = m.group("left") + operator = m.group("op") + right_str = m.group("right") + + # your existing parse & block‑creation logic + op1_obj = parse_reporter_or_value(left_str, parent_key, pick_key_func, all_generated_blocks) + op2_obj = parse_reporter_or_value(right_str, parent_key, pick_key_func, all_generated_blocks) + + opcode_map = { + '+': 'operator_add', + '-': 'operator_subtract', + '*': 'operator_multiply', + '/': 'operator_divide', + } + inputs = {"NUM1": op1_obj, "NUM2": op2_obj} + block_id = _register_block( + opcode_map[operator], parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs + ) + + if op1_obj.get("kind") == "block": + all_generated_blocks[op1_obj["block"]]["parent"] = block_id + if op2_obj.get("kind") == "block": + all_generated_blocks[op2_obj["block"]]["parent"] = block_id + + return {"kind": "block", "block": block_id} + + + # (costume ()) (looks_costumenumbername) - handle with or without 'v' + m = re.search(r"costume \((.+?)\)", text) + if not m: + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_costumenumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (backdrop ()) (looks_backdropnumbername) - handle with or without 'v' + m = re.search(r"backdrop \((.+?)\)", text) + if not m: + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_backdropnumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (distance to ()) (sensing_distanceto) - handle with or without 'v' + m = re.search(r"distance to \((.+?)\)", text) + if not m: + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + + # This block has a dropdown FIELD, not an input that links to a shadow block + fields = {"TARGET": [target_val, None]} + block_id = _register_block("sensing_distanceto", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (current ()) (sensing_current) - handle with or without 'v' + m = re.search(r"current \((.+?)\)", text) + if not m: + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + fields = {"CURRENTMENU": [unit.upper(), None]} + block_id = _register_block("sensing_current", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (() of ()) (sensing_of) - Corrected logic + #m = re.search(r"\((.+?)\)\s*of\s*(?:\((.+?)\)|\[(.+?)\s*v\])", text) + m = re.search(r"\((.+?)\)\s*of\s*(?:\((.+?)\)|\[(.+?)\s*v\])", text) + if m: + prop_str = m.group(1).strip() + obj = (m.group(2) or m.group(3)).strip() + + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + property_value = prop_map.get(prop_str, prop_str) + + if obj.lower() == "stage": obj_val = "_stage_" + elif obj.lower() == "myself": obj_val = "_myself_" + else: obj_val = obj + + # Create the sensing_of_object_menu shadow block + object_menu_id = _register_block("sensing_of_object_menu", parent_key, True, pick_key_func, all_generated_blocks, fields={"OBJECT": [obj_val, None]}) + + # Create the main sensing_of block + inputs = {"OBJECT": {"kind": "block", "block": object_menu_id}} # Link input to the shadow block ID + fields = {"PROPERTY": [property_value, None]} # PROPERTY is a field of the main block + + block_id = _register_block("sensing_of", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + all_generated_blocks[object_menu_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item (index) of [list v]) (data_itemoflist) + m = re.search(r"item \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + # Create data_list shadow block for the list name + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + + inputs = {"INDEX": index_obj, "LIST": {"kind": "block", "block": list_block_id}} + fields = {} # No fields in data_itemoflist itself for the list name + block_id = _register_block("data_itemoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item # of [item] in [list v]) (data_itemnumoflist) + m = re.search(r"item # of \((.+?)\) in \((.+?)\)", text) + if not m: + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + # Create data_list shadow block for the list name + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + + inputs = {"ITEM": item_obj, "LIST": {"kind": "block", "block": list_block_id}} + fields = {} # No fields in data_itemnumoflist itself for the list name + block_id = _register_block("data_itemnumoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if item_obj.get("kind") == "block": all_generated_blocks[item_obj["block"]]["parent"] = block_id + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + raise ValueError(f"Can't parse reporter or value: {text}") + +def parse_condition(stmt, parent_key, pick_key_func, all_generated_blocks): + """ + Parse Scratch-style boolean conditions, handling comparisons (<, =, >), + boolean operators (and, or, not), and other sensing conditions. + """ + s = stmt.strip() + s = extract_condition_balanced(s) + s_lower = s.lower() + print (f"text recived at parse_condition here:----------------->: {s_lower}") + # 1) Boolean NOT: `not <...>` + m_not = re.fullmatch(r"\s*(?:<\s*)?not\s+(.+?)(?:\s*>)?\s*", s_lower, re.IGNORECASE) + if m_not: + inner = m_not.group(1).strip() + inner_obj = parse_condition(inner, parent_key, pick_key_func, all_generated_blocks) + bid = _register_block("operator_not", parent_key, False, pick_key_func, all_generated_blocks, + inputs={"OPERAND": inner_obj}) + if inner_obj.get("kind") == "block": + all_generated_blocks[inner_obj["block"]]["parent"] = bid + return {"kind": "block", "block": bid} + + # 2) Boolean AND / OR + m_andor = re.fullmatch(r"\s*(.+?)\s+(and|or)\s+(.+?)\s*", s_lower, re.IGNORECASE) + if m_andor: + cond1_obj = parse_condition(m_andor.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + cond2_obj = parse_condition(m_andor.group(3).strip(), parent_key, pick_key_func, all_generated_blocks) + op_block = 'operator_and' if m_andor.group(2).lower() == 'and' else 'operator_or' + inputs = {"OPERAND1": cond1_obj, "OPERAND2": cond2_obj} + block_id = _register_block(op_block, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if cond1_obj.get("kind") == "block": all_generated_blocks[cond1_obj["block"]]["parent"] = block_id + if cond2_obj.get("kind") == "block": all_generated_blocks[cond2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # 1a) Comparisons with explicit angle wrappers: < (...) op (...) > + m = re.fullmatch( + r"\s*<\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*>\s*", + s, + re.VERBOSE + ) + if m: + left_txt, right_txt = m.group(1), m.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 1b) Simple comparisons without angle wrappers: A op B + m_simple = re.fullmatch(r"\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*", s) + if m_simple: + left_txt, right_txt = m_simple.group(1), m_simple.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m_simple.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 4) Contains: <[list v] contains [item]?> + m = re.fullmatch(r"\s*\[(.+?)\]\s+contains\s+\[(.+?)\]\?\s*", s) + if m: + list_name = m.group(1).strip() + item_val = {"kind": "value", "value": m.group(2).strip()} # Item can be a value or a block + + # Create the data_list reporter block + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + + inputs = {"LIST": {"kind": "block", "block": list_block_id}, "ITEM": item_val} + block_id = _register_block("data_listcontainsitem", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 5) Touching object: + m_touch = re.fullmatch(r""" + \s* # leading space + (?:<\s*)? # optional '<' + touching # literal + \s*\[\s* + (?P[^\]]+?) # capture the sprite name + \s*v\]\? # close the [sprite v]? + (?:\s*>)? # optional '>' + """, s_lower, re.IGNORECASE | re.VERBOSE) + if m_touch: + sprite = m_touch.group('sprite').strip() + val = {'mouse-pointer':'_mouse_', 'edge':'_edge_'}.get(sprite, sprite) + + mid = _register_block( + "sensing_touchingobjectmenu", parent_key, True, pick_key_func, all_generated_blocks, + fields={"TOUCHINGOBJECTMENU":[val, None]} + ) + bid = _register_block( + "sensing_touchingobject", parent_key, False, pick_key_func, all_generated_blocks, + inputs={"TOUCHINGOBJECTMENU":{"kind": "block", "block": mid}} # Link input to the shadow block ID + ) + all_generated_blocks[mid]["parent"] = bid + return {"kind":"block","block":bid} + + # 6) Touching color: + m = re.search(r"touching color \[(#[0-9A-Fa-f]{6})\]\?", s_lower) + if m: + inputs = {"COLOR": {"kind": "value", "value": m.group(1)}} + block_id = _register_block("sensing_touchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # 7) Color is touching color: + m = re.search(r"color \[(#[0-9A-Fa-f]{6})\] is touching \[(#[0-9A-Fa-f]{6})\]\?", s_lower) + if m: + inputs = {"COLOR1": {"kind": "value", "value": m.group(1)}, "COLOR2": {"kind": "value", "value": m.group(2)}} + block_id = _register_block("sensing_coloristouchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # 8) Key pressed: + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", s_lower) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", parent_key, True, pick_key_func, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) + + inputs = {"KEY_OPTION": {"kind": "block", "block": menu_block_id}} + block_id = _register_block("sensing_keypressed", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + all_generated_blocks[menu_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 9) Mouse down?: mouse down? + if s_lower == "mouse down?": + block_id = _register_block("sensing_mousedown", parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + val_obj = parse_reporter_or_value(unparen(stmt), parent_key, pick_key_func, all_generated_blocks) + if val_obj: + return val_obj + + raise ValueError(f"Can't parse condition: {stmt}") + +def classify(line): + """ + Classifies a pseudo-code line into its corresponding Scratch opcode and block type. + Order of checks matters: more specific patterns should come before more general ones. + """ + l = line.lower().strip() + + # Ignore comments + if l.startswith("//"): return None, None + + # Hat Blocks (most specific first) + if re.match(r"when green flag click(ed)?", l): return "event_whenflagclicked", "hat" + if re.match(r"when (.+?) key press(ed)?", l): return "event_whenkeypressed", "hat" + if re.match(r"when this sprite click(ed)?", l): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if re.match(r"when i start as a clo(ne)?", l): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + if l.startswith("procedure "): return "procedures_definition", "hat" # For "procedure moveBall" + + # Motion Blocks + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + # IMPORTANT: More specific glide block before less specific one + if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if re.match(r"set x to\s*\(.+\)", l): return "motion_setx", "stack" # Specific for set x + if l.startswith("change y by"): return "motion_changeyby", "stack" + if re.match(r"set y to\s*\(.+\)", l): return "motion_sety", "stack" # Specific for set y + #if re.match(r"if on edge, bounce( off edge)?", l): return "motion_ifonedgebounce", "stack" + if re.match(r"if on edge,\s*bounc(e)?(\s+off\s+edge)?", l.strip(), re.IGNORECASE): return "motion_ifonedgebounce", "stack" + if re.match(r"bounce off edg(e)?", l): return "motion_ifonedgebounce", "stack" # Alias + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + + + # Looks Blocks + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if re.match(r"next costum(e)?", l): return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + # Updated regex for change/set effect by/to + if re.match(r"change\s*(\[.+?v\]|\(.+?\))?\s*effect by", l): return "looks_changeeffectby", "stack" + if re.match(r"set\s*(\[.+?v\]|\(.+?\))?\s*effect to", l): return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + # Sound Blocks + if re.match(r"play sound (.+?) until do(ne)?", l): return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + # Event Blocks (broadcasts) + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + # Control Blocks + if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + # Updated regex for stop block to handle different options + if re.match(r"stop \[(all|this script|other scripts in sprite)\s*v\]", l): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + # Data Blocks + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + # Updated regex for delete of list + if re.match(r"delete \((.+?)\) of \[([^\]]+)\s*v\]", l): return "data_deleteoflist", "stack" + if l.startswith("delete all of [" ): return "data_deletealloflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + # Sensing Blocks + if re.match(r"ask (.+?) and wai(t)?", l): return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom Blocks (procedures_call) - specific rule for "call" + if l.startswith("call "): + return "procedures_call", "stack" + + # Custom Blocks (procedures_call) - LAST RESORT (generic match) + # This should be the very last check for stack-type blocks to avoid conflicts. + # It tries to match anything that looks like a function call with or without arguments. + custom_block_match = re.match(r"([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", l) + if custom_block_match: + # Before returning, ensure it's not a known simple reporter or variable name + # that might have been missed or is being used standalone. + # This is a heuristic; a full parser would be more robust. + potential_name = custom_block_match.group(1).strip() + if potential_name not in ["x position", "y position", "direction", "mouse x", "mouse y", "loudness", "timer", "days since 2000", "username", "answer", "size", "volume"] and \ + not re.fullmatch(r"\[[^\]]+\]", potential_name) and \ + not re.fullmatch(r"\[[^\]]+\]\s*v", potential_name): + return "procedures_call", "stack" + + + raise ValueError(f"Unknown statement: {line!r}") + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key -> block_data (pre-generated block definitions) + • opcode_keys: dict of opcode -> list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... list of block dictionaries ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + # This should ideally not happen if initial_opcode_counts is comprehensive + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + all_generated_blocks = {} # This will store the final, structured blocks + + # Populate all_generated_blocks with initial blocks, ensuring they have IDs + for key, block_data in generated_input.items(): + all_generated_blocks[key] = copy.deepcopy(block_data) + all_generated_blocks[key]["id"] = key # Ensure ID is set + + # Stack stores (indent, owner_block_id, last_block_in_current_linear_chain_id) + # owner_block_id: The ID of the C-block or Hat block that owns the current substack. + # last_block_in_current_linear_chain_id: The ID of the last block added to the *current linear sequence* within this substack. + stack = [(-2, None, None)] # Sentinel: (indent, owner_block_id, last_block_in_current_linear_chain_id) + # Using -2 for initial indent to be less than any valid indent (0 or more) + + top_level_script_keys = [] + + lines = pseudo_code.splitlines() + i = 0 + while i < len(lines): + raw_line = lines[i] + stripped_line = raw_line.strip() + + # Skip empty lines and comments + if not stripped_line or stripped_line.startswith("//"): + i += 1 + continue + + current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2 + + # Handle 'else' and 'end' first, as they control scope + if stripped_line.lower() == "else": + # Pop the 'then' substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # The 'if-else' block (popped_owner_key) is the owner. + # This 'else' must belong to the current owner on top of the stack (which should be the if-else block) + # Ensure the current_owner_block_id is indeed an if-else block + if popped_owner_key and all_generated_blocks[popped_owner_key]["op_code"] == "control_if_else": + # Push a new scope for the 'else' substack, with the same owner. + stack.append((current_indent, popped_owner_key, None)) # New scope for 'else' part, no last block yet + else: + # Error: 'else' without a preceding 'if' or incorrect nesting + print(f"Error: 'else' found without a corresponding 'if-else' block at line {i+1}") + # Attempt to recover by treating it as a regular block or skipping + stack.append((popped_indent, popped_owner_key, popped_last_block_in_chain)) # Put back what was popped + i += 1 + continue + + if stripped_line.lower() == "end": + # Pop the current substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # If the popped scope had an owner (C-block or procedure definition), + # and its substack input isn't already filled, set it to None (empty substack). + if popped_owner_key: + owner_block = all_generated_blocks[popped_owner_key] + if owner_block["block_shape"] == "C-Block" or owner_block["op_code"] == "procedures_definition": + # Determine which substack this 'end' is closing + # This logic needs to be smarter for if-else + if owner_block["op_code"] == "control_if_else": + # If we just popped the 'then' branch (SUBSTACK) + # and SUBSTACK2 is not yet set, then this 'end' closes SUBSTACK. + # If SUBSTACK2 was already set, this 'end' closes SUBSTACK2. + # This is tricky without explicit markers. We assume 'else' is always followed by 'end' for its substack. + # If SUBSTACK is filled and SUBSTACK2 is not, then this 'end' closes SUBSTACK. + # If SUBSTACK2 is filled, this 'end' closes SUBSTACK2. + # If neither is filled, it's an empty 'then' branch. + if owner_block["sub_stacks"].get("SUBSTACK") and not owner_block["sub_stacks"].get("SUBSTACK2"): + # This 'end' closes the first substack (then branch) + pass # Already handled by the stack logic + elif owner_block["sub_stacks"].get("SUBSTACK2"): + # This 'end' closes the second substack (else branch) + pass # Already handled by the stack logic + else: # Neither substack was filled, meaning an empty 'then' branch + # This should have been caught when the substack was initiated. + # For now, ensure it's not set to a block ID if it was empty. + if "SUBSTACK" in owner_block["sub_stacks"] and owner_block["sub_stacks"]["SUBSTACK"] is None: + pass # Already None + if "SUBSTACK2" in owner_block["sub_stacks"] and owner_block["sub_stacks"]["SUBSTACK2"] is None: + pass # Already None + elif not owner_block["sub_stacks"].get("SUBSTACK"): # Only set if not already set by a block + owner_block["sub_stacks"]["SUBSTACK"] = None # No blocks in substack + i += 1 + continue + + # Adjust stack based on indentation for regular blocks + # Pop scopes whose indentation is greater than or equal to the current line's indentation + while len(stack) > 1 and stack[-1][0] >= current_indent: + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None # Terminate the chain + + # Get the current active scope from the stack + current_scope_indent, current_owner_block_id, last_block_in_current_chain = stack[-1] + + # Classify the statement and create the block + stmt_for_parse = stripped_line.rstrip("then").strip() + opcode, ntype = classify(stmt_for_parse) + + if opcode is None: # Should not happen if classify is robust + i += 1 + continue + + # Create the new block (and register it in all_generated_blocks) + # _register_block now only sets parent for shadow/input blocks; main block parent/next/topLevel set here. + key = pick_key(opcode) + # Ensure the block exists in all_generated_blocks before trying to access it + if key not in all_generated_blocks: + # If not pre-generated, create a basic entry. This is a fallback. + all_generated_blocks[key] = copy.deepcopy(all_block_definitions.get(opcode, {})) + all_generated_blocks[key]["id"] = key + all_generated_blocks[key]["inputs"] = all_generated_blocks[key].get("inputs", {}) + all_generated_blocks[key]["fields"] = all_generated_blocks[key].get("fields", {}) + all_generated_blocks[key]["sub_stacks"] = all_generated_blocks[key].get("sub_stacks", {}) + + info = all_generated_blocks[key] + + # Set parent, next, and topLevel for the main script blocks + if ntype == "hat": + info["parent"] = None + info["topLevel"] = True + top_level_script_keys.append(key) + # Push a new scope for the children of this hat block. + stack.append((current_indent, key, None)) # New scope: owner is this hat, no last block yet + else: # Stack block or C-block (that is part of a linear sequence) + if last_block_in_current_chain: + # This block's parent is the previous block in the chain + info["parent"] = last_block_in_current_chain + all_generated_blocks[last_block_in_current_chain]["next"] = key + else: + # This is the first block in a new linear chain (e.g., first block inside a forever loop) + # Its parent is the owner of the current scope (the C-block or Hat block) + info["parent"] = current_owner_block_id + + # If the owner is a C-block or procedure definition, link its SUBSTACK input + if current_owner_block_id and (all_generated_blocks[current_owner_block_id]["block_shape"] == "C-Block" or all_generated_blocks[current_owner_block_id]["op_code"] == "procedures_definition"): + owner_block = all_generated_blocks[current_owner_block_id] + if owner_block["op_code"] == "control_if_else": + # If SUBSTACK is already set, this means we are starting SUBSTACK2 (else part) + if owner_block["sub_stacks"].get("SUBSTACK") and not owner_block["sub_stacks"].get("SUBSTACK2"): + owner_block["sub_stacks"]["SUBSTACK2"] = key + else: # This should be the first substack (then part) + owner_block["sub_stacks"]["SUBSTACK"] = key + elif not owner_block["sub_stacks"].get("SUBSTACK"): # Only set if not already set by a block + owner_block["sub_stacks"]["SUBSTACK"] = key + elif current_owner_block_id and all_generated_blocks[current_owner_block_id]["block_shape"] == "Hat Block": + # If the owner is a Hat block, this is its first child + all_generated_blocks[current_owner_block_id]["next"] = key + + info["topLevel"] = False + info["next"] = None # Default, will be overwritten if there's a next block + + # If it's a C-block or define block, it also starts a new inner scope + if ntype == "c_block" or opcode == "procedures_definition": + # Update the current scope's last_block_in_current_chain to this C-block + stack[-1] = (current_scope_indent, current_owner_block_id, key) + # Push a new scope for the C-block's substack + stack.append((current_indent, key, None)) # New scope: owner is this C-block, no last block yet + else: + # For regular stack blocks, just update the last_block_in_current_chain for the current scope + stack[-1] = (current_scope_indent, current_owner_block_id, key) + + # Parse inputs and fields (this part remains largely the same, but ensure parse_reporter_or_value/parse_condition + # are passed the *newly created block's ID* as the parent_key for nested inputs) + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if opcode == "motion_movesteps": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["STEPS"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_turnright" or opcode == "motion_turnleft": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DEGREES"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_gotoxy": + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_glidesecstoxy": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt_for_parse, re.IGNORECASE) + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_secs: info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_pointindirection": + m = re.search(r"direction\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DIRECTION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_changexby", "motion_changeyby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DX" if opcode == "motion_changexby" else "DY"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_setx", "motion_sety"]: + m = re.search(r"(?:set x to|set y to)\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["X" if opcode == "motion_setx" else "Y"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_changesizeby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_setsizeto": + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SIZE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_changeeffectby", "sound_changeeffectby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE" if opcode == "looks_changeeffectby" else "VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE" if opcode == "looks_seteffectto" else "VOLUME"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["NUM"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "control_wait": + m = re.search(r"wait\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DURATION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "control_repeat": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["TIMES"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "data_changevariableby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "data_deleteoflist": + m = re.search(r"delete\s*\((.+?)\)\s*of", stmt_for_parse, re.IGNORECASE) + if m: + val_str = m.group(1).strip() + if val_str.isdigit(): + info["inputs"]["INDEX"] = {"kind": "value", "value": int(val_str)} + else: + info["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + elif opcode == "data_insertatlist": + m_item = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt_for_parse, re.IGNORECASE) + m_index = re.search(r"at\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + elif opcode == "data_replaceitemoflist": + m_index = re.search(r"replace item\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + m_item = re.search(r"with\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + elif opcode == "event_whengreaterthan": + m = re.search(r">\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + + # String inputs + elif opcode == "looks_sayforsecs": + m = re.search(r"say\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_say": + m = re.search(r"say\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = parse_reporter_or_value(m.group(1).strip(), key, pick_key, all_generated_blocks) + elif opcode == "looks_thinkforsecs": + m = re.search(r"think\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_think": + m = re.search(r"think\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "sensing_askandwait": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["QUESTION"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["ITEM"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_setvariableto": + m_var = re.search(r"set\s*\[([^\]]+)\s*v\]\s*to\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m_var: + var_name = m_var.group(1).strip() + value_str = m_var.group(2).strip() + info["fields"]["VARIABLE"] = [var_name, None] + print(f"the datasetvariable: {value_str}") + info["inputs"]["VALUE"] = parse_reporter_or_value(value_str, key, pick_key, all_generated_blocks) + + # Dropdown/Menu inputs (UPDATED) + elif opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "random position": option_val = "_random_" + elif option == "mouse-pointer": option_val = "_mouse_" + else: option_val = option + + menu_block_id = _register_block("motion_goto_menu", key, True, pick_key, all_generated_blocks, fields={"TO": [option_val, None]}) + info["inputs"]["TO"] = {"kind": "block", "block": menu_block_id} + elif opcode == "motion_glideto": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*(?:\[([^\]]+)\s*v\]|\((.+?)\))", stmt_for_parse, re.IGNORECASE) + if m_secs: + info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + # Use group 3 for [random position v] or group 4 for (random position) + option = (m_secs.group(3) or m_secs.group(4)).strip() + if option == "random position": option_val = "_random_" + elif option == "mouse-pointer": option_val = "_mouse_" + else: option_val = option + + menu_block_id = _register_block("motion_glideto_menu", key, True, pick_key, all_generated_blocks, fields={"TO": [option_val, None]}) + info["inputs"]["TO"] = {"kind": "block", "block": menu_block_id} + elif opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option_val = "_mouse_" + else: option_val = option + + menu_block_id = _register_block("motion_pointtowards_menu", key, True, pick_key, all_generated_blocks, fields={"TOWARDS": [option_val, None]}) + info["inputs"]["TOWARDS"] = {"kind": "block", "block": menu_block_id} + elif opcode == "sensing_keypressed": + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", key, True, pick_key, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) + info["inputs"]["KEY_OPTION"] = {"kind": "block", "block": menu_block_id} + elif opcode == "sensing_touchingobject": + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option_val = "_mouse_" + elif option == "edge": option_val = "_edge_" + else: option_val = option + + menu_block_id = _register_block("sensing_touchingobjectmenu", key, True, pick_key, all_generated_blocks, fields={"TOUCHINGOBJECTMENU": [option_val, None]}) + info["inputs"]["TOUCHINGOBJECTMENU"] = {"kind": "block", "block": menu_block_id} + elif opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "myself": option_val = "_myself_" + else: option_val = option + + menu_block_id = _register_block("control_create_clone_of_menu", key, True, pick_key, all_generated_blocks, fields={"CLONE_OPTION": [option_val, None]}) + info["inputs"]["CLONE_OPTION"] = {"kind": "block", "block": menu_block_id} + elif opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sound_sounds_menu", key, True, pick_key, all_generated_blocks, fields={"SOUND_MENU": [option, None]}) + info["inputs"]["SOUND_MENU"] = {"kind": "block", "block": menu_block_id} + elif opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("looks_costume", key, True, pick_key, all_generated_blocks, fields={"COSTUME": [option, None]}) + info["inputs"]["COSTUME"] = {"kind": "block", "block": menu_block_id} + elif opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait"]: + m = re.search(r"switch backdrop to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("looks_backdrops", key, True, pick_key, all_generated_blocks, fields={"BACKDROP": [option, None]}) + info["inputs"]["BACKDROP"] = {"kind": "block", "block": menu_block_id} + elif opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + # Broadcast input doesn't use a separate menu block in definitions, it's a direct menu field in the input. + # So, it should be [1, [11, "message1", "id"]] or [1, [12, "message1"]] + # For now, let's keep it simple as [1, [11, option, None]] or similar if the definition allows. + # The `all_block_definitions` has `[1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]` + # Let's use that format, but without the specific ID for now. + info["inputs"]["BROADCAST_INPUT"] = {"kind": "value", "value": option} # Store as a value for now + + # Conditional inputs (Boolean blocks) + elif opcode in ["control_if", "control_if_else", "control_wait_until", "control_repeat_until"]: + #cond_match_str = stmt_for_parse.replace("if <", "").replace("> then else", "").replace("> then", "").replace("wait until <", "").replace("repeat until <", "").strip() + cond_match_str = extract_condition_balanced(stmt_for_parse) + print(f"The cond match text here:---->{cond_match_str}") + if cond_match_str: + # Pass current block's key as parent for nested condition + #info["inputs"]["CONDITION"] = parse_condition(cond_match_str, key, pick_key, all_generated_blocks) + condition_obj = parse_condition(cond_match_str, key, pick_key, all_generated_blocks) + info["inputs"]["CONDITION"] = condition_obj + # Ensure the parent of the condition block is set to this control block + if condition_obj.get("kind") == "block": + all_generated_blocks[condition_obj["block"]]["parent"] = key + + elif opcode in ["operator_and", "operator_or", "operator_not", "operator_contains", + "sensing_touchingcolor", "sensing_coloristouchingcolor", "sensing_mousedown"]: + pass + + + # Fields parsing + if "VARIABLE" in info["fields"]: + m = re.search(r"\[([^\]]+)\s*v\]", stmt_for_parse) + if m: + var_name = m.group(1).strip() + info["fields"]["VARIABLE"] = [var_name, None] + if "LIST" in info["fields"]: + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["LIST"] = [m.group(1), None] + if "STOP_OPTION" in info["fields"]: + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STOP_OPTION"] = [m.group(1), None] + if "STYLE" in info["fields"]: + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STYLE"] = [m.group(1), None] + if "DRAG_MODE" in info["fields"]: + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["DRAG_MODE"] = [m.group(1), None] + if "EFFECT" in info["fields"] and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["EFFECT"] = [m.group(1).upper(), None] + if "NUMBER_NAME" in info["fields"] and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["NUMBER_NAME"] = [m.group(1), None] + if "FRONT_BACK" in info["fields"] and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FRONT_BACK"] = [m.group(1), None] + if "FORWARD_BACKWARD" in info["fields"] and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + if "OPERATOR" in info["fields"] and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["OPERATOR"] = [m.group(1).upper(), None] + if "CURRENTMENU" in info["fields"] and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + if "PROPERTY" in info["fields"] and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt_for_parse, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + info["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + if "WHENGREATERTHANMENU" in info["fields"] and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + if "KEY_OPTION" in info["fields"] and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["KEY_OPTION"] = [m.group(1), None] + if "BACKDROP" in info["fields"] and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BACKDROP"] = [m.group(1), None] + if "BROADCAST_OPTION" in info["fields"] and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + # Custom block specific parsing + if opcode == "procedures_definition": + proc_def_match = re.match(r"(?:define|procedure)\s+([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))?", stmt_for_parse, re.IGNORECASE) + if proc_def_match: + proc_name = proc_def_match.group(1).strip() + args_str = proc_def_match.group(2) + info["procedure_name"] = proc_name + info["is_custom_definition"] = True + + mutation_block = { + "tagName": "mutation", + "children": [], + "proccode": proc_name, + "argumentids": [], + "argumentnames": [], + "argumentdefaults": [], + "warp": False # Assuming non-warp by default + } + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for arg in args: + arg_id = f"%s" # Scratch uses %s for string args, %n for number args + # For simplicity, we'll just use a generic ID for now, or match Scratch's pattern + # For the plan, we just need the names and order. + mutation_block["argumentids"].append(arg_id) + mutation_block["argumentnames"].append(arg) + mutation_block["argumentdefaults"].append("") + + info["mutation"] = mutation_block + + elif opcode == "procedures_call": + call_match = re.match(r"(?:call\s+)?([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", stmt_for_parse, re.IGNORECASE) + if call_match: + custom_block_name = call_match.group(1).strip() + args_str = call_match.group(2) + info["custom_block_name"] = custom_block_name + + info["mutation"] = { + "tagName": "mutation", + "children": [], + "proccode": custom_block_name, + "argumentids": [], + "argumentnames": [], + "warp": False + } + + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for idx, arg_val_str in enumerate(args): + arg_input_name = f"argument_name_{idx+1}" + info["mutation"]["argumentids"].append(arg_input_name) # Use the input name as argument ID + info["mutation"]["argumentnames"].append(f"arg{idx+1}") # Placeholder name for mutation + + info["inputs"][arg_input_name] = parse_reporter_or_value(arg_val_str, key, pick_key, all_generated_blocks) # Pass current block's key + + i += 1 # Move to the next line + + # Final pass to ensure last blocks have next: None (already handled by stack pops) + # The build_script_flow function will correctly traverse the linked list. + while len(stack) > 1: # Keep the initial sentinel + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # Construct the final flow output based on the collected top-level keys + print(f"[ALL OPCODE BLCOKS KEY ]:\n---\n {json.dumps(all_generated_blocks, indent=1)}\n---\n") + with open("all_generated_blocks.json", "w") as f: + json.dump(all_generated_blocks, f, indent=2) + # Recursively build the block structure for each top-level script + def build_script_flow(current_block_key, visited=None): + if visited is None: + visited = set() + + script_flow = [] + current_iter_key = current_block_key + + while current_iter_key: + # Detect cyclic reference + if current_iter_key in visited: + script_flow.append({ + "block_key": current_iter_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected, stopping recursion" + }) + break + + visited.add(current_iter_key) + block = all_generated_blocks.get(current_iter_key) + if not block: + break # Should not happen if keys are correct + + output_block = { + "block_key": block["id"], + "opcode": block["op_code"], + "type": block["block_shape"].replace(" Block", "").lower().replace("c-", "c_"), + "inputs": {}, + "fields": {}, + "shadow": block.get("shadow"), + "topLevel": block.get("topLevel"), + "parent": block.get("parent"), + "next": block.get("next") + } + + # Handle all input types + for inp_name, inp_val in block.get("inputs", {}).items(): + if inp_name in ["SUBSTACK", "SUBSTACK2"]: + if inp_val and inp_val in all_generated_blocks: # Direct block ID for substacks + output_block["inputs"][inp_name] = build_script_flow(inp_val, visited.copy()) + else: + output_block["inputs"][inp_name] = [] # Empty substack + elif isinstance(inp_val, dict) and inp_val.get("kind") == "block": + # Recursively build nested reporter/boolean blocks + nested_block_key = inp_val["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val # Keep original if not found (shouldn't happen) + elif isinstance(inp_val, dict) and inp_val.get("kind") == "value": + output_block["inputs"][inp_name] = inp_val["value"] # Extract the value + else: + output_block["inputs"][inp_name] = inp_val + + for field_name, field_val in block.get("fields", {}).items(): + output_block["fields"][field_name] = field_val + + if block.get("custom_block_name"): + output_block["custom_block_name"] = block["custom_block_name"] + + if block.get("procedure_name"): + output_block["procedure_name"] = block["procedure_name"] + output_block["is_custom_definition"] = True + if "mutation" in block: # Include mutation for custom definitions + output_block["mutation"] = block["mutation"] + + + script_flow.append(output_block) + + # Proceed to the next block in sequence + next_key = block.get("next") + if next_key in visited: + script_flow.append({ + "block_key": next_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected in 'next' pointer" + }) + break + + current_iter_key = next_key + + return script_flow + + final_flow_output = [] + for key in top_level_script_keys: + final_flow_output.extend(build_script_flow(key)) + return {"flow": final_flow_output} + +# Example input with opcodes for the initial generation +# initial_opcode_counts = [ +# {"opcode":"event_whenflagclicked","count":1}, +# {"opcode":"motion_gotoxy","count":1}, +# {"opcode":"motion_xposition","count":1}, +# {"opcode":"motion_setx","count":1}, +# {"opcode":"control_forever","count":1}, +# {"opcode":"control_if","count":2}, +# {"opcode":"control_stop","count":1}, +# {"opcode":"operator_lt","count":1}, +# {"opcode":"sensing_touchingobject","count":1}, +# {"opcode":"event_broadcast","count":1}, +# {"opcode":"data_setvariableto","count":2}, +# {"opcode":"data_showvariable","count":2}, +# {"opcode":"data_variable","count":4}, # For [score v], [speed v], [Sprite1 v], [Game Over v] +# {"opcode":"motion_pointtowards_menu", "count":1}, # For mouse-pointer menu +# {"opcode":"motion_glideto_menu", "count":1}, # For random position menu +# {"opcode":"motion_goto_menu", "count":1}, # For random position menu +# {"opcode":"sensing_touchingobjectmenu", "count":1}, # For Sprite1 menu +# ] +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_xposition","count":1}, + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":1}, + {"opcode":"control_stop","count":1}, + {"opcode":"operator_lt","count":1}, + {"opcode":"sensing_touchingobject","count":1}, + #{"opcode":"sensing_touchingobjectmenu","count":1}, + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":1}, + {"opcode":"data_showvariable","count":2}, +] + +# initial_opcode_counts = [ +# {"opcode":"event_whenflagclicked","count":1}, +# {"opcode":"motion_gotoxy","count":1}, +# {"opcode":"motion_glidesecstoxy","count":2}, # Increased count +# {"opcode":"motion_xposition","count":3}, # Used multiple times in conditions +# {"opcode":"motion_setx","count":1}, +# {"opcode":"control_forever","count":1}, +# {"opcode":"control_if","count":2}, # Two if blocks +# {"opcode":"control_stop","count":2}, # stop all v, stop this script v +# {"opcode":"operator_lt","count":1}, # Used in condition +# {"opcode":"sensing_touchingobject","count":2}, # Used in condition, and for if on edge, bounce +# {"opcode":"sensing_touchingobjectmenu","count":2}, # Menu for touchingobject +# {"opcode":"event_broadcast","count":2}, # broadcast [Game Over v], broadcast [jump v] +# {"opcode":"data_setvariableto","count":3}, # set [score v] to (1), set [speed v] to (1), set [other sprite X v] to ( (x position) of [Sprite2 v] ) +# {"opcode":"data_showvariable","count":2}, # show variable [score v], show variable [speed v] +# {"opcode":"operator_add","count":2}, # For set [var] to ((var) + (val)), (number 1) + (number 2) +# {"opcode":"data_variable","count":10}, # Increased count for general variables +# {"opcode":"looks_sayforsecs","count":2}, # For "say [Hello!] for (2) seconds", say [You win!] for (2) seconds +# {"opcode":"looks_say","count":2}, # For "say [Hello! v]", say [Welcome to my game! v] +# {"opcode":"motion_movesteps","count":3}, # For "move (10) steps" +# {"opcode":"control_wait","count":3}, # For "wait (0.1) seconds", wait (0.5) seconds, wait (1) seconds +# {"opcode":"motion_changeyby","count":2}, # For "change y by (10)" +# {"opcode":"motion_pointindirection","count":1}, # For "point in direction (90)" +# {"opcode":"event_whenkeypressed","count":3}, # For "when [space v] key pressed", when [up arrow v] key pressed, when [right arrow v] key pressed, when [left arrow v] key pressed +# {"opcode":"control_repeat","count":2}, # For "repeat (10)" +# {"opcode":"event_whenthisspriteclicked","count":2}, # For "when this sprite clicked" +# {"opcode":"looks_costumenumbername","count":1}, # For "(costume [name v])" +# {"opcode":"operator_join","count":3}, # For "join [Hello ] (answer)", join (length of [shopping list v]) [ items in the list.], join [Hello, ] (username) +# {"opcode":"sensing_answer","count":1}, # For "(answer)" +# {"opcode":"looks_hide","count":2}, # For "hide" +# {"opcode":"control_create_clone_of","count":2}, # For "create clone of [myself v]" +# {"opcode":"control_start_as_clone","count":2}, # For "when I start as a clone" +# {"opcode":"operator_random","count":1}, # For "pick random -240 to 240" +# {"opcode":"motion_ifonedgebounce","count":2}, # For "if on edge, bounce" +# {"opcode":"operator_gt","count":2}, # For "if <(score) > (10)> then", if <(item # of [Dog] in [myList v])> (0)> then +# {"opcode":"control_if_else","count":1}, # For "if <(score) > (10)> then else" +# {"opcode":"sound_play","count":2}, # Changed from sound_start to sound_play +# {"opcode":"sensing_loudness","count":2}, # For "(loudness)" +# {"opcode":"event_whengreaterthan","count":1}, # For "when [loudness v] > (70)" +# {"opcode":"control_repeat_until","count":1}, # For "repeat until " +# {"opcode":"looks_cleargraphiceffects","count":1}, # For "clear graphic effects" +# {"opcode":"looks_changeeffectby","count":2}, # For "change [color v] effect by (50)", change [fisheye v] effect by (5) +# {"opcode":"looks_seteffectto","count":1}, # For "set [ghost v] effect to (75)" +# {"opcode":"looks_setsizeto","count":2}, # Increased count +# {"opcode":"looks_changesizeby","count":1}, # For "change size by (5)" +# {"opcode":"looks_nextcostume","count":2}, # For "next costume" +# {"opcode":"looks_switchbackdroptowait","count":1}, # For "switch backdrop to [game over v] and wait" +# {"opcode":"looks_nextbackdrop","count":1}, # For "next backdrop" +# {"opcode":"sound_playuntildone","count":2}, # For "play sound [fanfare v] until done" +# {"opcode":"sound_stopallsounds","count":2}, # For "stop all sounds" +# {"opcode":"sound_changevolumeby","count":1}, # For "change volume by (-5)" +# {"opcode":"sound_setvolumeto","count":1}, # For "set volume to (50) %" +# {"opcode":"sensing_resettimer","count":2}, # For "reset timer" +# {"opcode":"sensing_setdragmode","count":2}, # For "set drag mode [not draggable v]" +# {"opcode":"data_addtolist","count":2}, # Increased count +# {"opcode":"data_deleteoflist","count":1}, # For "delete (all) of [my list v]" +# {"opcode":"data_insertatlist","count":1}, # For "insert [orange] at (2) of [fruits v]" +# {"opcode":"data_replaceitemoflist","count":1}, # For "replace item (1) of [colors v] with [blue]" +# {"opcode":"data_listcontainsitem","count":1}, # For "<[inventory v] contains [key]?>" +# {"opcode":"data_itemoflist","count":1}, # For "(item (2) of [myList v])" +# {"opcode":"data_lengthoflist","count":2}, # For "(length of [shopping list v])" +# {"opcode":"data_itemnumoflist","count":1}, # For "(item # of [Dog] in [myList v])" +# {"opcode":"sensing_touchingcolor","count":1}, # For "" +# {"opcode":"sensing_coloristouchingcolor","count":1}, # For "" +# {"opcode":"operator_and","count":1}, # For "< and >" +# {"opcode":"operator_or","count":1}, # For "< or >" +# {"opcode":"operator_not","count":1}, # For ">" +# {"opcode":"operator_contains","count":1}, # For "<[answer] contains [yes]?>" +# {"opcode":"procedures_call","count":1}, # For "jump (50)" +# {"opcode":"procedures_definition","count":1}, # For "define jump (height)" +# {"opcode":"sensing_of","count":1}, # For "(x position) of [Sprite2 v]" +# {"opcode":"sensing_current","count":1}, # For "(current [hour v])" +# {"opcode":"sensing_mousex","count":1}, # For "(mouse x)" +# {"opcode":"sensing_mousey","count":1}, # For "(mouse y)" +# {"opcode":"operator_subtract","count":1}, # For "((10) - (4))" +# {"opcode":"operator_multiply","count":1}, # For "(6) * (7)" +# {"opcode":"operator_divide","count":1}, # For "((20) / (5))" +# {"opcode":"data_list","count":1}, # For "[my list v]" +# {"opcode":"looks_gotofrontback","count":1}, # For "go to [front v] layer" +# {"opcode":"looks_goforwardbackwardlayers","count":1}, # For "go [forward v] (1) layers" +# {"opcode":"sound_sounds_menu","count":2}, # For sound menus +# {"opcode":"motion_goto_menu","count":1}, # For motion_goto menu +# {"opcode":"motion_glideto_menu","count":1}, # For motion_glideto menu +# {"opcode":"motion_pointtowards_menu","count":1}, # For motion_pointtowards menu +# {"opcode":"sensing_keyoptions","count":1}, # For sensing_keypressed menu +# {"opcode":"sensing_of_object_menu","count":1}, # For sensing_of menu +# {"opcode":"control_create_clone_of_menu","count":1}, # For control_create_clone_of menu +# {"opcode":"looks_costume","count":1}, # For looks_switchcostumeto menu +# {"opcode":"looks_backdrops","count":1}, # For looks_switchbackdropto menu +# {"opcode":"sensing_distanceto","count":1} # Added sensing_distanceto +# ] + +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) +with open("generated_output_json.json", "w") as f: + json.dump(generated_output_json, f, indent=2) + +pseudo_code_examples = [""" +when green flag clicked + set [score v] to (1) + go to x: (240) y: (-135) + set [speed v] to (1) + show variable [score v] + show variable [speed v] + forever + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end +end +""",] +# pseudo_code_examples = [ +# """ +# when green flag clicked +# go to x: (0) y: (0) +# point in direction (90) +# move (50) steps +# end +# """, +# """ +# when [right arrow v] key pressed +# turn right (15) degrees +# end +# """, +# """ +# when this sprite clicked +# go to [mouse-pointer v] +# """, +# """ +# when green flag clicked +# glide (2) secs to x: (150) y: (-100) +# glide (2) secs to x: (-150) y: (100) +# end +# """, +# """ +# when green flag clicked +# forever +# point towards [mouse-pointer v] +# move (5) steps +# end +# end +# """, +# """ +# when [right arrow v] key pressed +# change x by (10) +# end +# """, +# """ +# when green flag clicked +# set x to (0) +# set y to (0) +# end +# """, +# """ +# when [up arrow v] key pressed +# change y by (10) +# end +# """, +# """ +# when green flag clicked +# forever +# move (10) steps +# if on edge, bounce +# end +# end +# """, +# """ +# when green flag clicked +# set rotation style [left-right v] +# forever +# move (10) steps +# if on edge, bounce +# end +# end +# """, +# # From looks_block.json +# """ +# when green flag clicked +# say [Grr] for (3) seconds +# say [Have you seen my honey? v] for (3) seconds +# end +# """, +# """ +# when green flag clicked +# say [Welcome to my game! v] +# wait (2) seconds +# say [nothing] +# end +# """, +# """ +# when this sprite clicked +# think [What should I do? v] for (2) seconds +# end +# """, +# """ +# when I receive [correct answer v] +# think [That's right! v] +# wait (1) seconds +# think [good v] +# end +# """, +# """ +# when I receive [explosion v] +# repeat (5) +# next costume +# end +# hide +# end +# """, +# """ +# when green flag clicked +# forever +# next costume +# wait (0.2) seconds +# end +# end +# """, +# """ +# when green flag clicked +# switch backdrop to [start screen v] +# end +# """, +# """ +# broadcast [game over v] +# switch backdrop to [game over v] and wait +# stop [all v] +# end +# """, +# """ +# when [space v] key pressed +# next backdrop +# end +# """, +# """ +# when green flag clicked +# repeat (10) +# change size by (5) +# wait (0.1) seconds +# end +# end +# """, +# """ +# when green flag clicked +# set size to (50) % +# wait (1) seconds +# set size to (100) % +# end +# """, +# """ +# when green flag clicked +# forever +# change [color v] effect by (5) +# wait (0.1) seconds +# end +# end +# """, +# """ +# when green flag clicked +# set [ghost v] effect to (75) +# end +# """, +# """ +# when green flag clicked +# change [color v] effect by (50) +# wait (2) seconds +# clear graphic effects +# end +# """, +# """ +# when green flag clicked +# hide +# when I receive [start game v] +# show +# end +# """, +# """ +# when green flag clicked +# hide +# end +# """, +# """ +# when green flag clicked +# go to [front v] layer +# end +# """, +# """ +# when this sprite clicked +# go [forward v] (1) layers +# end +# """, +# """ +# say join [I am costume ] (costume [name v]) +# """, +# """ +# say join [Current backdrop: ] (backdrop [name v]) for (2) seconds +# """, +# """ +# set size to ( (size) + (10) ) +# """, +# # From sound_block.json +# """ +# when backdrop switches to [winning screen v] +# play sound [fanfare v] until done +# say [You won!] for (2) seconds +# end +# """, +# """ +# forever +# play sound [Music v] until done +# end +# """, +# """ +# when this sprite clicked +# start sound [Pop v] +# change [score v] by (1) +# end +# """, +# """ +# when I receive [game over v] +# stop all sounds +# end +# """, +# """ +# when [down arrow v] key pressed +# change volume by (-5) +# end +# """, +# """ +# when green flag clicked +# set volume to (50) % +# end +# """, +# """ +# say join [Current volume: ] (volume) +# """, +# # From event_block.json +# """ +# when green flag clicked +# go to x: (0) y: (0) +# say [Hello!] for (2) seconds +# end +# """, +# """ +# when [space v] key pressed +# repeat (10) +# change y by (10) +# wait (0.1) seconds +# change y by (-10) +# end +# end +# """, +# """ +# when [right arrow v] key pressed +# point in direction (90) +# move (10) steps +# end +# """, +# """ +# when this sprite clicked +# say [Ouch!] for (1) seconds +# change [score v] by (-1) +# end +# """, +# """ +# when backdrop switches to [game over v] +# stop [all v] +# end +# """, +# """ +# when [loudness v] > (70) +# start sound [scream v] +# end +# """, +# """ +# when I receive [start game v] +# show +# go to x: (0) y: (0) +# end +# """, +# """ +# when I receive [game over v] +# set score to 0 +# stop [all v] +# end +# """, +# """ +# if then +# broadcast [jump v] +# end +# """, +# """ +# broadcast [initialize sprites v] and wait +# say [Game Started!] for (2) seconds +# """, +# # From control_block.json +# """ +# say [Hello!] for (1) seconds +# wait (0.5) seconds +# say [Goodbye!] for (1) seconds +# """, +# """ +# when green flag clicked +# repeat (10) +# move (10) steps +# wait (0.1) seconds +# end +# end +# """, +# """ +# when green flag clicked +# forever +# move (5) steps +# if on edge, bounce +# end +# end +# """, +# """ +# forever +# if then +# stop [this script v] +# end +# end +# """, +# """ +# if <(score) > (10)> then +# say [You win!] for (2) seconds +# else +# say [Keep trying!] for (2) seconds +# end +# """, +# """ +# repeat until +# move (5) steps +# end +# """, +# """ +# if <(health) = (0)> then +# stop [all v] +# end +# """, +# """ +# when I start as a clone +# wait until +# delete this clone +# end +# """, +# """ +# when I start as a clone +# go to x: (pick random -240 to 240) y: (pick random -180 to 180) +# show +# forever +# move (10) steps +# if on edge, bounce +# end +# end +# """, +# """ +# when I start as a clone +# wait (5) seconds +# delete this clone +# end +# """, +# """ +# when green flag clicked +# hide +# forever +# create clone of [myself v] +# wait (1) seconds +# end +# """, +# # From data_block.json +# """ +# when green flag clicked +# set [score v] to (0) +# set [player name v] to [Guest] +# end +# """, +# """ +# when this sprite clicked +# change [score v] by (1) +# end +# """, +# """ +# when green flag clicked +# add [apple] to [shopping list v] +# add [banana] to [shopping list v] +# end +# """, +# """ +# when green flag clicked +# delete (all) of [my list v] +# end +# """, +# """ +# insert [orange] at (2) of [fruits v] +# """, +# """ +# replace item (1) of [colors v] with [blue] +# """, +# """ +# when green flag clicked +# show variable [score v] +# end +# """, +# """ +# when I receive [game over v] +# hide variable [score v] +# end +# """, +# """ +# when green flag clicked +# show list [shopping list v] +# end +# """, +# """ +# when I receive [game over v] +# hide list [shopping list v] +# end +# """, +# """ +# say ([score v]) for (2) seconds +# """, +# """ +# say ([my list v]) +# """, +# """ +# say (item (2) of [myList v]) for 2 seconds +# """, +# """ +# say join (length of [shopping list v]) [ items in the list.] +# """, +# """ +# if <(item # of [Dog] in [myList v])> (0)> then +# say join [Dog found at position ] (item # of [Dog] in [my list v]) +# end +# """, +# # From reporter_blocks.json (some already covered by other categories) +# """ +# when green flag clicked +# say (x position) for (2) seconds +# end +# """, +# """ +# set [worms v] to (y position) +# """, +# """ +# when green flag clicked +# say (direction) for (2) seconds +# end +# """, +# """ +# say join [I am costume ] (costume [name v]) +# """, +# """ +# set size to ( (size) + (10) ) +# """, +# """ +# say join [Current backdrop: ] (backdrop [name v]) for (2) seconds +# """, +# """ +# say join [Current volume: ] (volume) +# """, +# """ +# if <(distance to [Sprite2 v]) < (50)> then +# say [Too close!] +# end +# """, +# """ +# ask [What is your name?] and wait +# say join [Hello ] (answer) +# """, +# """ +# go to x: (mouse x) y: (mouse y) +# """, +# """ +# if <(mouse y) < (0)> then +# say [Below center] +# end +# """, +# """ +# when green flag clicked +# forever +# if <(loudness) > (30)> then +# start sound [pop v] +# end +# """, +# """ +# when green flag clicked +# reset timer +# say join [Time elapsed: ] (timer) +# end +# """, +# """ +# set [other sprite X v] to ( (x position) of [Sprite2 v] ) +# """, +# """ +# say join [The current hour is ] (current [hour v]) +# """, +# """ +# say join [Days passed: ] (days since 2000) +# """, +# """ +# say join [Hello, ] (username) +# """, +# """ +# set [total v] to ( number 1 + number 2 ) +# """, +# """ +# set [difference v] to ( (number 1) - (number 2) ) +# """, +# """ +# set [area v] to ( (length) * (width) ) +# """, +# """ +# set [average v] to ( ([total score v]) / ([number of students v]) ) +# """, +# """ +# go to x: (pick random -240 to 240) y: (pick random -180 to 180) +# """, +# """ +# say (join [Hello ][World!]) +# """, +# """ +# say (letter (1) of [apple]) +# """, +# """ +# say (length of [banana]) +# """, +# """ +# if <([number v] mod (2) = (0))> then +# say [Even number] +# end +# """, +# """ +# set [rounded score v] to (round (score)) +# """, +# """ +# set [distance v] to ([sqrt v] of ( ( (x position) * (x position) ) + ( (y position) * (y position) ) )) +# """, +# # From boolean_blocks.json (conditions already covered by parse_condition) +# """ +# if <(score) < (10)> then +# say [Keep trying!] +# end +# """, +# """ +# if <(answer) = (5)> then +# say [Correct!] +# end +# """, +# """ +# if <([health v]) > (0)> then +# move (10) steps +# else +# stop [all v] +# end +# """, +# """ +# if < and > then +# say [You're clicking me!] +# end +# """, +# """ +# if < or > then +# change x by (-10) +# end +# """, +# """ +# if > then +# say [I'm safe!] +# end +# """, +# """ +# if <[answer] contains [yes]?> then +# say [Great!] +# end +# """, +# """ +# if then +# broadcast [Game Over v] +# end +# """, +# """ +# if then +# bounce off edge +# end +# """, +# """ +# if then +# change [health v] by (-1) +# end +# """, +# """ +# if then +# say [Collision!] +# end +# """, +# """ +# forever +# if then +# broadcast [shoot v] +# end +# end +# """, +# """ +# if then +# go to mouse-pointer +# end +# """, +# """ +# if <[inventory v] contains [key]?> then +# say [You have the key!] +# end +# """, +# # Custom block example +# """ +# define jump (height) +# change y by (height) +# wait (0.5) seconds +# change y by (0 - (height)) +# end + +# when green flag clicked +# jump (50) +# end +# """, +# # User's custom procedure example +# """ +# // Define custom procedure "moveBall" +# procedure moveBall +# change x by vx +# change y by vy + +# if (x position > 240) or (x position < -240) then +# vx = -vx + +# if (y position > 180) or (y position < -180) then +# vy = -vy +# end procedure + +# // Main program +# when green flag clicked +# set x position to 0 +# set y position to 0 +# set vx to 5 +# set vy to 4 + +# forever +# call moveBall +# wait 0.02 seconds +# end forever +# """ +# ] +# # + +test=[ +""" +when green flag clicked + go to [random position v] +""", +""" +when green flag clicked + glide (1) secs to ([random position v]) +""", +""" +when green flag clicked + point towards [mouse-pointer v] +""", +""" +when green flag clicked + if then + broadcast [shoot v] + end +""", +""" +when green flag clicked + create clone of [myself v] +""", +""" +when green flag clicked + play sound [Meow v] until done +""", +""" +when green flag clicked + set [score v] to ((backdrop) of [Stage v]) +""", +""" +when green flag clicked + switch costume to [costume1 v] +""", +""" +when green flag clicked + switch backdrop to [backdrop1 v] +""" +] +txt="" +trace="" +# Process each example and print the plan +for i, pseudo_code_input in enumerate(pseudo_code_examples): + print(f"\n--- Processing Example {i+1} --- \n {pseudo_code_input} ---") + #print(pseudo_code_input.strip()) + try: + # Regenerate blocks and opcode_occurrences for each run to ensure fresh keys + # This is important because pick_key uses a defaultdict that persists state. + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) + #print(json.dumps(plan, indent=2)) + txt += str(plan) + " \n" + except Exception as e: + #print(f"Error processing example: {e}") + import traceback + #traceback.print_exc() + trace += f"Processing Example {i+1} \n"+str(e) +" "+str(pseudo_code_input)+" \n" + +with open("all_analysis.txt", "w", encoding="utf-8") as f: + f.write(txt) + +with open("all_analysis_trace.txt", "w", encoding="utf-8") as f: + f.write(trace) diff --git a/utils/plan_generator_12.py b/utils/plan_generator_12.py new file mode 100644 index 0000000000000000000000000000000000000000..5b9c380f3ae9f5f36d04068b28ae2dfd8648cf2a --- /dev/null +++ b/utils/plan_generator_12.py @@ -0,0 +1,3060 @@ +import json +import copy +import re +from collections import defaultdict + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + # Initialize parent, next, topLevel to None/False. These will be set correctly in generate_plan. + main_block_data["id"] = main_key # Ensure ID is present + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = False # Default to False, Hat blocks will override + main_block_data["shadow"] = False + + # Ensure inputs and fields are dictionaries, even if they were None or list in definition + if "inputs" not in main_block_data or not isinstance(main_block_data["inputs"], dict): + main_block_data["inputs"] = {} + if "fields" not in main_block_data or not isinstance(main_block_data["fields"], dict): + main_block_data["fields"] = {} + if "sub_stacks" not in main_block_data or not isinstance(main_block_data["sub_stacks"], dict): + main_block_data["sub_stacks"] = {} + + + generated_blocks[main_key] = main_block_data + + # Handle menus (shadow blocks) + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["id"] = menu_key # Ensure ID is present + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None # Shadow blocks never have a 'next' + menu_block_data["parent"] = main_key # Parent is the main block it's an input for + + # Ensure inputs and fields are dictionaries for menu blocks too + if "inputs" not in menu_block_data or not isinstance(menu_block_data["inputs"], dict): + menu_block_data["inputs"] = {} + if "fields" not in menu_block_data or not isinstance(menu_block_data["fields"], dict): + menu_block_data["fields"] = {} + if "sub_stacks" not in menu_block_data or not isinstance(menu_block_data["sub_stacks"], dict): + menu_block_data["sub_stacks"] = {} + + # Link the main block's input to the shadow block's key + # This assumes the input structure is [type_code, block_id_or_value] + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 0: # Check for at least type_code + # Assuming type code 1 for value input that takes a block + main_block_data["inputs"][input_name][1] = menu_key + else: # If input not defined as list, or empty, set it + main_block_data["inputs"][input_name] = [1, menu_key] # Default to value input type + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +# Consolidated block definitions from all JSON files +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_distanceto": { # Added sensing_distanceto + "block_name": "(distance to ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto", + "functionality": "Reports the distance from the sprite to the mouse-pointer or another specified sprite.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": False, "topLevel": True + }, + "sensing_distanceto_menu": { # Added sensing_distanceto_menu (though its use is now commented out in parse_reporter_or_value) + "block_name": "distance to menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto_menu", + "functionality": "Menu for distance to block.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, "sub_stacks": {"SUBSTACK": [2, None]} + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True, "sub_stacks": {"SUBSTACK": [2, None]} + }, + "control_if": { + "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": [2, None]}, "fields": {}, "shadow": False, "topLevel": True, "sub_stacks": {"SUBSTACK": [2, None]} + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True, "sub_stacks": {"SUBSTACK": [2, None], "SUBSTACK2": [2, None]} + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input] ", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True, "sub_stacks": {"SUBSTACK": [2, None]} + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True, + "sub_stacks": {"SUBSTACK": [2, None]} # Custom blocks can have a substack + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True + } +} + +def unparen(s): + s = s.strip() + # keep peeling off *all* matching outer parens + while True: + m = re.fullmatch(r"\((.*)\)", s) + if not m: + break + s = m.group(1).strip() + return s + +def _register_block(opcode, parent_key, is_shadow, pick_key_func, all_blocks_dict, inputs=None, fields=None, sub_stacks=None): + """ + Helper to create and register a block in the all_blocks_dict. + It uses pick_key_func to get a unique ID. + Parent, next, and topLevel are NOT set here for main blocks; they are handled in generate_plan. + For shadow blocks, parent is set here. + """ + key = pick_key_func(opcode) + block_data = copy.deepcopy(all_block_definitions[opcode]) + block_data["id"] = key + block_data["parent"] = parent_key if is_shadow else None # Only set parent for shadow blocks here + block_data["next"] = None # Default, will be set in generate_plan + block_data["topLevel"] = not is_shadow # Shadow blocks are not top-level + block_data["shadow"] = is_shadow + + # Ensure inputs, fields, and sub_stacks are dictionaries + if "inputs" not in block_data or not isinstance(block_data["inputs"], dict): + block_data["inputs"] = {} + if "fields" not in block_data or not isinstance(block_data["fields"], dict): + block_data["fields"] = {} + if "sub_stacks" not in block_data or not isinstance(block_data["sub_stacks"], dict): + block_data["sub_stacks"] = {} + + if inputs: + for inp_name, inp_val in inputs.items(): + # If the input is a block, ensure it's stored as a block ID reference + if isinstance(inp_val, dict) and inp_val.get("kind") == "block": + block_data["inputs"][inp_name] = [2, inp_val["block"]] # Type 2 for block input + elif isinstance(inp_val, dict) and inp_val.get("kind") == "value": + # For literal values, store as type 1 and the value itself + # Need to map to Scratch's internal value types if necessary, e.g., [4, "10"] for number + # For simplicity, we'll use a generic type 1 and the value for now. + # A more robust solution would map `type` from parse_reporter_or_value to Scratch's internal codes. + block_data["inputs"][inp_name] = [1, str(inp_val["value"])] # Store as string for now + else: + block_data["inputs"][inp_name] = inp_val # Fallback for other formats + + if fields: + block_data["fields"].update(fields) + + if sub_stacks: + block_data["sub_stacks"].update(sub_stacks) + + all_blocks_dict[key] = block_data + return key + +def _auto_balance(text): + # if there are more "(" than ")", append the missing ")" + diff = text.count("(") - text.count(")") + if diff > 0: + text = text + ")"*diff + # same for square brackets + diff = text.count("[") - text.count("]") + if diff > 0: + text = text + "]"*diff + return text + +def strip_outer_angle_brackets(text): + """ + Strip exactly one balanced pair of outer <...> brackets, only if they wrap the whole string. + """ + text = text.strip() + if text.startswith("<") and text.endswith(">"): + depth = 0 + for i, char in enumerate(text): + if char == '<': + depth += 1 + elif char == '>': + depth -= 1 + if depth == 0 and i == len(text) - 1: + return text[1:-1].strip() + # If we exit the loop and depth is 0, it means the outer brackets were balanced and wrapped the whole string + if depth == 0: + return text[1:-1].strip() + return text + +def extract_condition_balanced(stmt): + # 1. Remove "if" and "then" + stmt = stmt.strip() + if stmt.lower().startswith("if "): + stmt = stmt[3:].strip() + if stmt.lower().startswith("repeat until"): + stmt = stmt[12:].strip() + if stmt.lower().startswith("wait until "): + stmt = stmt[11:].strip() + if stmt.lower().endswith(" then"): + stmt = stmt[:-5].strip() + + # Helper to detect and strip single outer balanced angle brackets + def unwrap_balanced(s): + if s.startswith("<") and s.endswith(">"): + depth = 0 + for i in range(len(s)): + if s[i] == "<": + depth += 1 + elif s[i] == ">": + depth -= 1 + if depth == 0 and i < len(s) - 1: + return s # Early balance → not a single outer wrapper + if depth == 0: + return s[1:-1].strip() + return s + + # Recursively simplify things like > to not + def simplify(s): + s = unwrap_balanced(s) + s = s.strip() + + # Match > pattern + m = re.fullmatch(r"not\s*<(.+)>", s, re.IGNORECASE) + if m: + inner = m.group(1).strip() + inner = simplify(inner) + return f"not <{inner}>" + + # Match comparison operators like <(x position) < (100)> + # This part might be redundant if the main parser handles it, but good for internal consistency + m_comp = re.fullmatch(r"<\s*\(([^<>]+?)\)\s*([<>=])\s*\(([^<>]+?)\)\s*>", stmt) + if m_comp: + return f"({m_comp.group(1).strip()}) {m_comp.group(2)} ({m_comp.group(3).strip()})" + + return s + + return simplify(stmt) + +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, parent_key, pick_key_func, all_generated_blocks): + #print (f"text recived at parse_reporter_or_value _auto_balance here:----------------->: {text}") + text = _auto_balance(text.strip()) + #text = unparen(text.strip()) + print (f"text recived at parse_reporter_or_value unparen here:----------------->: {text}") + text = strip_outer_angle_brackets(text) + # Check for numeric literal (including parenthesized numbers like "(0)" or "(10)") + print (f"text recived at parse_reporter_or_value here:----------------->: {text}") + + m_num = re.fullmatch(r"\(?\s*(-?\d+(\.\d+)?)\s*\)?", text) + if m_num: + val_str = m_num.group(1) + return {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # Variable reporter: [score v], [health v], etc. + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, + fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + + # Now catch other bracketed values as literal strings + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + # Check for simple reporters, potentially with outer parentheses + m_simple_reporter = re.fullmatch(r"\((.+?)\)", text) + if m_simple_reporter: + inner_text = m_simple_reporter.group(1).strip() + if inner_text in simple_reporters: + block_id = _register_block(simple_reporters[inner_text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + # Also check for simple reporters without parentheses (e.g., if passed directly) + if text in simple_reporters: + block_id = _register_block(simple_reporters[text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + + # Variable reporter: [score v] or (score) or just "score" + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + # Ensure it's not a simple reporter already handled, or a number + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [potential_var_name, None]}) + return {"kind": "block", "block": block_id} + # Handle plain variable names like "score", "number 1", "total score" + if re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_ ]*", text): # Allow spaces for "number 1" etc. + # Exclude known simple reporters that don't have 'v' or parentheses + if text not in simple_reporters: + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [text, None]}) + return {"kind": "block", "block": block_id} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + return {"kind": "block", "block": block_id} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) # Parent will be set later + max_val_obj = parse_reporter_or_value(m.group(2).strip(), parent_key, pick_key_func, all_generated_blocks) # Parent will be set later + inputs = {"FROM": min_val_obj, "TO": max_val_obj} + block_id = _register_block("operator_random", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if min_val_obj.get("kind") == "block": all_generated_blocks[min_val_obj["block"]]["parent"] = block_id + if max_val_obj.get("kind") == "block": all_generated_blocks[max_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (join ()()) (operator_join) - handle both [] and () for inputs + + #m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s+(\[.+?\]|\(.+?\))", text) # Try (val) (val) + m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s*(\[.+?\]|\(.+?\))", text) + if m: + part1_txt = m.group(1).strip() + part2_txt = m.group(2).strip() + str1_obj = parse_reporter_or_value(part1_txt, parent_key, pick_key_func, all_generated_blocks) + str2_obj = parse_reporter_or_value(part2_txt, parent_key, pick_key_func, all_generated_blocks) + inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + block_id = _register_block("operator_join", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs) + # set parents if nested blocks + for obj in (str1_obj, str2_obj): + if obj.get("kind") == "block": + all_generated_blocks[obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # letter () of () (operator_letterof) - handle both [] and () for inputs + m = re.search(r"letter \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + string_val_obj = parse_reporter_or_value(m.group(2).strip(), parent_key, pick_key_func, all_generated_blocks) + inputs = {"LETTER": index_obj, "STRING": string_val_obj} + block_id = _register_block("operator_letterof", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + if string_val_obj.get("kind") == "block": all_generated_blocks[string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (length of ()) (operator_length) - handle both [] and () for inputs + #m = re.search(r"length of \((.+?)\)", text) + m = re.search(r"length of\s*(?:\((.+?)\)|\[(.+?)\])", text) + if not m: + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + arg_txt = (m.group(1) or m.group(2)).strip() + list_or_string_val_obj = parse_reporter_or_value(arg_txt, parent_key, pick_key_func, all_generated_blocks) + #list_or_string_val_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + inputs = {"STRING": list_or_string_val_obj} + block_id = _register_block("operator_length", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if list_or_string_val_obj.get("kind") == "block": all_generated_blocks[list_or_string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (() mod ()) (operator_mod) + # m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + m = re.search(r"\[([^\]]+)\s*v\]\s*mod\s*\(?\s*(.+?)\s*\)?", text) + if m: + num1_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + num2_obj = parse_reporter_or_value(m.group(2).strip(), parent_key, pick_key_func, all_generated_blocks) + inputs = {"NUM1": num1_obj, "NUM2": num2_obj} + block_id = _register_block("operator_mod", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num1_obj.get("kind") == "block": all_generated_blocks[num1_obj["block"]]["parent"] = block_id + if num2_obj.get("kind") == "block": all_generated_blocks[num2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + inputs = {"NUM": num_obj} + block_id = _register_block("operator_round", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num_obj.get("kind") == "block": all_generated_blocks[num_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (() of ()) (operator_mathop) - handle variable for function type + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) # e.g. [sqrt v] of ((x pos) * (x pos)) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), parent_key, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + # Also handle direct string for function type (e.g., "abs of (x)") + m = re.search(r"([a-zA-Z]+)\s*of\s*\((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), parent_key, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + # This regex is designed to handle nested parentheses correctly. + # It looks for an opening parenthesis, then non-parenthesis characters or balanced parentheses, + # followed by an operator, and then the second operand. + # This is a simplified approach; a full-fledged parser would use a stack. + # arithmetic_match = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + # arithmetic_match = re.search(r"\(?\s*(.+?)\s*\)?\s*([\+\-\*/])\s*\(?\s*(.+?)\s*\)?", text) + # if not arithmetic_match: + # # Try to match without outer parentheses for the operands, but still with an operator + # arithmetic_match = re.search(r"(.+?)\s*([+\-*/])\s*(.+)", text) + + # if arithmetic_match: + # op1_str = arithmetic_match.group(1).strip() + # operator_symbol = arithmetic_match.group(2).strip() + # op2_str = arithmetic_match.group(3).strip() + # print() + # op1_obj = parse_reporter_or_value(op1_str, parent_key, pick_key_func, all_generated_blocks) + # op2_obj = parse_reporter_or_value(op2_str, parent_key, pick_key_func, all_generated_blocks) + + # opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + # inputs = {"NUM1": op1_obj, "NUM2": op2_obj} + # block_id = _register_block(opcode_map[operator_symbol], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # if op1_obj.get("kind") == "block": all_generated_blocks[op1_obj["block"]]["parent"] = block_id + # if op2_obj.get("kind") == "block": all_generated_blocks[op2_obj["block"]]["parent"] = block_id + # return {"kind": "block", "block": block_id} + _strip_outer = re.compile(r"^\s*(?P[\(\[]+)\s*(?P.*\S\s*(?P=open)\s*$", re.VERBOSE) + _expr = re.compile(r"^\s*(?P[^\s\(\)\[\]]+)\s*(?P[+\-*/])\s*(?P[^\s\(\)\[\]]+)\s*$", re.VERBOSE) + #m_art = re.compile(r"\(?\s*([^\s()]+)\s*\)?\s*([+\-*/])\s*\(?\s*([^\s()]+)\s*\)?", re.VERBOSE) + if text: + expr = text + # strip *all* matching layers of () or [] + while True: + m = _strip_outer.match(expr) + if not m: + break + expr = m.group("inner") + + # now match the core "left op right" + m = _expr.match(expr) + if not m: + return None + + left_str = m.group("left") + operator = m.group("op") + right_str = m.group("right") + + # your existing parse & block‑creation logic + op1_obj = parse_reporter_or_value(left_str, parent_key, pick_key_func, all_generated_blocks) + op2_obj = parse_reporter_or_value(right_str, parent_key, pick_key_func, all_generated_blocks) + + opcode_map = { + '+': 'operator_add', + '-': 'operator_subtract', + '*': 'operator_multiply', + '/': 'operator_divide', + } + inputs = {"NUM1": op1_obj, "NUM2": op2_obj} + block_id = _register_block( + opcode_map[operator], parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs + ) + + if op1_obj.get("kind") == "block": + all_generated_blocks[op1_obj["block"]]["parent"] = block_id + if op2_obj.get("kind") == "block": + all_generated_blocks[op2_obj["block"]]["parent"] = block_id + + return {"kind": "block", "block": block_id} + + + # (costume ()) (looks_costumenumbername) - handle with or without 'v' + m = re.search(r"costume \((.+?)\)", text) + if not m: + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_costumenumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (backdrop ()) (looks_backdropnumbername) - handle with or without 'v' + m = re.search(r"backdrop \((.+?)\)", text) + if not m: + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_backdropnumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (distance to ()) (sensing_distanceto) - handle with or without 'v' + m = re.search(r"distance to \((.+?)\)", text) + if not m: + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + + # This block has a dropdown FIELD, not an input that links to a shadow block + fields = {"TARGET": [target_val, None]} + block_id = _register_block("sensing_distanceto", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (current ()) (sensing_current) - handle with or without 'v' + m = re.search(r"current \((.+?)\)", text) + if not m: + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + fields = {"CURRENTMENU": [unit.upper(), None]} + block_id = _register_block("sensing_current", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (() of ()) (sensing_of) - Corrected logic + #m = re.search(r"\((.+?)\)\s*of\s*(?:\((.+?)\)|\[(.+?)\s*v\])", text) + m = re.search(r"\((.+?)\)\s*of\s*(?:\((.+?)\)|\[(.+?)\s*v\])", text) + if m: + prop_str = m.group(1).strip() + obj = (m.group(2) or m.group(3)).strip() + + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + property_value = prop_map.get(prop_str, prop_str) + + if obj.lower() == "stage": obj_val = "_stage_" + elif obj.lower() == "myself": obj_val = "_myself_" + else: obj_val = obj + + # Create the sensing_of_object_menu shadow block + object_menu_id = _register_block("sensing_of_object_menu", parent_key, True, pick_key_func, all_generated_blocks, fields={"OBJECT": [obj_val, None]}) + + # Create the main sensing_of block + inputs = {"OBJECT": {"kind": "block", "block": object_menu_id}} # Link input to the shadow block ID + fields = {"PROPERTY": [property_value, None]} # PROPERTY is a field of the main block + + block_id = _register_block("sensing_of", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + all_generated_blocks[object_menu_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item (index) of [list v]) (data_itemoflist) + m = re.search(r"item \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + # Create data_list shadow block for the list name + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + + inputs = {"INDEX": index_obj, "LIST": {"kind": "block", "block": list_block_id}} + fields = {} # No fields in data_itemoflist itself for the list name + block_id = _register_block("data_itemoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item # of [item] in [list v]) (data_itemnumoflist) + m = re.search(r"item # of \((.+?)\) in \((.+?)\)", text) + if not m: + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item_obj = parse_reporter_or_value(m.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + # Create data_list shadow block for the list name + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + + inputs = {"ITEM": item_obj, "LIST": {"kind": "block", "block": list_block_id}} + fields = {} # No fields in data_itemnumoflist itself for the list name + block_id = _register_block("data_itemnumoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if item_obj.get("kind") == "block": all_generated_blocks[item_obj["block"]]["parent"] = block_id + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + raise ValueError(f"Can't parse reporter or value: {text}") + +def parse_condition(stmt, parent_key, pick_key_func, all_generated_blocks): + """ + Parse Scratch-style boolean conditions, handling comparisons (<, =, >), + boolean operators (and, or, not), and other sensing conditions. + """ + s = stmt.strip() + s = extract_condition_balanced(s) + s_lower = s.lower() + print (f"text recived at parse_condition here:----------------->: {s_lower}") + # 1) Boolean NOT: `not <...>` + m_not = re.fullmatch(r"\s*(?:<\s*)?not\s+(.+?)(?:\s*>)?\s*", s_lower, re.IGNORECASE) + if m_not: + inner = m_not.group(1).strip() + inner_obj = parse_condition(inner, parent_key, pick_key_func, all_generated_blocks) + bid = _register_block("operator_not", parent_key, False, pick_key_func, all_generated_blocks, + inputs={"OPERAND": inner_obj}) + if inner_obj.get("kind") == "block": + all_generated_blocks[inner_obj["block"]]["parent"] = bid + return {"kind": "block", "block": bid} + + # 2) Boolean AND / OR + m_andor = re.fullmatch(r"\s*(.+?)\s+(and|or)\s+(.+?)\s*", s_lower, re.IGNORECASE) + if m_andor: + cond1_obj = parse_condition(m_andor.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) + cond2_obj = parse_condition(m_andor.group(3).strip(), parent_key, pick_key_func, all_generated_blocks) + op_block = 'operator_and' if m_andor.group(2).lower() == 'and' else 'operator_or' + inputs = {"OPERAND1": cond1_obj, "OPERAND2": cond2_obj} + block_id = _register_block(op_block, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if cond1_obj.get("kind") == "block": all_generated_blocks[cond1_obj["block"]]["parent"] = block_id + if cond2_obj.get("kind") == "block": all_generated_blocks[cond2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # 1a) Comparisons with explicit angle wrappers: < (...) op (...) > + m = re.fullmatch( + r"\s*<\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*>\s*", + s, + re.VERBOSE + ) + if m: + left_txt, right_txt = m.group(1), m.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 1b) Simple comparisons without angle wrappers: A op B + m_simple = re.fullmatch(r"\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*", s) + if m_simple: + left_txt, right_txt = m_simple.group(1), m_simple.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m_simple.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 4) Contains: <[list v] contains [item]?> + m = re.fullmatch(r"\s*\[(.+?)\]\s+contains\s+\[(.+?)\]\?\s*", s) + if m: + list_name = m.group(1).strip() + item_val = {"kind": "value", "value": m.group(2).strip()} # Item can be a value or a block + + # Create the data_list reporter block + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + + inputs = {"LIST": {"kind": "block", "block": list_block_id}, "ITEM": item_val} + block_id = _register_block("data_listcontainsitem", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 5) Touching object: + m_touch = re.fullmatch(r""" + \s* # leading space + (?:<\s*)? # optional '<' + touching # literal + \s*\[\s* + (?P[^\]]+?) # capture the sprite name + \s*v\]\? # close the [sprite v]? + (?:\s*>)? # optional '>' + """, s_lower, re.IGNORECASE | re.VERBOSE) + if m_touch: + sprite = m_touch.group('sprite').strip() + val = {'mouse-pointer':'_mouse_', 'edge':'_edge_'}.get(sprite, sprite) + + mid = _register_block( + "sensing_touchingobjectmenu", parent_key, True, pick_key_func, all_generated_blocks, + fields={"TOUCHINGOBJECTMENU":[val, None]} + ) + bid = _register_block( + "sensing_touchingobject", parent_key, False, pick_key_func, all_generated_blocks, + inputs={"TOUCHINGOBJECTMENU":{"kind": "block", "block": mid}} # Link input to the shadow block ID + ) + all_generated_blocks[mid]["parent"] = bid + return {"kind":"block","block":bid} + + # 6) Touching color: + m = re.search(r"touching color \[(#[0-9A-Fa-f]{6})\]\?", s_lower) + if m: + inputs = {"COLOR": {"kind": "value", "value": m.group(1)}} + block_id = _register_block("sensing_touchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # 7) Color is touching color: + m = re.search(r"color \[(#[0-9A-Fa-f]{6})\] is touching \[(#[0-9A-Fa-f]{6})\]\?", s_lower) + if m: + inputs = {"COLOR1": {"kind": "value", "value": m.group(1)}, "COLOR2": {"kind": "value", "value": m.group(2)}} + block_id = _register_block("sensing_coloristouchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # 8) Key pressed: + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", s_lower) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", parent_key, True, pick_key_func, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) + + inputs = {"KEY_OPTION": {"kind": "block", "block": menu_block_id}} + block_id = _register_block("sensing_keypressed", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + all_generated_blocks[menu_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 9) Mouse down?: mouse down? + if s_lower == "mouse down?": + block_id = _register_block("sensing_mousedown", parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + val_obj = parse_reporter_or_value(unparen(stmt), parent_key, pick_key_func, all_generated_blocks) + if val_obj: + return val_obj + + raise ValueError(f"Can't parse condition: {stmt}") + +def classify(line): + """ + Classifies a pseudo-code line into its corresponding Scratch opcode and block type. + Order of checks matters: more specific patterns should come before more general ones. + """ + l = line.lower().strip() + + # Ignore comments + if l.startswith("//"): return None, None + + # Hat Blocks (most specific first) + if re.match(r"when green flag click(ed)?", l): return "event_whenflagclicked", "hat" + if re.match(r"when (.+?) key press(ed)?", l): return "event_whenkeypressed", "hat" + if re.match(r"when this sprite click(ed)?", l): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if re.match(r"when i start as a clo(ne)?", l): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + if l.startswith("procedure "): return "procedures_definition", "hat" # For "procedure moveBall" + + # Motion Blocks + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + # IMPORTANT: More specific glide block before less specific one + if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if re.match(r"set x to\s*\(.+\)", l): return "motion_setx", "stack" # Specific for set x + if l.startswith("change y by"): return "motion_changeyby", "stack" + if re.match(r"set y to\s*\(.+\)", l): return "motion_sety", "stack" # Specific for set y + #if re.match(r"if on edge, bounce( off edge)?", l): return "motion_ifonedgebounce", "stack" + if re.match(r"if on edge,\s*bounc(e)?(\s+off\s+edge)?", l.strip(), re.IGNORECASE): return "motion_ifonedgebounce", "stack" + if re.match(r"bounce off edg(e)?", l): return "motion_ifonedgebounce", "stack" # Alias + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + + + # Looks Blocks + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if re.match(r"next costum(e)?", l): return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + # Updated regex for change/set effect by/to + if re.match(r"change\s*(\[.+?v\]|\(.+?\))?\s*effect by", l): return "looks_changeeffectby", "stack" + if re.match(r"set\s*(\[.+?v\]|\(.+?\))?\s*effect to", l): return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + # Sound Blocks + if re.match(r"play sound (.+?) until do(ne)?", l): return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + # Event Blocks (broadcasts) + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + # Control Blocks + if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + # Updated regex for stop block to handle different options + if re.match(r"stop \[(all|this script|other scripts in sprite)\s*v\]", l): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + # Data Blocks + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + # Updated regex for delete of list + if re.match(r"delete \((.+?)\) of \[([^\]]+)\s*v\]", l): return "data_deleteoflist", "stack" + if l.startswith("delete all of [" ): return "data_deletealloflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + # Sensing Blocks + if re.match(r"ask (.+?) and wai(t)?", l): return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom Blocks (procedures_call) - specific rule for "call" + if l.startswith("call "): + return "procedures_call", "stack" + + # Custom Blocks (procedures_call) - LAST RESORT (generic match) + # This should be the very last check for stack-type blocks to avoid conflicts. + # It tries to match anything that looks like a function call with or without arguments. + custom_block_match = re.match(r"([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", l) + if custom_block_match: + # Before returning, ensure it's not a known simple reporter or variable name + # that might have been missed or is being used standalone. + # This is a heuristic; a full parser would be more robust. + potential_name = custom_block_match.group(1).strip() + if potential_name not in ["x position", "y position", "direction", "mouse x", "mouse y", "loudness", "timer", "days since 2000", "username", "answer", "size", "volume"] and \ + not re.fullmatch(r"\[[^\]]+\]", potential_name) and \ + not re.fullmatch(r"\[[^\]]+\]\s*v", potential_name): + return "procedures_call", "stack" + + + raise ValueError(f"Unknown statement: {line!r}") + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key -> block_data (pre-generated block definitions) + • opcode_keys: dict of opcode -> list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... list of block dictionaries ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + # This should ideally not happen if initial_opcode_counts is comprehensive + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + all_generated_blocks = {} # This will store the final, structured blocks + + # Populate all_generated_blocks with initial blocks, ensuring they have IDs + for key, block_data in generated_input.items(): + all_generated_blocks[key] = copy.deepcopy(block_data) + all_generated_blocks[key]["id"] = key # Ensure ID is set + + # Stack stores (indent, owner_block_id, last_block_in_current_linear_chain_id) + # owner_block_id: The ID of the C-block or Hat block that owns the current substack. + # last_block_in_current_linear_chain_id: The ID of the last block added to the *current linear sequence* within this substack. + stack = [(-2, None, None)] # Sentinel: (indent, owner_block_id, last_block_in_current_linear_chain_id) + # Using -2 for initial indent to be less than any valid indent (0 or more) + + top_level_script_keys = [] + + lines = pseudo_code.splitlines() + i = 0 + while i < len(lines): + raw_line = lines[i] + stripped_line = raw_line.strip() + + # Skip empty lines and comments + if not stripped_line or stripped_line.startswith("//"): + i += 1 + continue + + current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2 + + # Handle 'else' and 'end' first, as they control scope + if stripped_line.lower() == "else": + # Pop the 'then' substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # The 'if-else' block (popped_owner_key) is the owner. + # This 'else' must belong to the current owner on top of the stack (which should be the if-else block) + # Ensure the current_owner_block_id is indeed an if-else block + if popped_owner_key and all_generated_blocks[popped_owner_key]["op_code"] == "control_if_else": + # Push a new scope for the 'else' substack, with the same owner. + stack.append((current_indent, popped_owner_key, None)) # New scope for 'else' part, no last block yet + else: + # Error: 'else' without a preceding 'if' or incorrect nesting + print(f"Error: 'else' found without a corresponding 'if-else' block at line {i+1}") + # Attempt to recover by treating it as a regular block or skipping + stack.append((popped_indent, popped_owner_key, popped_last_block_in_chain)) # Put back what was popped + i += 1 + continue + + if stripped_line.lower() == "end": + # Pop the current substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # If the popped scope had an owner (C-block or procedure definition), + # and its substack input isn't already filled, set it to None (empty substack). + if popped_owner_key: + owner_block = all_generated_blocks[popped_owner_key] + if owner_block["block_shape"] == "C-Block" or owner_block["op_code"] == "procedures_definition": + # Determine which substack this 'end' is closing + # This logic needs to be smarter for if-else + if owner_block["op_code"] == "control_if_else": + # If we just popped the 'then' branch (SUBSTACK) + # and SUBSTACK2 is not yet set, then this 'end' closes SUBSTACK. + # If SUBSTACK2 was already set, this 'end' closes SUBSTACK2. + # This is tricky without explicit markers. We assume 'else' is always followed by 'end' for its substack. + # If SUBSTACK is filled and SUBSTACK2 is not, then this 'end' closes SUBSTACK. + # If SUBSTACK2 is filled, this 'end' closes SUBSTACK2. + # If neither is filled, it's an empty 'then' branch. + if owner_block["sub_stacks"].get("SUBSTACK") and not owner_block["sub_stacks"].get("SUBSTACK2"): + # This 'end' closes the first substack (then branch) + pass # Already handled by the stack logic + elif owner_block["sub_stacks"].get("SUBSTACK2"): + # This 'end' closes the second substack (else branch) + pass # Already handled by the stack logic + else: # Neither substack was filled, meaning an empty 'then' branch + # This should have been caught when the substack was initiated. + # For now, ensure it's not set to a block ID if it was empty. + if "SUBSTACK" in owner_block["sub_stacks"] and owner_block["sub_stacks"]["SUBSTACK"] is None: + pass # Already None + if "SUBSTACK2" in owner_block["sub_stacks"] and owner_block["sub_stacks"]["SUBSTACK2"] is None: + pass # Already None + elif not owner_block["sub_stacks"].get("SUBSTACK"): # Only set if not already set by a block + owner_block["sub_stacks"]["SUBSTACK"] = None # No blocks in substack + i += 1 + continue + + # Adjust stack based on indentation for regular blocks + # Pop scopes whose indentation is greater than or equal to the current line's indentation + while len(stack) > 1 and stack[-1][0] >= current_indent: + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None # Terminate the chain + + # Get the current active scope from the stack + current_scope_indent, current_owner_block_id, last_block_in_current_chain = stack[-1] + + # Classify the statement and create the block + stmt_for_parse = stripped_line.rstrip("then").strip() + opcode, ntype = classify(stmt_for_parse) + + if opcode is None: # Should not happen if classify is robust + i += 1 + continue + + # Create the new block (and register it in all_generated_blocks) + # _register_block now only sets parent for shadow/input blocks; main block parent/next/topLevel set here. + key = pick_key(opcode) + # Ensure the block exists in all_generated_blocks before trying to access it + if key not in all_generated_blocks: + # If not pre-generated, create a basic entry. This is a fallback. + all_generated_blocks[key] = copy.deepcopy(all_block_definitions.get(opcode, {})) + all_generated_blocks[key]["id"] = key + all_generated_blocks[key]["inputs"] = all_generated_blocks[key].get("inputs", {}) + all_generated_blocks[key]["fields"] = all_generated_blocks[key].get("fields", {}) + all_generated_blocks[key]["sub_stacks"] = all_generated_blocks[key].get("sub_stacks", {}) + + info = all_generated_blocks[key] + + # Set parent, next, and topLevel for the main script blocks + if ntype == "hat": + info["parent"] = None + info["topLevel"] = True + top_level_script_keys.append(key) + # Push a new scope for the children of this hat block. + stack.append((current_indent, key, None)) # New scope: owner is this hat, no last block yet + else: # Stack block or C-block (that is part of a linear sequence) + if last_block_in_current_chain: + # This block's parent is the previous block in the chain + info["parent"] = last_block_in_current_chain + all_generated_blocks[last_block_in_current_chain]["next"] = key + else: + # This is the first block in a new linear chain (e.g., first block inside a forever loop) + # Its parent is the owner of the current scope (the C-block or Hat block) + info["parent"] = current_owner_block_id + + # If the owner is a C-block or procedure definition, link its SUBSTACK input + if current_owner_block_id and (all_generated_blocks[current_owner_block_id]["block_shape"] == "C-Block" or all_generated_blocks[current_owner_block_id]["op_code"] == "procedures_definition"): + owner_block = all_generated_blocks[current_owner_block_id] + if owner_block["op_code"] == "control_if_else": + # If SUBSTACK is already set, this means we are starting SUBSTACK2 (else part) + if owner_block["sub_stacks"].get("SUBSTACK") and not owner_block["sub_stacks"].get("SUBSTACK2"): + owner_block["sub_stacks"]["SUBSTACK2"] = key + else: # This should be the first substack (then part) + owner_block["sub_stacks"]["SUBSTACK"] = key + elif not owner_block["sub_stacks"].get("SUBSTACK"): # Only set if not already set by a block + owner_block["sub_stacks"]["SUBSTACK"] = key + elif current_owner_block_id and all_generated_blocks[current_owner_block_id]["block_shape"] == "Hat Block": + # If the owner is a Hat block, this is its first child + all_generated_blocks[current_owner_block_id]["next"] = key + + info["topLevel"] = False + info["next"] = None # Default, will be overwritten if there's a next block + + # If it's a C-block or define block, it also starts a new inner scope + if ntype == "c_block" or opcode == "procedures_definition": + # Update the current scope's last_block_in_current_chain to this C-block + stack[-1] = (current_scope_indent, current_owner_block_id, key) + # Push a new scope for the C-block's substack + stack.append((current_indent, key, None)) # New scope: owner is this C-block, no last block yet + else: + # For regular stack blocks, just update the last_block_in_current_chain for the current scope + stack[-1] = (current_scope_indent, current_owner_block_id, key) + + # Parse inputs and fields (this part remains largely the same, but ensure parse_reporter_or_value/parse_condition + # are passed the *newly created block's ID* as the parent_key for nested inputs) + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if opcode == "motion_movesteps": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["STEPS"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_turnright" or opcode == "motion_turnleft": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DEGREES"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_gotoxy": + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_glidesecstoxy": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt_for_parse, re.IGNORECASE) + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_secs: info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_pointindirection": + m = re.search(r"direction\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DIRECTION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_changexby", "motion_changeyby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DX" if opcode == "motion_changexby" else "DY"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_setx", "motion_sety"]: + m = re.search(r"(?:set x to|set y to)\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["X" if opcode == "motion_setx" else "Y"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_changesizeby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_setsizeto": + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SIZE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_changeeffectby", "sound_changeeffectby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE" if opcode == "looks_changeeffectby" else "VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE" if opcode == "looks_seteffectto" else "VOLUME"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["NUM"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "control_wait": + m = re.search(r"wait\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DURATION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "control_repeat": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["TIMES"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "data_changevariableby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "data_deleteoflist": + m = re.search(r"delete\s*\((.+?)\)\s*of", stmt_for_parse, re.IGNORECASE) + if m: + val_str = m.group(1).strip() + if val_str.isdigit(): + info["inputs"]["INDEX"] = {"kind": "value", "value": int(val_str)} + else: + info["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + elif opcode == "data_insertatlist": + m_item = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt_for_parse, re.IGNORECASE) + m_index = re.search(r"at\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + elif opcode == "data_replaceitemoflist": + m_index = re.search(r"replace item\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + m_item = re.search(r"with\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + elif opcode == "event_whengreaterthan": + m = re.search(r">\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + + # String inputs + elif opcode == "looks_sayforsecs": + m = re.search(r"say\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_say": + m = re.search(r"say\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = parse_reporter_or_value(m.group(1).strip(), key, pick_key, all_generated_blocks) + elif opcode == "looks_thinkforsecs": + m = re.search(r"think\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_think": + m = re.search(r"think\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "sensing_askandwait": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["QUESTION"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["ITEM"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_setvariableto": + m_var = re.search(r"set\s*\[([^\]]+)\s*v\]\s*to\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m_var: + var_name = m_var.group(1).strip() + value_str = m_var.group(2).strip() + info["fields"]["VARIABLE"] = [var_name, None] + print(f"the datasetvariable: {value_str}") + info["inputs"]["VALUE"] = parse_reporter_or_value(value_str, key, pick_key, all_generated_blocks) + + # Dropdown/Menu inputs (UPDATED) + elif opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "random position": option_val = "_random_" + elif option == "mouse-pointer": option_val = "_mouse_" + else: option_val = option + + menu_block_id = _register_block("motion_goto_menu", key, True, pick_key, all_generated_blocks, fields={"TO": [option_val, None]}) + info["inputs"]["TO"] = {"kind": "block", "block": menu_block_id} + elif opcode == "motion_glideto": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*(?:\[([^\]]+)\s*v\]|\((.+?)\))", stmt_for_parse, re.IGNORECASE) + if m_secs: + info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + # Use group 3 for [random position v] or group 4 for (random position) + option = (m_secs.group(3) or m_secs.group(4)).strip() + if option == "random position": option_val = "_random_" + elif option == "mouse-pointer": option_val = "_mouse_" + else: option_val = option + + menu_block_id = _register_block("motion_glideto_menu", key, True, pick_key, all_generated_blocks, fields={"TO": [option_val, None]}) + info["inputs"]["TO"] = {"kind": "block", "block": menu_block_id} + elif opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option_val = "_mouse_" + else: option_val = option + + menu_block_id = _register_block("motion_pointtowards_menu", key, True, pick_key, all_generated_blocks, fields={"TOWARDS": [option_val, None]}) + info["inputs"]["TOWARDS"] = {"kind": "block", "block": menu_block_id} + elif opcode == "sensing_keypressed": + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", key, True, pick_key, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) + info["inputs"]["KEY_OPTION"] = {"kind": "block", "block": menu_block_id} + elif opcode == "sensing_touchingobject": + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option_val = "_mouse_" + elif option == "edge": option_val = "_edge_" + else: option_val = option + + menu_block_id = _register_block("sensing_touchingobjectmenu", key, True, pick_key, all_generated_blocks, fields={"TOUCHINGOBJECTMENU": [option_val, None]}) + info["inputs"]["TOUCHINGOBJECTMENU"] = {"kind": "block", "block": menu_block_id} + elif opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "myself": option_val = "_myself_" + else: option_val = option + + menu_block_id = _register_block("control_create_clone_of_menu", key, True, pick_key, all_generated_blocks, fields={"CLONE_OPTION": [option_val, None]}) + info["inputs"]["CLONE_OPTION"] = {"kind": "block", "block": menu_block_id} + elif opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sound_sounds_menu", key, True, pick_key, all_generated_blocks, fields={"SOUND_MENU": [option, None]}) + info["inputs"]["SOUND_MENU"] = {"kind": "block", "block": menu_block_id} + elif opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("looks_costume", key, True, pick_key, all_generated_blocks, fields={"COSTUME": [option, None]}) + info["inputs"]["COSTUME"] = {"kind": "block", "block": menu_block_id} + elif opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait"]: + m = re.search(r"switch backdrop to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("looks_backdrops", key, True, pick_key, all_generated_blocks, fields={"BACKDROP": [option, None]}) + info["inputs"]["BACKDROP"] = {"kind": "block", "block": menu_block_id} + elif opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + # Broadcast input doesn't use a separate menu block in definitions, it's a direct menu field in the input. + # So, it should be [1, [11, "message1", "id"]] or [1, [12, "message1"]] + # For now, let's keep it simple as [1, [11, option, None]] or similar if the definition allows. + # The `all_block_definitions` has `[1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]` + # Let's use that format, but without the specific ID for now. + info["inputs"]["BROADCAST_INPUT"] = {"kind": "value", "value": option} # Store as a value for now + + # Conditional inputs (Boolean blocks) + elif opcode in ["control_if", "control_if_else", "control_wait_until", "control_repeat_until"]: + #cond_match_str = stmt_for_parse.replace("if <", "").replace("> then else", "").replace("> then", "").replace("wait until <", "").replace("repeat until <", "").strip() + cond_match_str = extract_condition_balanced(stmt_for_parse) + print(f"The cond match text here:---->{cond_match_str}") + if cond_match_str: + # Pass current block's key as parent for nested condition + info["inputs"]["CONDITION"] = parse_condition(cond_match_str, key, pick_key, all_generated_blocks) + elif opcode in ["operator_and", "operator_or", "operator_not", "operator_contains", + "sensing_touchingcolor", "sensing_coloristouchingcolor", "sensing_mousedown"]: + pass + + + # Fields parsing + if "VARIABLE" in info["fields"]: + m = re.search(r"\[([^\]]+)\s*v\]", stmt_for_parse) + if m: + var_name = m.group(1).strip() + info["fields"]["VARIABLE"] = [var_name, None] + if "LIST" in info["fields"]: + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["LIST"] = [m.group(1), None] + if "STOP_OPTION" in info["fields"]: + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STOP_OPTION"] = [m.group(1), None] + if "STYLE" in info["fields"]: + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STYLE"] = [m.group(1), None] + if "DRAG_MODE" in info["fields"]: + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["DRAG_MODE"] = [m.group(1), None] + if "EFFECT" in info["fields"] and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["EFFECT"] = [m.group(1).upper(), None] + if "NUMBER_NAME" in info["fields"] and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["NUMBER_NAME"] = [m.group(1), None] + if "FRONT_BACK" in info["fields"] and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FRONT_BACK"] = [m.group(1), None] + if "FORWARD_BACKWARD" in info["fields"] and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + if "OPERATOR" in info["fields"] and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["OPERATOR"] = [m.group(1).upper(), None] + if "CURRENTMENU" in info["fields"] and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + if "PROPERTY" in info["fields"] and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt_for_parse, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + info["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + if "WHENGREATERTHANMENU" in info["fields"] and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + if "KEY_OPTION" in info["fields"] and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["KEY_OPTION"] = [m.group(1), None] + if "BACKDROP" in info["fields"] and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BACKDROP"] = [m.group(1), None] + if "BROADCAST_OPTION" in info["fields"] and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + # Custom block specific parsing + if opcode == "procedures_definition": + proc_def_match = re.match(r"(?:define|procedure)\s+([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))?", stmt_for_parse, re.IGNORECASE) + if proc_def_match: + proc_name = proc_def_match.group(1).strip() + args_str = proc_def_match.group(2) + info["procedure_name"] = proc_name + info["is_custom_definition"] = True + + mutation_block = { + "tagName": "mutation", + "children": [], + "proccode": proc_name, + "argumentids": [], + "argumentnames": [], + "argumentdefaults": [], + "warp": False # Assuming non-warp by default + } + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for arg in args: + arg_id = f"%s" # Scratch uses %s for string args, %n for number args + # For simplicity, we'll just use a generic ID for now, or match Scratch's pattern + # For the plan, we just need the names and order. + mutation_block["argumentids"].append(arg_id) + mutation_block["argumentnames"].append(arg) + mutation_block["argumentdefaults"].append("") + + info["mutation"] = mutation_block + + elif opcode == "procedures_call": + call_match = re.match(r"(?:call\s+)?([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", stmt_for_parse, re.IGNORECASE) + if call_match: + custom_block_name = call_match.group(1).strip() + args_str = call_match.group(2) + info["custom_block_name"] = custom_block_name + + info["mutation"] = { + "tagName": "mutation", + "children": [], + "proccode": custom_block_name, + "argumentids": [], + "argumentnames": [], + "warp": False + } + + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for idx, arg_val_str in enumerate(args): + arg_input_name = f"argument_name_{idx+1}" + info["mutation"]["argumentids"].append(arg_input_name) # Use the input name as argument ID + info["mutation"]["argumentnames"].append(f"arg{idx+1}") # Placeholder name for mutation + + info["inputs"][arg_input_name] = parse_reporter_or_value(arg_val_str, key, pick_key, all_generated_blocks) # Pass current block's key + + i += 1 # Move to the next line + + # Final pass to ensure last blocks have next: None (already handled by stack pops) + # The build_script_flow function will correctly traverse the linked list. + while len(stack) > 1: # Keep the initial sentinel + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # Construct the final flow output based on the collected top-level keys + print(f"[ALL OPCODE BLCOKS KEY ]:\n---\n {json.dumps(all_generated_blocks, indent=1)}\n---\n") + with open("all_generated_blocks.json", "w") as f: + json.dump(all_generated_blocks, f, indent=2) + # Recursively build the block structure for each top-level script + def build_script_flow(current_block_key, visited=None): + if visited is None: + visited = set() + + script_flow = [] + current_iter_key = current_block_key + + while current_iter_key: + # Detect cyclic reference + if current_iter_key in visited: + script_flow.append({ + "block_key": current_iter_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected, stopping recursion" + }) + break + + visited.add(current_iter_key) + block = all_generated_blocks.get(current_iter_key) + if not block: + break # Should not happen if keys are correct + + output_block = { + "block_key": block["id"], + "opcode": block["op_code"], + "type": block["block_shape"].replace(" Block", "").lower().replace("c-", "c_"), + "inputs": {}, + "fields": {}, + "shadow": block.get("shadow"), + "topLevel": block.get("topLevel"), + "parent": block.get("parent"), + "next": block.get("next") + } + + # Handle all input types + for inp_name, inp_val in block.get("inputs", {}).items(): + if inp_name in ["SUBSTACK", "SUBSTACK2"]: + if inp_val and inp_val in all_generated_blocks: # Direct block ID for substacks + output_block["inputs"][inp_name] = build_script_flow(inp_val, visited.copy()) + else: + output_block["inputs"][inp_name] = [] # Empty substack + elif isinstance(inp_val, dict) and inp_val.get("kind") == "block": + # Recursively build nested reporter/boolean blocks + nested_block_key = inp_val["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val # Keep original if not found (shouldn't happen) + elif isinstance(inp_val, dict) and inp_val.get("kind") == "value": + output_block["inputs"][inp_name] = inp_val["value"] # Extract the value + else: + output_block["inputs"][inp_name] = inp_val + + for field_name, field_val in block.get("fields", {}).items(): + output_block["fields"][field_name] = field_val + + if block.get("custom_block_name"): + output_block["custom_block_name"] = block["custom_block_name"] + + if block.get("procedure_name"): + output_block["procedure_name"] = block["procedure_name"] + output_block["is_custom_definition"] = True + if "mutation" in block: # Include mutation for custom definitions + output_block["mutation"] = block["mutation"] + + + script_flow.append(output_block) + + # Proceed to the next block in sequence + next_key = block.get("next") + if next_key in visited: + script_flow.append({ + "block_key": next_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected in 'next' pointer" + }) + break + + current_iter_key = next_key + + return script_flow + + final_flow_output = [] + for key in top_level_script_keys: + final_flow_output.extend(build_script_flow(key)) + return {"flow": final_flow_output} + +# Example input with opcodes for the initial generation +# initial_opcode_counts = [ +# {"opcode":"event_whenflagclicked","count":1}, +# {"opcode":"motion_gotoxy","count":1}, +# {"opcode":"motion_xposition","count":1}, +# {"opcode":"motion_setx","count":1}, +# {"opcode":"control_forever","count":1}, +# {"opcode":"control_if","count":2}, +# {"opcode":"control_stop","count":1}, +# {"opcode":"operator_lt","count":1}, +# {"opcode":"sensing_touchingobject","count":1}, +# {"opcode":"event_broadcast","count":1}, +# {"opcode":"data_setvariableto","count":2}, +# {"opcode":"data_showvariable","count":2}, +# {"opcode":"data_variable","count":4}, # For [score v], [speed v], [Sprite1 v], [Game Over v] +# {"opcode":"motion_pointtowards_menu", "count":1}, # For mouse-pointer menu +# {"opcode":"motion_glideto_menu", "count":1}, # For random position menu +# {"opcode":"motion_goto_menu", "count":1}, # For random position menu +# {"opcode":"sensing_touchingobjectmenu", "count":1}, # For Sprite1 menu +# ] +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_xposition","count":1}, + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":1}, + {"opcode":"control_stop","count":1}, + {"opcode":"operator_lt","count":1}, + {"opcode":"sensing_touchingobject","count":1}, + #{"opcode":"sensing_touchingobjectmenu","count":1}, + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":1}, + {"opcode":"data_showvariable","count":2}, +] + +# initial_opcode_counts = [ +# {"opcode":"event_whenflagclicked","count":1}, +# {"opcode":"motion_gotoxy","count":1}, +# {"opcode":"motion_glidesecstoxy","count":2}, # Increased count +# {"opcode":"motion_xposition","count":3}, # Used multiple times in conditions +# {"opcode":"motion_setx","count":1}, +# {"opcode":"control_forever","count":1}, +# {"opcode":"control_if","count":2}, # Two if blocks +# {"opcode":"control_stop","count":2}, # stop all v, stop this script v +# {"opcode":"operator_lt","count":1}, # Used in condition +# {"opcode":"sensing_touchingobject","count":2}, # Used in condition, and for if on edge, bounce +# {"opcode":"sensing_touchingobjectmenu","count":2}, # Menu for touchingobject +# {"opcode":"event_broadcast","count":2}, # broadcast [Game Over v], broadcast [jump v] +# {"opcode":"data_setvariableto","count":3}, # set [score v] to (1), set [speed v] to (1), set [other sprite X v] to ( (x position) of [Sprite2 v] ) +# {"opcode":"data_showvariable","count":2}, # show variable [score v], show variable [speed v] +# {"opcode":"operator_add","count":2}, # For set [var] to ((var) + (val)), (number 1) + (number 2) +# {"opcode":"data_variable","count":10}, # Increased count for general variables +# {"opcode":"looks_sayforsecs","count":2}, # For "say [Hello!] for (2) seconds", say [You win!] for (2) seconds +# {"opcode":"looks_say","count":2}, # For "say [Hello! v]", say [Welcome to my game! v] +# {"opcode":"motion_movesteps","count":3}, # For "move (10) steps" +# {"opcode":"control_wait","count":3}, # For "wait (0.1) seconds", wait (0.5) seconds, wait (1) seconds +# {"opcode":"motion_changeyby","count":2}, # For "change y by (10)" +# {"opcode":"motion_pointindirection","count":1}, # For "point in direction (90)" +# {"opcode":"event_whenkeypressed","count":3}, # For "when [space v] key pressed", when [up arrow v] key pressed, when [right arrow v] key pressed, when [left arrow v] key pressed +# {"opcode":"control_repeat","count":2}, # For "repeat (10)" +# {"opcode":"event_whenthisspriteclicked","count":2}, # For "when this sprite clicked" +# {"opcode":"looks_costumenumbername","count":1}, # For "(costume [name v])" +# {"opcode":"operator_join","count":3}, # For "join [Hello ] (answer)", join (length of [shopping list v]) [ items in the list.], join [Hello, ] (username) +# {"opcode":"sensing_answer","count":1}, # For "(answer)" +# {"opcode":"looks_hide","count":2}, # For "hide" +# {"opcode":"control_create_clone_of","count":2}, # For "create clone of [myself v]" +# {"opcode":"control_start_as_clone","count":2}, # For "when I start as a clone" +# {"opcode":"operator_random","count":1}, # For "pick random -240 to 240" +# {"opcode":"motion_ifonedgebounce","count":2}, # For "if on edge, bounce" +# {"opcode":"operator_gt","count":2}, # For "if <(score) > (10)> then", if <(item # of [Dog] in [myList v])> (0)> then +# {"opcode":"control_if_else","count":1}, # For "if <(score) > (10)> then else" +# {"opcode":"sound_play","count":2}, # Changed from sound_start to sound_play +# {"opcode":"sensing_loudness","count":2}, # For "(loudness)" +# {"opcode":"event_whengreaterthan","count":1}, # For "when [loudness v] > (70)" +# {"opcode":"control_repeat_until","count":1}, # For "repeat until " +# {"opcode":"looks_cleargraphiceffects","count":1}, # For "clear graphic effects" +# {"opcode":"looks_changeeffectby","count":2}, # For "change [color v] effect by (50)", change [fisheye v] effect by (5) +# {"opcode":"looks_seteffectto","count":1}, # For "set [ghost v] effect to (75)" +# {"opcode":"looks_setsizeto","count":2}, # Increased count +# {"opcode":"looks_changesizeby","count":1}, # For "change size by (5)" +# {"opcode":"looks_nextcostume","count":2}, # For "next costume" +# {"opcode":"looks_switchbackdroptowait","count":1}, # For "switch backdrop to [game over v] and wait" +# {"opcode":"looks_nextbackdrop","count":1}, # For "next backdrop" +# {"opcode":"sound_playuntildone","count":2}, # For "play sound [fanfare v] until done" +# {"opcode":"sound_stopallsounds","count":2}, # For "stop all sounds" +# {"opcode":"sound_changevolumeby","count":1}, # For "change volume by (-5)" +# {"opcode":"sound_setvolumeto","count":1}, # For "set volume to (50) %" +# {"opcode":"sensing_resettimer","count":2}, # For "reset timer" +# {"opcode":"sensing_setdragmode","count":2}, # For "set drag mode [not draggable v]" +# {"opcode":"data_addtolist","count":2}, # Increased count +# {"opcode":"data_deleteoflist","count":1}, # For "delete (all) of [my list v]" +# {"opcode":"data_insertatlist","count":1}, # For "insert [orange] at (2) of [fruits v]" +# {"opcode":"data_replaceitemoflist","count":1}, # For "replace item (1) of [colors v] with [blue]" +# {"opcode":"data_listcontainsitem","count":1}, # For "<[inventory v] contains [key]?>" +# {"opcode":"data_itemoflist","count":1}, # For "(item (2) of [myList v])" +# {"opcode":"data_lengthoflist","count":2}, # For "(length of [shopping list v])" +# {"opcode":"data_itemnumoflist","count":1}, # For "(item # of [Dog] in [myList v])" +# {"opcode":"sensing_touchingcolor","count":1}, # For "" +# {"opcode":"sensing_coloristouchingcolor","count":1}, # For "" +# {"opcode":"operator_and","count":1}, # For "< and >" +# {"opcode":"operator_or","count":1}, # For "< or >" +# {"opcode":"operator_not","count":1}, # For ">" +# {"opcode":"operator_contains","count":1}, # For "<[answer] contains [yes]?>" +# {"opcode":"procedures_call","count":1}, # For "jump (50)" +# {"opcode":"procedures_definition","count":1}, # For "define jump (height)" +# {"opcode":"sensing_of","count":1}, # For "(x position) of [Sprite2 v]" +# {"opcode":"sensing_current","count":1}, # For "(current [hour v])" +# {"opcode":"sensing_mousex","count":1}, # For "(mouse x)" +# {"opcode":"sensing_mousey","count":1}, # For "(mouse y)" +# {"opcode":"operator_subtract","count":1}, # For "((10) - (4))" +# {"opcode":"operator_multiply","count":1}, # For "(6) * (7)" +# {"opcode":"operator_divide","count":1}, # For "((20) / (5))" +# {"opcode":"data_list","count":1}, # For "[my list v]" +# {"opcode":"looks_gotofrontback","count":1}, # For "go to [front v] layer" +# {"opcode":"looks_goforwardbackwardlayers","count":1}, # For "go [forward v] (1) layers" +# {"opcode":"sound_sounds_menu","count":2}, # For sound menus +# {"opcode":"motion_goto_menu","count":1}, # For motion_goto menu +# {"opcode":"motion_glideto_menu","count":1}, # For motion_glideto menu +# {"opcode":"motion_pointtowards_menu","count":1}, # For motion_pointtowards menu +# {"opcode":"sensing_keyoptions","count":1}, # For sensing_keypressed menu +# {"opcode":"sensing_of_object_menu","count":1}, # For sensing_of menu +# {"opcode":"control_create_clone_of_menu","count":1}, # For control_create_clone_of menu +# {"opcode":"looks_costume","count":1}, # For looks_switchcostumeto menu +# {"opcode":"looks_backdrops","count":1}, # For looks_switchbackdropto menu +# {"opcode":"sensing_distanceto","count":1} # Added sensing_distanceto +# ] + +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) +with open("generated_output_json.json", "w") as f: + json.dump(generated_output_json, f, indent=2) + +pseudo_code_examples = [""" +when green flag clicked + set [score v] to (1) + go to x: (240) y: (-135) + set [speed v] to (1) + show variable [score v] + show variable [speed v] + forever + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end +end +""",] +# pseudo_code_examples = [ +# """ +# when green flag clicked +# go to x: (0) y: (0) +# point in direction (90) +# move (50) steps +# end +# """, +# """ +# when [right arrow v] key pressed +# turn right (15) degrees +# end +# """, +# """ +# when this sprite clicked +# go to [mouse-pointer v] +# """, +# """ +# when green flag clicked +# glide (2) secs to x: (150) y: (-100) +# glide (2) secs to x: (-150) y: (100) +# end +# """, +# """ +# when green flag clicked +# forever +# point towards [mouse-pointer v] +# move (5) steps +# end +# end +# """, +# """ +# when [right arrow v] key pressed +# change x by (10) +# end +# """, +# """ +# when green flag clicked +# set x to (0) +# set y to (0) +# end +# """, +# """ +# when [up arrow v] key pressed +# change y by (10) +# end +# """, +# """ +# when green flag clicked +# forever +# move (10) steps +# if on edge, bounce +# end +# end +# """, +# """ +# when green flag clicked +# set rotation style [left-right v] +# forever +# move (10) steps +# if on edge, bounce +# end +# end +# """, +# # From looks_block.json +# """ +# when green flag clicked +# say [Grr] for (3) seconds +# say [Have you seen my honey? v] for (3) seconds +# end +# """, +# """ +# when green flag clicked +# say [Welcome to my game! v] +# wait (2) seconds +# say [nothing] +# end +# """, +# """ +# when this sprite clicked +# think [What should I do? v] for (2) seconds +# end +# """, +# """ +# when I receive [correct answer v] +# think [That's right! v] +# wait (1) seconds +# think [good v] +# end +# """, +# """ +# when I receive [explosion v] +# repeat (5) +# next costume +# end +# hide +# end +# """, +# """ +# when green flag clicked +# forever +# next costume +# wait (0.2) seconds +# end +# end +# """, +# """ +# when green flag clicked +# switch backdrop to [start screen v] +# end +# """, +# """ +# broadcast [game over v] +# switch backdrop to [game over v] and wait +# stop [all v] +# end +# """, +# """ +# when [space v] key pressed +# next backdrop +# end +# """, +# """ +# when green flag clicked +# repeat (10) +# change size by (5) +# wait (0.1) seconds +# end +# end +# """, +# """ +# when green flag clicked +# set size to (50) % +# wait (1) seconds +# set size to (100) % +# end +# """, +# """ +# when green flag clicked +# forever +# change [color v] effect by (5) +# wait (0.1) seconds +# end +# end +# """, +# """ +# when green flag clicked +# set [ghost v] effect to (75) +# end +# """, +# """ +# when green flag clicked +# change [color v] effect by (50) +# wait (2) seconds +# clear graphic effects +# end +# """, +# """ +# when green flag clicked +# hide +# when I receive [start game v] +# show +# end +# """, +# """ +# when green flag clicked +# hide +# end +# """, +# """ +# when green flag clicked +# go to [front v] layer +# end +# """, +# """ +# when this sprite clicked +# go [forward v] (1) layers +# end +# """, +# """ +# say join [I am costume ] (costume [name v]) +# """, +# """ +# say join [Current backdrop: ] (backdrop [name v]) for (2) seconds +# """, +# """ +# set size to ( (size) + (10) ) +# """, +# # From sound_block.json +# """ +# when backdrop switches to [winning screen v] +# play sound [fanfare v] until done +# say [You won!] for (2) seconds +# end +# """, +# """ +# forever +# play sound [Music v] until done +# end +# """, +# """ +# when this sprite clicked +# start sound [Pop v] +# change [score v] by (1) +# end +# """, +# """ +# when I receive [game over v] +# stop all sounds +# end +# """, +# """ +# when [down arrow v] key pressed +# change volume by (-5) +# end +# """, +# """ +# when green flag clicked +# set volume to (50) % +# end +# """, +# """ +# say join [Current volume: ] (volume) +# """, +# # From event_block.json +# """ +# when green flag clicked +# go to x: (0) y: (0) +# say [Hello!] for (2) seconds +# end +# """, +# """ +# when [space v] key pressed +# repeat (10) +# change y by (10) +# wait (0.1) seconds +# change y by (-10) +# end +# end +# """, +# """ +# when [right arrow v] key pressed +# point in direction (90) +# move (10) steps +# end +# """, +# """ +# when this sprite clicked +# say [Ouch!] for (1) seconds +# change [score v] by (-1) +# end +# """, +# """ +# when backdrop switches to [game over v] +# stop [all v] +# end +# """, +# """ +# when [loudness v] > (70) +# start sound [scream v] +# end +# """, +# """ +# when I receive [start game v] +# show +# go to x: (0) y: (0) +# end +# """, +# """ +# when I receive [game over v] +# set score to 0 +# stop [all v] +# end +# """, +# """ +# if then +# broadcast [jump v] +# end +# """, +# """ +# broadcast [initialize sprites v] and wait +# say [Game Started!] for (2) seconds +# """, +# # From control_block.json +# """ +# say [Hello!] for (1) seconds +# wait (0.5) seconds +# say [Goodbye!] for (1) seconds +# """, +# """ +# when green flag clicked +# repeat (10) +# move (10) steps +# wait (0.1) seconds +# end +# end +# """, +# """ +# when green flag clicked +# forever +# move (5) steps +# if on edge, bounce +# end +# end +# """, +# """ +# forever +# if then +# stop [this script v] +# end +# end +# """, +# """ +# if <(score) > (10)> then +# say [You win!] for (2) seconds +# else +# say [Keep trying!] for (2) seconds +# end +# """, +# """ +# repeat until +# move (5) steps +# end +# """, +# """ +# if <(health) = (0)> then +# stop [all v] +# end +# """, +# """ +# when I start as a clone +# wait until +# delete this clone +# end +# """, +# """ +# when I start as a clone +# go to x: (pick random -240 to 240) y: (pick random -180 to 180) +# show +# forever +# move (10) steps +# if on edge, bounce +# end +# end +# """, +# """ +# when I start as a clone +# wait (5) seconds +# delete this clone +# end +# """, +# """ +# when green flag clicked +# hide +# forever +# create clone of [myself v] +# wait (1) seconds +# end +# """, +# # From data_block.json +# """ +# when green flag clicked +# set [score v] to (0) +# set [player name v] to [Guest] +# end +# """, +# """ +# when this sprite clicked +# change [score v] by (1) +# end +# """, +# """ +# when green flag clicked +# add [apple] to [shopping list v] +# add [banana] to [shopping list v] +# end +# """, +# """ +# when green flag clicked +# delete (all) of [my list v] +# end +# """, +# """ +# insert [orange] at (2) of [fruits v] +# """, +# """ +# replace item (1) of [colors v] with [blue] +# """, +# """ +# when green flag clicked +# show variable [score v] +# end +# """, +# """ +# when I receive [game over v] +# hide variable [score v] +# end +# """, +# """ +# when green flag clicked +# show list [shopping list v] +# end +# """, +# """ +# when I receive [game over v] +# hide list [shopping list v] +# end +# """, +# """ +# say ([score v]) for (2) seconds +# """, +# """ +# say ([my list v]) +# """, +# """ +# say (item (2) of [myList v]) for 2 seconds +# """, +# """ +# say join (length of [shopping list v]) [ items in the list.] +# """, +# """ +# if <(item # of [Dog] in [myList v])> (0)> then +# say join [Dog found at position ] (item # of [Dog] in [my list v]) +# end +# """, +# # From reporter_blocks.json (some already covered by other categories) +# """ +# when green flag clicked +# say (x position) for (2) seconds +# end +# """, +# """ +# set [worms v] to (y position) +# """, +# """ +# when green flag clicked +# say (direction) for (2) seconds +# end +# """, +# """ +# say join [I am costume ] (costume [name v]) +# """, +# """ +# set size to ( (size) + (10) ) +# """, +# """ +# say join [Current backdrop: ] (backdrop [name v]) for (2) seconds +# """, +# """ +# say join [Current volume: ] (volume) +# """, +# """ +# if <(distance to [Sprite2 v]) < (50)> then +# say [Too close!] +# end +# """, +# """ +# ask [What is your name?] and wait +# say join [Hello ] (answer) +# """, +# """ +# go to x: (mouse x) y: (mouse y) +# """, +# """ +# if <(mouse y) < (0)> then +# say [Below center] +# end +# """, +# """ +# when green flag clicked +# forever +# if <(loudness) > (30)> then +# start sound [pop v] +# end +# """, +# """ +# when green flag clicked +# reset timer +# say join [Time elapsed: ] (timer) +# end +# """, +# """ +# set [other sprite X v] to ( (x position) of [Sprite2 v] ) +# """, +# """ +# say join [The current hour is ] (current [hour v]) +# """, +# """ +# say join [Days passed: ] (days since 2000) +# """, +# """ +# say join [Hello, ] (username) +# """, +# """ +# set [total v] to ( number 1 + number 2 ) +# """, +# """ +# set [difference v] to ( (number 1) - (number 2) ) +# """, +# """ +# set [area v] to ( (length) * (width) ) +# """, +# """ +# set [average v] to ( ([total score v]) / ([number of students v]) ) +# """, +# """ +# go to x: (pick random -240 to 240) y: (pick random -180 to 180) +# """, +# """ +# say (join [Hello ][World!]) +# """, +# """ +# say (letter (1) of [apple]) +# """, +# """ +# say (length of [banana]) +# """, +# """ +# if <([number v] mod (2) = (0))> then +# say [Even number] +# end +# """, +# """ +# set [rounded score v] to (round (score)) +# """, +# """ +# set [distance v] to ([sqrt v] of ( ( (x position) * (x position) ) + ( (y position) * (y position) ) )) +# """, +# # From boolean_blocks.json (conditions already covered by parse_condition) +# """ +# if <(score) < (10)> then +# say [Keep trying!] +# end +# """, +# """ +# if <(answer) = (5)> then +# say [Correct!] +# end +# """, +# """ +# if <([health v]) > (0)> then +# move (10) steps +# else +# stop [all v] +# end +# """, +# """ +# if < and > then +# say [You're clicking me!] +# end +# """, +# """ +# if < or > then +# change x by (-10) +# end +# """, +# """ +# if > then +# say [I'm safe!] +# end +# """, +# """ +# if <[answer] contains [yes]?> then +# say [Great!] +# end +# """, +# """ +# if then +# broadcast [Game Over v] +# end +# """, +# """ +# if then +# bounce off edge +# end +# """, +# """ +# if then +# change [health v] by (-1) +# end +# """, +# """ +# if then +# say [Collision!] +# end +# """, +# """ +# forever +# if then +# broadcast [shoot v] +# end +# end +# """, +# """ +# if then +# go to mouse-pointer +# end +# """, +# """ +# if <[inventory v] contains [key]?> then +# say [You have the key!] +# end +# """, +# # Custom block example +# """ +# define jump (height) +# change y by (height) +# wait (0.5) seconds +# change y by (0 - (height)) +# end + +# when green flag clicked +# jump (50) +# end +# """, +# # User's custom procedure example +# """ +# // Define custom procedure "moveBall" +# procedure moveBall +# change x by vx +# change y by vy + +# if (x position > 240) or (x position < -240) then +# vx = -vx + +# if (y position > 180) or (y position < -180) then +# vy = -vy +# end procedure + +# // Main program +# when green flag clicked +# set x position to 0 +# set y position to 0 +# set vx to 5 +# set vy to 4 + +# forever +# call moveBall +# wait 0.02 seconds +# end forever +# """ +# ] +# # + +test=[ +""" +when green flag clicked + go to [random position v] +""", +""" +when green flag clicked + glide (1) secs to ([random position v]) +""", +""" +when green flag clicked + point towards [mouse-pointer v] +""", +""" +when green flag clicked + if then + broadcast [shoot v] + end +""", +""" +when green flag clicked + create clone of [myself v] +""", +""" +when green flag clicked + play sound [Meow v] until done +""", +""" +when green flag clicked + set [score v] to ((backdrop) of [Stage v]) +""", +""" +when green flag clicked + switch costume to [costume1 v] +""", +""" +when green flag clicked + switch backdrop to [backdrop1 v] +""" +] +txt="" +trace="" +# Process each example and print the plan +for i, pseudo_code_input in enumerate(pseudo_code_examples): + print(f"\n--- Processing Example {i+1} --- \n {pseudo_code_input} ---") + #print(pseudo_code_input.strip()) + try: + # Regenerate blocks and opcode_occurrences for each run to ensure fresh keys + # This is important because pick_key uses a defaultdict that persists state. + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) + #print(json.dumps(plan, indent=2)) + txt += str(plan) + " \n" + except Exception as e: + #print(f"Error processing example: {e}") + import traceback + #traceback.print_exc() + trace += f"Processing Example {i+1} \n"+str(e) +" "+str(pseudo_code_input)+" \n" + +with open("all_analysis.txt", "w", encoding="utf-8") as f: + f.write(txt) + +with open("all_analysis_trace.txt", "w", encoding="utf-8") as f: + f.write(trace) diff --git a/utils/plan_generator_2.py b/utils/plan_generator_2.py new file mode 100644 index 0000000000000000000000000000000000000000..4a50fd0e16eb6a143c6b867dcbcf32616675c3a2 --- /dev/null +++ b/utils/plan_generator_2.py @@ -0,0 +1,2235 @@ +import json +import copy +import re +from collections import defaultdict + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False + + generated_blocks[main_key] = main_block_data + + # Handle menus + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_key + + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 1 and \ + main_block_data["inputs"][input_name][0] == 1: + + main_block_data["inputs"][input_name][1] = menu_key + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": [ + { + "name": "PROCCONTAINER", + "type": "block_prototype" + } + ], + "fields": {}, + "shadow": False, + "topLevel": True + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": [], # Inputs are dynamic based on definition + "fields": {}, + "shadow": False, + "topLevel": True + } +} + +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, pick_key_func): + text = text.strip() + + # Check for numeric literal + if re.fullmatch(r"-?\d+(\.\d+)?", text): + return {"kind": "value", "value": float(text) if '.' in text else int(text)} + # Check for string literal (e.g., "[Hello!]") + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + if text in simple_reporters: + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func(simple_reporters[text]), "inputs": {}}} + + # Variable reporter: [score v] or (score) + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_variable"), "inputs": {}, "fields": {"VARIABLE": [var_name, None]}}} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_variable"), "inputs": {}, "fields": {"VARIABLE": [potential_var_name, None]}}} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_list"), "inputs": {}, "fields": {"LIST": [list_name, None]}}} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + max_val = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_random"), "inputs": {"FROM": min_val, "TO": max_val}}} + + # (join ()()) (operator_join) + m = re.search(r"join \[(.+?)\] \[(.+?)\]", text) + if m: + str1 = {"kind": "value", "value": m.group(1).strip()} + str2 = {"kind": "value", "value": m.group(2).strip()} + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_join"), "inputs": {"STRING1": str1, "STRING2": str2}}} + + # letter () of () (operator_letterof) + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + string_val = {"kind": "value", "value": m.group(2).strip()} + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_letterof"), "inputs": {"LETTER": index, "STRING": string_val}}} + + # (length of ()) (operator_length) + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + list_name = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_lengthoflist"), "inputs": {}, "fields": {"LIST": [list_name, None]}}} + + # (() mod ()) (operator_mod) + m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + if m: + num1 = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + num2 = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_mod"), "inputs": {"NUM1": num1, "NUM2": num2}}} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_round"), "inputs": {"NUM": num}}} + + # (() of ()) (operator_mathop) + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_mathop"), "inputs": {"NUM": value}, "fields": {"OPERATOR": [func_type.upper(), None]}}} + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + m = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + if m: + op1 = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + operator_symbol = m.group(2).strip() + op2 = parse_reporter_or_value(m.group(3).strip(), pick_key_func) + opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func(opcode_map[operator_symbol]), "inputs": {"NUM1": op1, "NUM2": op2}}} + + # (costume ()) (looks_costumenumbername) + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("looks_costumenumbername"), "inputs": {}, "fields": {"NUMBER_NAME": [option, None]}}} + + # (backdrop ()) (looks_backdropnumbername) + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("looks_backdropnumbername"), "inputs": {}, "fields": {"NUMBER_NAME": [option, None]}}} + + # (distance to ()) (sensing_distanceto) + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("sensing_distanceto"), "inputs": {}, "fields": {"TARGET": [target_val, None]}}} + + # (current ()) (sensing_current) + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("sensing_current"), "inputs": {}, "fields": {"CURRENTMENU": [unit.upper(), None]}}} + + # (() of ()) (sensing_of) + m = re.search(r"\((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + prop = m.group(1).strip() + obj = m.group(2).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("sensing_of"), "inputs": {"OBJECT": {"kind": "menu", "option": obj}}, "fields": {"PROPERTY": [prop_map.get(prop, prop), None]}}} + + # (item (index) of [list v]) (data_itemoflist) + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + list_name = m.group(2).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_itemoflist"), "inputs": {"INDEX": index}, "fields": {"LIST": [list_name, None]}}} + + # (item # of [item] in [list v]) (data_itemnumoflist) + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item = m.group(1).strip() + list_name = m.group(2).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_itemnumoflist"), "inputs": {"ITEM": {"kind": "value", "value": item}}, "fields": {"LIST": [list_name, None]}}} + + raise ValueError(f"Can't parse reporter or value: {text}") + + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key → block_data + • opcode_keys: dict of opcode → list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... node objects ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + # This is a safeguard for cases where opcode_counts might be insufficient. + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + # classify each line into (opcode, node_type) + def classify(line): + l = line.lower().strip() + # Relaxed matching for "clicked" to "click" + if l.startswith("when green flag click"): return "event_whenflagclicked", "hat" + if l.startswith("when ") and " key pressed" in l: return "event_whenkeypressed", "hat" + if l.startswith("when this sprite click"): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if l.startswith("when i start as a clone"): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if l.startswith("set x to"): return "motion_setx", "stack" + if l.startswith("change y by"): return "motion_changeyby", "stack" + if l.startswith("set y to"): return "motion_sety", "stack" + if l == "if on edge, bounce": return "motion_ifonedgebounce", "stack" + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if l == "next costume": return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + if l.startswith("change ") and " effect by" in l: return "looks_changeeffectby", "stack" + if l.startswith("set ") and " effect to" in l: return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + if l.startswith("play sound ") and " until done" in l: return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + if l.startswith("wait ") and " seconds" in l: return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + if l.startswith("stop "): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + if l.startswith("delete ") and " of [" in l: return "data_deleteoflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + if l.startswith("ask ") and " and wait" in l: return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom blocks (procedures_call) - generic matching for "word (arg1) (arg2)" + # This is a heuristic and might need refinement based on actual custom block definitions. + # For the provided examples, "jump (50)" is the only one. + custom_block_match = re.match(r"([a-zA-Z_]+)\s*(\(.+?\))*\s*$", l) + if custom_block_match and custom_block_match.group(1) not in all_block_definitions: # Avoid matching built-in blocks + return "procedures_call", "stack" + + + raise ValueError(f"Unknown statement: {line!r}") + + # parse the boolean condition inside an `if` + def parse_condition(stmt): + stmt_lower = stmt.lower() + + # <() < ()> (operator_lt) + m = re.search(r"<\s*\(([^)]+)\)\s*<\s*\(([^)]+)\)\s*>", stmt_lower) + if m: + operand1 = parse_reporter_or_value(m.group(1).strip(), pick_key) + operand2 = parse_reporter_or_value(m.group(2).strip(), pick_key) + return { + "block": pick_key("operator_lt"), + "inputs": { + "OPERAND1": operand1, + "OPERAND2": operand2 + } + } + + # <() = ()> (operator_equals) + m = re.search(r"<\s*\(([^)]+)\)\s*=\s*\(([^)]+)\)\s*>", stmt_lower) + if m: + operand1 = parse_reporter_or_value(m.group(1).strip(), pick_key) + operand2 = parse_reporter_or_value(m.group(2).strip(), pick_key) + return { + "block": pick_key("operator_equals"), + "inputs": { + "OPERAND1": operand1, + "OPERAND2": operand2 + } + } + + # <() > ()> (operator_gt) + m = re.search(r"<\s*\(([^)]+)\)\s*>\s*\(([^)]+)\)\s*>", stmt_lower) + if m: + operand1 = parse_reporter_or_value(m.group(1).strip(), pick_key) + operand2 = parse_reporter_or_value(m.group(2).strip(), pick_key) + return { + "block": pick_key("operator_gt"), + "inputs": { + "OPERAND1": operand1, + "OPERAND2": operand2 + } + } + + # <<> and <>> (operator_and) + m = re.search(r"<\s*(.+?)\s*and\s*(.+?)\s*>", stmt_lower) + if m: + cond1_str = m.group(1).strip() + cond2_str = m.group(2).strip() + cond1 = parse_condition(cond1_str) + cond2 = parse_condition(cond2_str) + return { + "block": pick_key("operator_and"), + "inputs": { + "OPERAND1": cond1, + "OPERAND2": cond2 + } + } + + # <<> or <>> (operator_or) + m = re.search(r"<\s*(.+?)\s*or\s*(.+?)\s*>", stmt_lower) + if m: + cond1_str = m.group(1).strip() + cond2_str = m.group(2).strip() + cond1 = parse_condition(cond1_str) + cond2 = parse_condition(cond2_str) + return { + "block": pick_key("operator_or"), + "inputs": { + "OPERAND1": cond1, + "OPERAND2": cond2 + } + } + + # > (operator_not) + m = re.search(r"", stmt_lower) + if m: + cond_str = m.group(1).strip() + cond = parse_condition(cond_str) + return { + "block": pick_key("operator_not"), + "inputs": { + "OPERAND": cond + } + } + + # <() contains ()?> (operator_contains) + m = re.search(r"<\s*\[([^\]]+)\]\s*contains\s*\[([^\]]+)\]\s*\?>", stmt_lower) + if m: + string1 = parse_reporter_or_value(f"[{m.group(1).strip()}]", pick_key) + string2 = parse_reporter_or_value(f"[{m.group(2).strip()}]", pick_key) + return { + "block": pick_key("operator_contains"), + "inputs": { + "STRING1": string1, + "STRING2": string2 + } + } + + # (sensing_touchingobject) + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_lower) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option_val = "_mouse_" + elif option == "edge": option_val = "_edge_" + else: option_val = option + return { + "block": pick_key("sensing_touchingobject"), + "inputs": { + "TOUCHINGOBJECTMENU": {"kind": "menu", "option": option_val} + } + } + + # (sensing_touchingcolor) + m = re.search(r"touching color \[(#[0-9a-fA-F]{6})\]\?", stmt_lower) + if m: + color_value = m.group(1).strip() + return { + "block": pick_key("sensing_touchingcolor"), + "inputs": { + "COLOR": {"kind": "color", "value": color_value} + } + } + + # (sensing_coloristouchingcolor) + m = re.search(r"color \[(#[0-9a-fA-F]{6})\] is touching \[(#[0-9a-fA-F]{6})\]\?", stmt_lower) + if m: + color1 = m.group(1).strip() + color2 = m.group(2).strip() + return { + "block": pick_key("sensing_coloristouchingcolor"), + "inputs": { + "COLOR1": {"kind": "color", "value": color1}, + "COLOR2": {"kind": "color", "value": color2} + } + } + + # (sensing_keypressed) + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_lower) + if m: + key_option = m.group(1).strip() + return { + "block": pick_key("sensing_keypressed"), + "inputs": { + "KEY_OPTION": {"kind": "menu", "option": key_option} + } + } + + # (sensing_mousedown) + if stmt_lower == "mouse down?": + return { + "block": pick_key("sensing_mousedown"), + "inputs": {} + } + + # <[my list v] contains ()?> (data_listcontainsitem) + m = re.search(r"\[([^\]]+)\s*v\] contains \[(.+?)\]\?", stmt_lower) + if m: + list_name = m.group(1).strip() + item_value = m.group(2).strip() + return { + "block": pick_key("data_listcontainsitem"), + "inputs": { + "LIST": {"kind": "list_variable", "name": list_name}, + "ITEM": {"kind": "value", "value": item_value} + } + } + + raise ValueError(f"Can't parse condition: {stmt}") + + flow = [] + stack = [(-1, flow)] + + for raw in pseudo_code.splitlines(): + if not raw.strip(): continue + indent = (len(raw) - len(raw.lstrip())) // 2 + stmt = raw.strip().rstrip("then").rstrip("end") + + # pop to correct nesting level + while stack and stack[-1][0] >= indent: + stack.pop() + container = stack[-1][1] + + # classify & pick block key + opcode, ntype = classify(stmt) + key = pick_key(opcode) + node = {"block_key": key, "type": ntype} + info = generated_input[key] + + # add metadata + if ntype in ("hat", "c_block"): + node["description"] = info["block_name"] + if ntype == "stack" and info.get("fields"): + # extract variable name (if it's a variable-related block) + if "VARIABLE" in info["fields"]: + m = re.search(r"\[([^\]]+)\s*v\]", stmt) + if m: + node["variable"] = m.group(1) + + + # Parse inputs + node["inputs"] = {} + if info.get("inputs"): + for inp_name, spec in info["inputs"].items(): + # Handle VALUE input for data_setvariableto and data_changevariableby + if opcode in ["data_setvariableto", "data_changevariableby"] and inp_name == "VALUE": + value_match = re.search(r"to\s*(.+)|by\s*(.+)", stmt, re.IGNORECASE) + if value_match: + value_str = next(filter(None, value_match.groups())).strip() + node["inputs"]["VALUE"] = parse_reporter_or_value(value_str, pick_key) + continue + + # Handle inputs for custom blocks (procedures_call) + if opcode == "procedures_call": + args = re.findall(r"\(([^)]+)\)", stmt) + if args: + for i, arg_val_str in enumerate(args): + # Assuming generic argument names for custom blocks + node["inputs"][f"argument_name_{i+1}"] = parse_reporter_or_value(arg_val_str, pick_key) + continue # Move to next input/field + + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if spec[0] == 1 and spec[1] and isinstance(spec[1], list) and spec[1][0] == 4: # Numeric shadow block + m = None + if inp_name == "X": + m = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt, re.IGNORECASE) + elif inp_name == "Y": + m = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt, re.IGNORECASE) + elif inp_name == "SECS": + m = re.search(r"(?:glide|for|wait)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt, re.IGNORECASE) + if not m: # For glide secs to x: y: + m = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt, re.IGNORECASE) + elif inp_name == "STEPS": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt, re.IGNORECASE) + elif inp_name == "DEGREES": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt, re.IGNORECASE) + elif inp_name == "TIMES": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt, re.IGNORECASE) + elif inp_name == "CHANGE": + m = re.search(r"change\s*(?:size|volume)?\s*(?:\[[^\]]+\]\s*effect)?\s*by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt, re.IGNORECASE) + elif inp_name == "SIZE": + m = re.search(r"set size to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt, re.IGNORECASE) + elif inp_name == "VALUE" and opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"set\s*(?:volume|\[[^\]]+\]\s*effect)?\s*to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt, re.IGNORECASE) + elif inp_name == "NUM" and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt, re.IGNORECASE) + + if m: + val_str = next(filter(None, m.groups())) + node["inputs"][inp_name] = {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # String inputs (e.g., say [Hello!], ask [What?]) + elif spec[0] == 1 and spec[1] and isinstance(spec[1], list) and spec[1][0] == 10: # String shadow block + m = None + if inp_name == "MESSAGE": + m = re.search(r"(?:say|think)\s*\[([^\]]+)\]", stmt, re.IGNORECASE) + elif inp_name == "QUESTION": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt, re.IGNORECASE) + elif inp_name == "BROADCAST_INPUT": + m = re.search(r"broadcast\s*\[([^\]]+)\]", stmt, re.IGNORECASE) + elif inp_name == "ITEM" and opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt, re.IGNORECASE) + elif inp_name == "ITEM" and opcode == "data_insertatlist": + m = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt, re.IGNORECASE) + elif inp_name == "ITEM" and opcode == "data_replaceitemoflist": + m = re.search(r"with\s*\[([^\]]+)\]", stmt, re.IGNORECASE) + + if m: + node["inputs"][inp_name] = {"kind": "value", "value": m.group(1).strip()} + + # Dropdown inputs (e.g., go to [random position v], switch costume to [costume1 v]) + elif spec[0] == 1 and isinstance(spec[1], str) and spec[1].endswith("_menu"): # Menu block reference + m = None + if inp_name == "TO" and opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "TO" and opcode == "motion_glideto": + m = re.search(r"glide\s*\(.+?\)\s*secs to\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "TOWARDS" and opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "KEY_OPTION" and opcode == "sensing_keypressed": + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt, re.IGNORECASE) + elif inp_name == "TOUCHINGOBJECTMENU" and opcode == "sensing_touchingobject": + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt, re.IGNORECASE) + elif inp_name == "CLONE_OPTION" and opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "SOUND_MENU" and opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "COSTUME" and opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "BACKDROP" and opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait", "event_whenbackdropswitchesto"]: + m = re.search(r"(?:switch backdrop to|when backdrop switches to)\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "BROADCAST_INPUT" and opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "BROADCAST_OPTION" and opcode == "event_whenbroadcastreceived": + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "INDEX" and opcode in ["data_deleteoflist", "data_insertatlist", "data_replaceitemoflist"]: + m = re.search(r"(?:delete|insert|replace item)\s*(?:\()?\s*(\w+)\s*(?:\))?\s*(?:of|at)", stmt, re.IGNORECASE) + if m: + val_str = m.group(1) + if not val_str.isdigit(): # "all", "last", "random" + node["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + m = None # Prevent re-matching as numeric if it's a dropdown option + + + if m: + option = m.group(1).strip() + # Special mapping for certain menu options + if inp_name == "TO" and option == "random position": option = "_random_" + if inp_name == "TO" and option == "mouse-pointer": option = "_mouse_" + if inp_name == "TOWARDS" and option == "mouse-pointer": option = "_mouse_" + if inp_name == "CLONE_OPTION" and option == "myself": option = "_myself_" + if inp_name == "TOUCHINGOBJECTMENU" and option == "mouse-pointer": option = "_mouse_" + if inp_name == "TOUCHINGOBJECTMENU" and option == "edge": option = "_edge_" + node["inputs"][inp_name] = {"kind": "menu", "option": option} + + # Parse fields (dropdowns, variable names) + node["fields"] = {} + if info.get("fields"): + for field_name, field_spec in info["fields"].items(): + if field_name == "VARIABLE": + m = re.search(r"\[([^\]]+)\s*v\]", stmt) + if m: node["fields"]["VARIABLE"] = [m.group(1), None] + elif field_name == "LIST": + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt) + if m: node["fields"]["LIST"] = [m.group(1), None] + elif field_name == "STOP_OPTION": + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt) + if m: node["fields"]["STOP_OPTION"] = [m.group(1), None] + elif field_name == "STYLE": + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt) + if m: node["fields"]["STYLE"] = [m.group(1), None] + elif field_name == "DRAG_MODE": + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt) + if m: node["fields"]["DRAG_MODE"] = [m.group(1), None] + elif field_name == "EFFECT" and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt, re.IGNORECASE) + if m: node["fields"]["EFFECT"] = [m.group(1).upper(), None] + elif field_name == "NUMBER_NAME" and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + if m: node["fields"]["NUMBER_NAME"] = [m.group(1), None] + elif field_name == "FRONT_BACK" and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt, re.IGNORECASE) + if m: node["fields"]["FRONT_BACK"] = [m.group(1), None] + elif field_name == "FORWARD_BACKWARD" and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + if m: node["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + elif field_name == "OPERATOR" and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt, re.IGNORECASE) + if m: node["fields"]["OPERATOR"] = [m.group(1).upper(), None] + elif field_name == "CURRENTMENU" and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + if m: node["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + elif field_name == "PROPERTY" and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + node["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + elif field_name == "WHENGREATERTHANMENU" and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt, re.IGNORECASE) + if m: node["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + elif field_name == "KEY_OPTION" and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt, re.IGNORECASE) + if m: node["fields"]["KEY_OPTION"] = [m.group(1), None] + elif field_name == "BACKDROP" and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + if m: node["fields"]["BACKDROP"] = [m.group(1), None] + elif field_name == "BROADCAST_OPTION" and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + if m: node["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + + # condition & body for c_blocks + if ntype == "c_block": + # Extract the condition string from the statement + # For 'if then' or 'if then else' + cond_match = re.search(r"if\s*<(.+?)>\s*(?:then|then else)", raw.strip(), re.IGNORECASE) + if cond_match: + node["condition"] = parse_condition(cond_match.group(1).strip()) + # For 'repeat until ' + elif opcode == "control_repeat_until": + cond_match = re.search(r"repeat until\s*<(.+?)>", raw.strip(), re.IGNORECASE) + if cond_match: + node["condition"] = parse_condition(cond_match.group(1).strip()) + + node["body"] = [] + if opcode == "control_if_else": + node["body2"] = [] # For the 'else' part + + # next-list for hats + if ntype == "hat": + node["next"] = [] + + # attach & possibly push new container + container.append(node) + if ntype == "hat": + stack.append((indent, node["next"])) + elif ntype == "c_block": + stack.append((indent, node["body"])) + if opcode == "control_if_else": + # For if-else, the 'else' part is at the same indent level as 'if' + # but needs a separate container. This is a simplification. + # A more robust parser would need to detect the 'else' keyword. + # For now, assuming direct nesting. + pass # The 'else' part will be handled when 'else' line is processed. + + + # Post-processing to correctly assign body2 for if-else + # This is a bit of a hack given the line-by-line parsing. + # A proper AST builder would handle this more naturally. + # For this simplified model, we assume 'else' directly follows 'then' block. + final_flow = [] + i = 0 + while i < len(flow): + current_node = flow[i] + final_flow.append(current_node) + + if current_node.get("opcode") == "control_if_else" and "body" in current_node: + # Find the end of the 'then' body + then_body_end_idx = i + 1 + while then_body_end_idx < len(flow) and flow[then_body_end_idx].get("indent", -1) > current_node.get("indent", -1): + then_body_end_idx += 1 + + # Look for 'else' at the same indent level + if then_body_end_idx < len(flow) and \ + flow[then_body_end_idx].get("opcode") == "control_else" and \ + flow[then_body_end_idx].get("indent") == current_node.get("indent"): + + current_node["body2"] = flow[then_body_end_idx].get("body", []) + i = then_body_end_idx # Skip the 'else' block as it's now part of 'if-else' + i += 1 + + + return {"flow": flow} + +# Example input with opcodes for the initial generation +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_glidesecstoxy","count":1}, + {"opcode":"motion_xposition","count":3}, # Used multiple times in conditions + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":2}, # Two if blocks + {"opcode":"control_stop","count":2}, # stop all v, stop this script v + {"opcode":"operator_lt","count":1}, # Used in condition + {"opcode":"sensing_touchingobject","count":2}, # Used in condition, and for if on edge, bounce + {"opcode":"sensing_touchingobjectmenu","count":2}, # Menu for touchingobject + {"opcode":"event_broadcast","count":2}, # broadcast [Game Over v], broadcast [jump v] + {"opcode":"data_setvariableto","count":3}, # set [score v] to (1), set [speed v] to (1), set [other sprite X v] to ( (x position) of [Sprite2 v] ) + {"opcode":"data_showvariable","count":2}, # show variable [score v], show variable [speed v] + {"opcode":"operator_add","count":2}, # For set [var] to ((var) + (val)), (number 1) + (number 2) + {"opcode":"data_variable","count":5}, # For variable reporters like (score) or [score v] + {"opcode":"looks_sayforsecs","count":2}, # For "say [Hello!] for (2) seconds", say [You win!] for (2) seconds + {"opcode":"looks_say","count":2}, # For "say [Hello! v]", say [Welcome to my game! v] + {"opcode":"motion_movesteps","count":3}, # For "move (10) steps" + {"opcode":"control_wait","count":3}, # For "wait (0.1) seconds", wait (0.5) seconds, wait (1) seconds + {"opcode":"motion_changeyby","count":2}, # For "change y by (10)" + {"opcode":"motion_pointindirection","count":1}, # For "point in direction (90)" + {"opcode":"event_whenkeypressed","count":3}, # For "when [space v] key pressed", when [up arrow v] key pressed, when [right arrow v] key pressed, when [left arrow v] key pressed + {"opcode":"control_repeat","count":2}, # For "repeat (10)" + {"opcode":"event_whenthisspriteclicked","count":2}, # For "when this sprite clicked" + {"opcode":"looks_costumenumbername","count":1}, # For "(costume [name v])" + {"opcode":"operator_join","count":3}, # For "join [Hello ] (answer)", join (length of [shopping list v]) [ items in the list.], join [Hello, ] (username) + {"opcode":"sensing_answer","count":1}, # For "(answer)" + {"opcode":"looks_hide","count":2}, # For "hide" + {"opcode":"control_create_clone_of","count":2}, # For "create clone of [myself v]" + {"opcode":"control_start_as_clone","count":2}, # For "when I start as a clone" + {"opcode":"operator_random","count":1}, # For "pick random -240 to 240" + {"opcode":"motion_ifonedgebounce","count":2}, # For "if on edge, bounce" + {"opcode":"operator_gt","count":2}, # For "if <(score) > (10)> then", if <(item # of [Dog] in [myList v])> (0)> then + {"opcode":"control_if_else","count":1}, # For "if <(score) > (10)> then else" + {"opcode":"sound_play","count":2}, # Changed from sound_start to sound_play + {"opcode":"sensing_loudness","count":2}, # For "(loudness)" + {"opcode":"event_whengreaterthan","count":1}, # For "when [loudness v] > (70)" + {"opcode":"control_repeat_until","count":1}, # For "repeat until " + {"opcode":"looks_cleargraphiceffects","count":1}, # For "clear graphic effects" + {"opcode":"looks_changeeffectby","count":2}, # For "change [color v] effect by (50)", change [fisheye v] effect by (5) + {"opcode":"looks_seteffectto","count":1}, # For "set [ghost v] effect to (75)" + {"opcode":"looks_setsizeto","count":1}, # For "set size to (50) %" + {"opcode":"looks_changesizeby","count":1}, # For "change size by (5)" + {"opcode":"looks_nextcostume","count":2}, # For "next costume" + {"opcode":"looks_switchbackdroptowait","count":1}, # For "switch backdrop to [game over v] and wait" + {"opcode":"looks_nextbackdrop","count":1}, # For "next backdrop" + {"opcode":"sound_playuntildone","count":2}, # For "play sound [fanfare v] until done" + {"opcode":"sound_stopallsounds","count":2}, # For "stop all sounds" + {"opcode":"sound_changevolumeby","count":1}, # For "change volume by (-5)" + {"opcode":"sound_setvolumeto","count":1}, # For "set volume to (50) %" + {"opcode":"sensing_resettimer","count":2}, # For "reset timer" + {"opcode":"sensing_setdragmode","count":2}, # For "set drag mode [not draggable v]" + {"opcode":"data_addtolist","count":1}, # For "add [apple] to [shopping list v]" + {"opcode":"data_deleteoflist","count":1}, # For "delete (all) of [my list v]" + {"opcode":"data_insertatlist","count":1}, # For "insert [orange] at (2) of [fruits v]" + {"opcode":"data_replaceitemoflist","count":1}, # For "replace item (1) of [colors v] with [blue]" + {"opcode":"data_listcontainsitem","count":1}, # For "<[inventory v] contains [key]?>" + {"opcode":"data_itemoflist","count":1}, # For "(item (2) of [myList v])" + {"opcode":"data_lengthoflist","count":2}, # For "(length of [shopping list v])" + {"opcode":"data_itemnumoflist","count":1}, # For "(item # of [Dog] in [myList v])" + {"opcode":"sensing_touchingcolor","count":1}, # For "" + {"opcode":"sensing_coloristouchingcolor","count":1}, # For "" + {"opcode":"operator_and","count":1}, # For "< and >" + {"opcode":"operator_or","count":1}, # For "< or >" + {"opcode":"operator_not","count":1}, # For ">" + {"opcode":"operator_contains","count":1}, # For "<[answer] contains [yes]?>" + {"opcode":"procedures_call","count":1}, # For "jump (50)" + {"opcode":"procedures_definition","count":1}, # For "define jump (height)" + {"opcode":"sensing_of","count":1}, # For "(x position) of [Sprite2 v]" + {"opcode":"sensing_current","count":1}, # For "(current [hour v])" + {"opcode":"sensing_mousex","count":1}, # For "(mouse x)" + {"opcode":"sensing_mousey","count":1}, # For "(mouse y)" + {"opcode":"operator_subtract","count":1}, # For "((10) - (4))" + {"opcode":"operator_multiply","count":1}, # For "(6) * (7)" + {"opcode":"operator_divide","count":1}, # For "((20) / (5))" + {"opcode":"data_list","count":1}, # For "[my list v]" + {"opcode":"looks_gotofrontback","count":1}, # For "go to [front v] layer" + {"opcode":"looks_goforwardbackwardlayers","count":1}, # For "go [forward v] (1) layers" + {"opcode":"sound_sounds_menu","count":2}, # For sound menus + {"opcode":"motion_goto_menu","count":1}, # For motion_goto menu + {"opcode":"motion_glideto_menu","count":1}, # For motion_glideto menu + {"opcode":"motion_pointtowards_menu","count":1}, # For motion_pointtowards menu + {"opcode":"sensing_keyoptions","count":1}, # For sensing_keypressed menu + {"opcode":"sensing_of_object_menu","count":1}, # For sensing_of menu + {"opcode":"control_create_clone_of_menu","count":1}, # For control_create_clone_of menu + {"opcode":"looks_costume","count":1}, # For looks_switchcostumeto menu + {"opcode":"looks_backdrops","count":1}, # For looks_switchbackdropto menu +] + +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + +# Example pseudo-code inputs from the JSON files +pseudo_code_examples = [ + # From motion_block.json + """ +when green flag clicked + go to x: (0) y: (0) + point in direction (90) + move (50) steps +end + """, + """ +when [right arrow v] key pressed + turn right (15) degrees +end + """, + """ +when this sprite clicked + go to [mouse-pointer v] + """, + """ +when green flag clicked + glide (2) secs to x: (150) y: (-100) + glide (2) secs to x: (-150) y: (100) +end + """, + """ +when green flag clicked + forever + point towards [mouse-pointer v] + move (5) steps + end +end + """, + """ +when [right arrow v] key pressed + change x by (10) +end + """, + """ +when green flag clicked + set x to (0) + set y to (0) +end + """, + """ +when [up arrow v] key pressed + change y by (10) +end + """, + """ +when green flag clicked + forever + move (10) steps + if on edge, bounce + end +end + """, + """ +when green flag clicked + set rotation style [left-right v] + forever + move (10) steps + if on edge, bounce + end +end + """, + # From looks_block.json + """ +when green flag clicked + say [Grr] for (3) seconds + say [Have you seen my honey? v] for (3) seconds +end + """, + """ +when green flag clicked + say [Welcome to my game! v] + wait (2) seconds + say [] +end + """, + """ +when this sprite clicked + think [What should I do? v] for (2) seconds +end + """, + """ +when I receive [correct answer v] + think [That's right! v] + wait (1) seconds + think [good v] +end + """, + """ +when I receive [explosion v] + repeat (5) + next costume + end + hide +end + """, + """ +when green flag clicked + forever + next costume + wait (0.2) seconds + end +end + """, + """ +when green flag clicked + switch backdrop to [start screen v] +end + """, + """ +broadcast [game over v] + switch backdrop to [game over v] and wait + stop [all v] +end + """, + """ +when [space v] key pressed + next backdrop +end + """, + """ +when green flag clicked + repeat (10) + change size by (5) + wait (0.1) seconds + end +end + """, + """ +when green flag clicked + set size to (50) % + wait (1) seconds + set size to (100) % +end + """, + """ +when green flag clicked + forever + change [color v] effect by (5) + wait (0.1) seconds + end +end + """, + """ +when green flag clicked + set [ghost v] effect to (75) +end + """, + """ +when green flag clicked + change [color v] effect by (50) + wait (2) seconds + clear graphic effects +end + """, + """ +when green flag clicked + hide +when I receive [start game v] + show +end + """, + """ +when green flag clicked + hide +end + """, + """ +when green flag clicked + go to [front v] layer +end + """, + """ +when this sprite clicked + go [forward v] (1) layers +end + """, + """ +say join [I am costume ] (costume [name v]) + """, + """ +say join [Current backdrop: ] (backdrop [name v]) for (2) seconds + """, + """ +set size to ( (size) + (10) ) + """, + # From sound_block.json + """ +when backdrop switches to [winning screen v] + play sound [fanfare v] until done + say [You won!] for (2) seconds +end + """, + """ +forever + play sound [Music v] until done +end + """, + """ +when this sprite clicked + start sound [Pop v] + change [score v] by (1) +end + """, + """ +when I receive [game over v] + stop all sounds +end + """, + """ +when [down arrow v] key pressed + change volume by (-5) +end + """, + """ +when green flag clicked + set volume to (50) % +end + """, + """ +say join [Current volume: ] (volume) + """, + # From event_block.json + """ +when green flag clicked + go to x: (0) y: (0) + say [Hello!] for (2) seconds +end + """, + """ +when [space v] key pressed + repeat (10) + change y by (10) + wait (0.1) seconds + change y by (-10) + end +end + """, + """ +when [right arrow v] key pressed + point in direction (90) + move (10) steps +end + """, + """ +when this sprite clicked + say [Ouch!] for (1) seconds + change [score v] by (-1) +end + """, + """ +when backdrop switches to [game over v] + stop [all v] +end + """, + """ +when [loudness v] > (70) + start sound [scream v] +end + """, + """ +when I receive [start game v] + show + go to x: (0) y: (0) +end + """, + """ +when I receive [game over v] + set score to 0 + stop [all v] +end + """, + """ +if then + broadcast [jump v] +end + """, + """ +broadcast [initialize sprites v] and wait + say [Game Started!] for (2) seconds + """, + # From control_block.json + """ +say [Hello!] for (1) seconds + wait (0.5) seconds + say [Goodbye!] for (1) seconds + """, + """ +when green flag clicked + repeat (10) + move (10) steps + wait (0.1) seconds + end +end + """, + """ +when green flag clicked + forever + move (5) steps + if on edge, bounce + end +end + """, + """ +forever + if then + stop [this script v] + end +end + """, + """ +if <(score) > (10)> then + say [You win!] for (2) seconds +else + say [Keep trying!] for (2) seconds +end + """, + """ +repeat until + move (5) steps +end + """, + """ +if <(health) = (0)> then + stop [all v] +end + """, + """ +when I start as a clone + wait until + delete this clone +end + """, + """ +when I start as a clone + go to x: (pick random -240 to 240) y: (pick random -180 to 180) + show + forever + move (10) steps + if on edge, bounce + end +end + """, + """ +when I start as a clone + wait (5) seconds + delete this clone +end + """, + """ +when green flag clicked + hide + forever + create clone of [myself v] + wait (1) seconds + end + """, + # From data_block.json + """ +when green flag clicked + set [score v] to (0) + set [player name v] to [Guest] +end + """, + """ +when this sprite clicked + change [score v] by (1) +end + """, + """ +when green flag clicked + add [apple] to [shopping list v] + add [banana] to [shopping list v] +end + """, + """ +when green flag clicked + delete (all) of [my list v] +end + """, + """ +insert [orange] at (2) of [fruits v] + """, + """ +replace item (1) of [colors v] with [blue] + """, + """ +when green flag clicked + show variable [score v] +end + """, + """ +when I receive [game over v] + hide variable [score v] +end + """, + """ +when green flag clicked + show list [shopping list v] +end + """, + """ +when I receive [game over v] + hide list [shopping list v] +end + """, + """ +say ([score v]) for (2) seconds + """, + """ +say ([my list v]) + """, + """ +say (item (2) of [myList v]) for 2 seconds + """, + """ +say join (length of [shopping list v]) [ items in the list.] + """, + """ +if <(item # of [Dog] in [myList v])> (0)> then + say join [Dog found at position ] (item # of [Dog] in [my list v]) +end + """, + # From reporter_blocks.json (some already covered by other categories) + """ +when green flag clicked + say (x position) for (2) seconds +end + """, + """ +set [worms v] to (y position) + """, + """ +when green flag clicked + say (direction) for (2) seconds +end + """, + """ +say join [I am costume ] (costume [name v]) + """, + """ +set size to ( (size) + (10) ) + """, + """ +say join [Current backdrop: ] (backdrop [name v]) for (2) seconds + """, + """ +say join [Current volume: ] (volume) + """, + """ +if <(distance to [Sprite2 v]) < (50)> then + say [Too close!] +end + """, + """ +ask [What is your name?] and wait + say join [Hello ] (answer) + """, + """ +go to x: (mouse x) y: (mouse y) + """, + """ +if <(mouse y) < (0)> then + say [Below center] +end + """, + """ +when green flag clicked + forever + if <(loudness) > (30)> then + start sound [pop v] +end + """, + """ +when green flag clicked + reset timer + wait (5) seconds + say join [Time elapsed: ] (timer) +end + """, + """ +set [other sprite X v] to ( (x position) of [Sprite2 v] ) + """, + """ +say join [The current hour is ] (current [hour v]) + """, + """ +say join [Days passed: ] (days since 2000) + """, + """ +say join [Hello, ] (username) + """, + """ +set [total v] to ( (number 1) + (number 2) ) + """, + """ +set [difference v] to ( (number 1) - (number 2) ) + """, + """ +set [area v] to ( (length) * (width) ) + """, + """ +set [average v] to ( (total score) / (number of students) ) + """, + """ +go to x: (pick random -240 to 240) y: (pick random -180 to 180) + """, + """ +say (join [Hello ][World!]) + """, + """ +say (letter (1) of [apple]) + """, + """ +say (length of [banana]) + """, + """ +if <([number v] mod (2) = (0))> then + say [Even number] +end + """, + """ +set [rounded score v] to (round (score)) + """, + """ +set [distance v] to ([sqrt v] of ( ( (x position) * (x position) ) + ( (y position) * (y position) ) )) + """, + # From boolean_blocks.json (conditions already covered by parse_condition) + """ +if <(score) < (10)> then + say [Keep trying!] +end + """, + """ +if <(answer) = (5)> then + say [Correct!] +end + """, + """ +if <([health v]) > (0)> then + move (10) steps +else + stop [all v] +end + """, + """ +if < and > then + say [You're clicking me!] +end + """, + """ +if < or > then + change x by (-10) +end + """, + """ +if > then + say [I'm safe!] +end + """, + """ +if <[answer] contains [yes]?> then + say [Great!] +end + """, + """ +if then + broadcast [Game Over v] +end + """, + """ +if then + bounce off edge +end + """, + """ +if then + change [health v] by (-1) +end + """, + """ +if then + say [Collision!] +end + """, + """ +forever + if then + broadcast [shoot v] + end +end + """, + """ +if then + go to mouse-pointer +end + """, + """ +if <[inventory v] contains [key]?> then + say [You have the key!] +end + """, + # Custom block example + """ +define jump (height) + change y by (height) + wait (0.5) seconds + change y by (0 - (height)) +end + +when green flag clicked + jump (50) +end + """ +] +txt="" +trace="" +# Process each example and print the plan +for i, pseudo_code_input in enumerate(pseudo_code_examples): + print(f"\n--- Processing Example {i+1} ---") + print(pseudo_code_input.strip()) + try: + # Regenerate blocks and opcode_occurrences for each run to ensure fresh keys + # This is important because pick_key uses a defaultdict that persists state. + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) + print(json.dumps(plan, indent=2)) + txt += str(plan) + " \n" + except Exception as e: + print(f"Error processing example: {e}") + import traceback + #traceback.print_exc() + trace += str(e) + " \n" + +with open("all_analysis.txt", "w", encoding="utf-8") as f: + f.write(txt) + +with open("all_analysis_trace.txt", "w", encoding="utf-8") as f: + f.write(trace) \ No newline at end of file diff --git a/utils/plan_generator_3.py b/utils/plan_generator_3.py new file mode 100644 index 0000000000000000000000000000000000000000..ac8cc83a6efdf25d3b964dcefd6e3a7e631b212f --- /dev/null +++ b/utils/plan_generator_3.py @@ -0,0 +1,2319 @@ +import json +import copy +import re +from collections import defaultdict + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False + + generated_blocks[main_key] = main_block_data + + # Handle menus + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_key + + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 1 and \ + main_block_data["inputs"][input_name][0] == 1: + + main_block_data["inputs"][input_name][1] = menu_key + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_distanceto": { # Added sensing_distanceto + "block_name": "(distance to ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto", + "functionality": "Reports the distance from the sprite to the mouse-pointer or another specified sprite.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": False, "topLevel": True + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": [ + { + "name": "PROCCONTAINER", + "type": "block_prototype" + } + ], + "fields": {}, + "shadow": False, + "topLevel": True + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": [], # Inputs are dynamic based on definition + "fields": {}, + "shadow": False, + "topLevel": True + } +} + +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, pick_key_func): + text = text.strip() + + # Check for numeric literal (including parenthesized numbers like "(0)" or "(10)") + m_num = re.fullmatch(r"\(?\s*(-?\d+(\.\d+)?)\s*\)?", text) + if m_num: + val_str = m_num.group(1) + return {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # Check for string literal (e.g., "[Hello!]") + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + # Check for simple reporters, potentially with outer parentheses + m_simple_reporter = re.fullmatch(r"\((.+?)\)", text) + if m_simple_reporter: + inner_text = m_simple_reporter.group(1).strip() + if inner_text in simple_reporters: + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func(simple_reporters[inner_text]), "inputs": {}}} + # Also check for simple reporters without parentheses (e.g., if passed directly) + if text in simple_reporters: + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func(simple_reporters[text]), "inputs": {}}} + + + # Variable reporter: [score v] or (score) or just "score" + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_variable"), "inputs": {}, "fields": {"VARIABLE": [var_name, None]}}} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + # Ensure it's not a simple reporter already handled, or a number + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_variable"), "inputs": {}, "fields": {"VARIABLE": [potential_var_name, None]}}} + # Handle plain variable names like "score", "number 1", "total score" + if re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_ ]*", text): # Allow spaces for "number 1" etc. + # Exclude known simple reporters that don't have 'v' or parentheses + if text not in simple_reporters: + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_variable"), "inputs": {}, "fields": {"VARIABLE": [text, None]}}} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_list"), "inputs": {}, "fields": {"LIST": [list_name, None]}}} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + max_val = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_random"), "inputs": {"FROM": min_val, "TO": max_val}}} + + # (join ()()) (operator_join) - handle both [] and () for inputs + m = re.search(r"join \((.+?)\) \((.+?)\)", text) # Try (val) (val) + if not m: + m = re.search(r"join \[(.+?)\] \[(.+?)\]", text) # Try [val] [val] + if m: + str1 = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + str2 = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_join"), "inputs": {"STRING1": str1, "STRING2": str2}}} + + # letter () of () (operator_letterof) - handle both [] and () for inputs + m = re.search(r"letter \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + string_val = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_letterof"), "inputs": {"LETTER": index, "STRING": string_val}}} + + # (length of ()) (operator_length) - handle both [] and () for inputs + m = re.search(r"length of \((.+?)\)", text) + if not m: + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + list_or_string_val = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_length"), "inputs": {"STRING": list_or_string_val}}} + + + # (() mod ()) (operator_mod) + m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + if m: + num1 = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + num2 = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_mod"), "inputs": {"NUM1": num1, "NUM2": num2}}} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_round"), "inputs": {"NUM": num}}} + + # (() of ()) (operator_mathop) - handle variable for function type + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) # e.g. [sqrt v] of ((x pos) * (x pos)) + if m: + func_type = m.group(1).strip() + value = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_mathop"), "inputs": {"NUM": value}, "fields": {"OPERATOR": [func_type.upper(), None]}}} + # Also handle direct string for function type (e.g., "abs of (x)") + m = re.search(r"([a-zA-Z]+)\s*of\s*\((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("operator_mathop"), "inputs": {"NUM": value}, "fields": {"OPERATOR": [func_type.upper(), None]}}} + + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + m = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + if m: + op1 = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + operator_symbol = m.group(2).strip() + op2 = parse_reporter_or_value(m.group(3).strip(), pick_key_func) + opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func(opcode_map[operator_symbol]), "inputs": {"NUM1": op1, "NUM2": op2}}} + + # (costume ()) (looks_costumenumbername) - handle with or without 'v' + m = re.search(r"costume \((.+?)\)", text) + if not m: + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("looks_costumenumbername"), "inputs": {}, "fields": {"NUMBER_NAME": [option, None]}}} + + # (backdrop ()) (looks_backdropnumbername) - handle with or without 'v' + m = re.search(r"backdrop \((.+?)\)", text) + if not m: + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("looks_backdropnumbername"), "inputs": {}, "fields": {"NUMBER_NAME": [option, None]}}} + + # (distance to ()) (sensing_distanceto) - handle with or without 'v' + m = re.search(r"distance to \((.+?)\)", text) + if not m: + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("sensing_distanceto"), "inputs": {}, "fields": {"TARGET": [target_val, None]}}} + + # (current ()) (sensing_current) - handle with or without 'v' + m = re.search(r"current \((.+?)\)", text) + if not m: + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("sensing_current"), "inputs": {}, "fields": {"CURRENTMENU": [unit.upper(), None]}}} + + # (() of ()) (sensing_of) - handle both variable and non-variable properties, and objects + m = re.search(r"\((.+?)\) of \((.+?)\)", text) # (prop) of (obj) + if not m: + m = re.search(r"\((.+?)\) of \[([^\]]+)\s*v\]", text) # (prop) of [obj v] + if m: + prop = m.group(1).strip() + obj = m.group(2).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + # The object can be a sprite name or "_stage_" + obj_kind = "menu" + if obj.lower() == "stage": obj_val = "_stage_" + elif obj.lower() == "myself": obj_val = "_myself_" + else: obj_val = obj # Assume it's a sprite name + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("sensing_of"), "inputs": {"OBJECT": {"kind": obj_kind, "option": obj_val}}, "fields": {"PROPERTY": [prop_map.get(prop, prop), None]}}} + + # (item (index) of [list v]) (data_itemoflist) - handle with or without 'v' and parentheses for index + m = re.search(r"item \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + list_name = m.group(2).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_itemoflist"), "inputs": {"INDEX": index}, "fields": {"LIST": [list_name, None]}}} + + # (item # of [item] in [list v]) (data_itemnumoflist) - handle with or without 'v' and parentheses for item + m = re.search(r"item # of \((.+?)\) in \((.+?)\)", text) + if not m: + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + list_name = m.group(2).strip() + return {"kind": "nested_reporter", "reporter": {"block": pick_key_func("data_itemnumoflist"), "inputs": {"ITEM": item}, "fields": {"LIST": [list_name, None]}}} + + + raise ValueError(f"Can't parse reporter or value: {text}") + +def parse_condition(stmt, pick_key_func): + stmt_lower = stmt.lower() + + # <() < ()> (operator_lt) + m = re.search(r"<\s*\(([^)]+)\)\s*<\s*\(([^)]+)\)\s*>", stmt_lower) + if m: + operand1 = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + operand2 = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return { + "block": pick_key_func("operator_lt"), + "inputs": { + "OPERAND1": operand1, + "OPERAND2": operand2 + } + } + + # <() = ()> (operator_equals) + m = re.search(r"<\s*\(([^)]+)\)\s*=\s*\(([^)]+)\)\s*>", stmt_lower) + if m: + operand1 = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + operand2 = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return { + "block": pick_key_func("operator_equals"), + "inputs": { + "OPERAND1": operand1, + "OPERAND2": operand2 + } + } + + # <() > ()> (operator_gt) + m = re.search(r"<\s*\(([^)]+)\)\s*>\s*\(([^)]+)\)\s*>", stmt_lower) + if m: + operand1 = parse_reporter_or_value(m.group(1).strip(), pick_key_func) + operand2 = parse_reporter_or_value(m.group(2).strip(), pick_key_func) + return { + "block": pick_key_func("operator_gt"), + "inputs": { + "OPERAND1": operand1, + "OPERAND2": operand2 + } + } + + # <<> and <>> (operator_and) + m = re.search(r"<\s*(.+?)\s*and\s*(.+?)\s*>", stmt_lower) + if m: + cond1_str = m.group(1).strip() + cond2_str = m.group(2).strip() + cond1 = parse_condition(cond1_str, pick_key_func) + cond2 = parse_condition(cond2_str, pick_key_func) + return { + "block": pick_key_func("operator_and"), + "inputs": { + "OPERAND1": cond1, + "OPERAND2": cond2 + } + } + + # <<> or <>> (operator_or) + m = re.search(r"<\s*(.+?)\s*or\s*(.+?)\s*>", stmt_lower) + if m: + cond1_str = m.group(1).strip() + cond2_str = m.group(2).strip() + cond1 = parse_condition(cond1_str, pick_key_func) + cond2 = parse_condition(cond2_str, pick_key_func) + return { + "block": pick_key_func("operator_or"), + "inputs": { + "OPERAND1": cond1, + "OPERAND2": cond2 + } + } + + # > (operator_not) + m = re.search(r"", stmt_lower) + if m: + cond_str = m.group(1).strip() + cond = parse_condition(cond_str, pick_key_func) + return { + "block": pick_key_func("operator_not"), + "inputs": { + "OPERAND": cond + } + } + + # <() contains ()?> (operator_contains) + m = re.search(r"<\s*\[([^\]]+)\]\s*contains\s*\[([^\]]+)\]\s*\?>", stmt_lower) + if m: + string1 = parse_reporter_or_value(f"[{m.group(1).strip()}]", pick_key_func) + string2 = parse_reporter_or_value(f"[{m.group(2).strip()}]", pick_key_func) + return { + "block": pick_key_func("operator_contains"), + "inputs": { + "STRING1": string1, + "STRING2": string2 + } + } + + # (sensing_touchingobject) + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_lower) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option_val = "_mouse_" + elif option == "edge": option_val = "_edge_" + else: option_val = option + return { + "block": pick_key_func("sensing_touchingobject"), + "inputs": { + "TOUCHINGOBJECTMENU": {"kind": "menu", "option": option_val} + } + } + + # (sensing_touchingcolor) + m = re.search(r"touching color \[(#[0-9a-fA-F]{6})\]\?", stmt_lower) + if m: + color_value = m.group(1).strip() + return { + "block": pick_key_func("sensing_touchingcolor"), + "inputs": { + "COLOR": {"kind": "color", "value": color_value} + } + } + + # (sensing_coloristouchingcolor) + m = re.search(r"color \[(#[0-9a-fA-F]{6})\] is touching \[(#[0-9a-fA-F]{6})\]\?", stmt_lower) + if m: + color1 = m.group(1).strip() + color2 = m.group(2).strip() + return { + "block": pick_key_func("sensing_coloristouchingcolor"), + "inputs": { + "COLOR1": {"kind": "color", "value": color1}, + "COLOR2": {"kind": "color", "value": color2} + } + } + + # (sensing_keypressed) + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_lower) + if m: + key_option = m.group(1).strip() + return { + "block": pick_key_func("sensing_keypressed"), + "inputs": { + "KEY_OPTION": {"kind": "menu", "option": key_option} + } + } + + # (sensing_mousedown) + if stmt_lower == "mouse down?": + return { + "block": pick_key_func("sensing_mousedown"), + "inputs": {} + } + + # <[my list v] contains ()?> (data_listcontainsitem) + m = re.search(r"\[([^\]]+)\s*v\] contains \[(.+?)\]\?", stmt_lower) + if m: + list_name = m.group(1).strip() + item_value = m.group(2).strip() + return { + "block": pick_key_func("data_listcontainsitem"), + "inputs": { + "LIST": {"kind": "list_variable", "name": list_name}, + "ITEM": {"kind": "value", "value": item_value} + } + } + + raise ValueError(f"Can't parse condition: {stmt}") + + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key → block_data + • opcode_keys: dict of opcode → list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... node objects ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + # This is a safeguard for cases where opcode_counts might be insufficient. + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + # classify each line into (opcode, node_type) + def classify(line): + l = line.lower().strip() + + # Hat Blocks (most specific first) + if re.match(r"when green flag click(ed)?", l): return "event_whenflagclicked", "hat" + if re.match(r"when (.+?) key press(ed)?", l): return "event_whenkeypressed", "hat" + if re.match(r"when this sprite click(ed)?", l): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if re.match(r"when i start as a clo(ne)?", l): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + + # Motion Blocks + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if l.startswith("set x to"): return "motion_setx", "stack" + if l.startswith("change y by"): return "motion_changeyby", "stack" + if l.startswith("set y to"): return "motion_sety", "stack" + if re.match(r"if on edge, bounce( off edge)?", l): return "motion_ifonedgebounce", "stack" + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + if re.match(r"bounce off edg(e)?", l): return "motion_ifonedgebounce", "stack" + + # Looks Blocks + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if re.match(r"next costum(e)?", l): return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + if l.startswith("change ") and " effect by" in l: return "looks_changeeffectby", "stack" + if l.startswith("set ") and " effect to" in l: return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + # Sound Blocks + if re.match(r"play sound (.+?) until do(ne)?", l): return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + # Event Blocks (broadcasts) + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + # Control Blocks + if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + if l.startswith("stop "): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + # Data Blocks + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + if l.startswith("delete ") and " of [" in l: return "data_deleteoflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + # Sensing Blocks + if re.match(r"ask (.+?) and wai(t)?", l): return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom Blocks (procedures_call) - specific rule for "call" + if l.startswith("call "): + return "procedures_call", "stack" + + # Custom Blocks (procedures_call) - LAST RESORT (generic match) + # This regex is for custom blocks that are *not* defined as standard blocks. + # It should be the final check for stack-type blocks. + custom_block_match = re.match(r"([a-zA-Z_][a-zA-Z0-9_ ]*)\s*(\(.+?\))*\s*$", l) + if custom_block_match: + return "procedures_call", "stack" + + raise ValueError(f"Unknown statement: {line!r}") + + + flow = [] + stack = [(-1, flow)] + + for raw in pseudo_code.splitlines(): + if not raw.strip(): continue + indent = (len(raw) - len(raw.lstrip())) // 2 + stmt = raw.strip().rstrip("then").rstrip("end") + + # pop to correct nesting level + while stack and stack[-1][0] >= indent: + stack.pop() + container = stack[-1][1] + + # classify & pick block key + opcode, ntype = classify(stmt) + key = pick_key(opcode) + node = {"block_key": key, "type": ntype} + info = generated_input[key] + + # add metadata + if ntype in ("hat", "c_block"): + node["description"] = info["block_name"] + if ntype == "stack" and info.get("fields"): + # extract variable name (if it's a variable-related block) + if "VARIABLE" in info["fields"]: + m = re.search(r"\[([^\]]+)\s*v\]", stmt) + if m: + node["variable"] = m.group(1) + + + # Parse inputs + node["inputs"] = {} + if info.get("inputs"): # Check if 'inputs' key exists and is not None + if isinstance(info["inputs"], dict): # Handle dictionary inputs (most common case) + for inp_name, spec in info["inputs"].items(): + # Handle VALUE input for data_setvariableto and data_changevariableby + if opcode in ["data_setvariableto", "data_changevariableby"] and inp_name == "VALUE": + value_match = re.search(r"to\s*(.+)|by\s*(.+)", stmt, re.IGNORECASE) + if value_match: + value_str = next(filter(None, value_match.groups())).strip() + node["inputs"]["VALUE"] = parse_reporter_or_value(value_str, pick_key) + continue + + # Handle inputs for custom blocks (procedures_call) - these are dynamic + if opcode == "procedures_call": + # For "call moveBall" + if not re.search(r"\(.+\)", stmt): # No parentheses, no arguments + continue # No inputs to parse for this block + + args = re.findall(r"\(([^)]+)\)", stmt) + if args: + for i, arg_val_str in enumerate(args): + # Assuming generic argument names for custom blocks + node["inputs"][f"argument_name_{i+1}"] = parse_reporter_or_value(arg_val_str, pick_key) + continue # Move to next input/field, as custom block inputs are handled as a group + + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if spec[0] == 1 and spec[1] and isinstance(spec[1], list) and spec[1][0] == 4: # Numeric shadow block + m = None + if inp_name == "X": + m = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt, re.IGNORECASE) + elif inp_name == "Y": + m = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt, re.IGNORECASE) + elif inp_name == "SECS": + m = re.search(r"(?:glide|for|wait)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt, re.IGNORECASE) + if not m: # For glide secs to x: y: + m = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt, re.IGNORECASE) + elif inp_name == "STEPS": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt, re.IGNORECASE) + elif inp_name == "DEGREES": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt, re.IGNORECASE) + elif inp_name == "TIMES": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt, re.IGNORECASE) + elif inp_name == "CHANGE": + m = re.search(r"change\s*(?:size|volume)?\s*(?:\[[^\]]+\]\s*effect)?\s*by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt, re.IGNORECASE) + elif inp_name == "SIZE": + m = re.search(r"set size to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt, re.IGNORECASE) + elif inp_name == "VALUE" and opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"set\s*(?:volume|\[[^\]]+\]\s*effect)?\s*to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt, re.IGNORECASE) + elif inp_name == "NUM" and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt, re.IGNORECASE) + + if m: + val_str = next(filter(None, m.groups())) + node["inputs"][inp_name] = {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # String inputs (e.g., say [Hello!], ask [What?]) + elif spec[0] == 1 and spec[1] and isinstance(spec[1], list) and spec[1][0] == 10: # String shadow block + m = None + if inp_name == "MESSAGE": + m = re.search(r"(?:say|think)\s*\[([^\]]+)\]", stmt, re.IGNORECASE) + elif inp_name == "QUESTION": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt, re.IGNORECASE) + elif inp_name == "BROADCAST_INPUT": + m = re.search(r"broadcast\s*\[([^\]]+)\]", stmt, re.IGNORECASE) + elif inp_name == "ITEM" and opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt, re.IGNORECASE) + elif inp_name == "ITEM" and opcode == "data_insertatlist": + m = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt, re.IGNORECASE) + elif inp_name == "ITEM" and opcode == "data_replaceitemoflist": + m = re.search(r"with\s*\[([^\]]+)\]", stmt, re.IGNORECASE) + + if m: + node["inputs"][inp_name] = {"kind": "value", "value": m.group(1).strip()} + + # Dropdown inputs (e.g., go to [random position v], switch costume to [costume1 v]) + elif spec[0] == 1 and isinstance(spec[1], str) and spec[1].endswith("_menu"): # Menu block reference + m = None + if inp_name == "TO" and opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "TO" and opcode == "motion_glideto": + m = re.search(r"glide\s*\(.+?\)\s*secs to\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "TOWARDS" and opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "KEY_OPTION" and opcode == "sensing_keypressed": + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt, re.IGNORECASE) + elif inp_name == "TOUCHINGOBJECTMENU" and opcode == "sensing_touchingobject": + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt, re.IGNORECASE) + elif inp_name == "CLONE_OPTION" and opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "SOUND_MENU" and opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "COSTUME" and opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "BACKDROP" and opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait", "event_whenbackdropswitchesto"]: + m = re.search(r"(?:switch backdrop to|when backdrop switches to)\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "BROADCAST_INPUT" and opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "BROADCAST_OPTION" and opcode == "event_whenbroadcastreceived": + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + elif inp_name == "INDEX" and opcode in ["data_deleteoflist", "data_insertatlist", "data_replaceitemoflist"]: + m = re.search(r"(?:delete|insert|replace item)\s*(?:\()?\s*(\w+)\s*(?:\))?\s*(?:of|at)", stmt, re.IGNORECASE) + if m: + val_str = m.group(1) + if not val_str.isdigit(): # "all", "last", "random" + node["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + m = None # Prevent re-matching as numeric if it's a dropdown option + + + if m: + option = m.group(1).strip() + # Special mapping for certain menu options + if inp_name == "TO" and option == "random position": option = "_random_" + if inp_name == "TO" and option == "mouse-pointer": option = "_mouse_" + if inp_name == "TOWARDS" and option == "mouse-pointer": option = "_mouse_" + if inp_name == "CLONE_OPTION" and option == "myself": option = "_myself_" + if inp_name == "TOUCHINGOBJECTMENU" and option == "mouse-pointer": option = "_mouse_" + if inp_name == "TOUCHINGOBJECTMENU" and option == "edge": option = "_edge_" + node["inputs"][inp_name] = {"kind": "menu", "option": option} + elif isinstance(info["inputs"], list) and opcode == "procedures_definition": + # For procedures_definition, the inputs list defines the prototype, not values from pseudo-code. + # No parsing needed from `stmt` for these inputs, as they are part of the definition. + pass + + # Parse fields (dropdowns, variable names) + node["fields"] = {} + if info.get("fields"): + for field_name, field_spec in info["fields"].items(): + if field_name == "VARIABLE": + m = re.search(r"\[([^\]]+)\s*v\]", stmt) + if m: node["fields"]["VARIABLE"] = [m.group(1), None] + elif field_name == "LIST": + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt) + if m: node["fields"]["LIST"] = [m.group(1), None] + elif field_name == "STOP_OPTION": + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt) + if m: node["fields"]["STOP_OPTION"] = [m.group(1), None] + elif field_name == "STYLE": + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt) + if m: node["fields"]["STYLE"] = [m.group(1), None] + elif field_name == "DRAG_MODE": + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt) + if m: node["fields"]["DRAG_MODE"] = [m.group(1), None] + elif field_name == "EFFECT" and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt, re.IGNORECASE) + if m: node["fields"]["EFFECT"] = [m.group(1).upper(), None] + elif field_name == "NUMBER_NAME" and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + if m: node["fields"]["NUMBER_NAME"] = [m.group(1), None] + elif field_name == "FRONT_BACK" and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt, re.IGNORECASE) + if m: node["fields"]["FRONT_BACK"] = [m.group(1), None] + elif field_name == "FORWARD_BACKWARD" and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + if m: node["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + elif field_name == "OPERATOR" and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt, re.IGNORECASE) + if m: node["fields"]["OPERATOR"] = [m.group(1).upper(), None] + elif field_name == "CURRENTMENU" and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + if m: node["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + elif field_name == "PROPERTY" and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + node["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + elif field_name == "WHENGREATERTHANMENU" and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt, re.IGNORECASE) + if m: node["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + elif field_name == "KEY_OPTION" and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt, re.IGNORECASE) + if m: node["fields"]["KEY_OPTION"] = [m.group(1), None] + elif field_name == "BACKDROP" and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + if m: node["fields"]["BACKDROP"] = [m.group(1), None] + elif field_name == "BROADCAST_OPTION" and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt, re.IGNORECASE) + if m: node["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + + # condition & body for c_blocks + if ntype == "c_block": + # Extract the condition string from the statement + # For 'if then' or 'if then else' + cond_match = re.search(r"if\s*<(.+?)>\s*(?:then|then else)", raw.strip(), re.IGNORECASE) + if cond_match: + node["condition"] = parse_condition(cond_match.group(1).strip(), pick_key) + # For 'repeat until ' + elif opcode == "control_repeat_until": + cond_match = re.search(r"repeat until\s*<(.+?)>", raw.strip(), re.IGNORECASE) + if cond_match: + node["condition"] = parse_condition(cond_match.group(1).strip(), pick_key) + + node["body"] = [] + if opcode == "control_if_else": + node["body2"] = [] # For the 'else' part + + # next-list for hats + if ntype == "hat": + node["next"] = [] + + # attach & possibly push new container + container.append(node) + if ntype == "hat": + stack.append((indent, node["next"])) + elif ntype == "c_block": + stack.append((indent, node["body"])) + if opcode == "control_if_else": + # For if-else, the 'else' part is at the same indent level as 'if' + # but needs a separate container. This is a simplification. + # A more robust parser would need to detect the 'else' keyword. + # For now, assuming direct nesting. + pass # The 'else' part will be handled when 'else' line is processed. + + + # Post-processing to correctly assign body2 for if-else + # This is a bit of a hack given the line-by-line parsing. + # A proper AST builder would handle this more naturally. + # For this simplified model, we assume 'else' directly follows 'then' block. + # The 'final_flow' variable was removed as it was not being used. + + return {"flow": flow} + +# Example input with opcodes for the initial generation +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_glidesecstoxy","count":2}, # Increased count + {"opcode":"motion_xposition","count":3}, # Used multiple times in conditions + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":2}, # Two if blocks + {"opcode":"control_stop","count":2}, # stop all v, stop this script v + {"opcode":"operator_lt","count":1}, # Used in condition + {"opcode":"sensing_touchingobject","count":2}, # Used in condition, and for if on edge, bounce + {"opcode":"sensing_touchingobjectmenu","count":2}, # Menu for touchingobject + {"opcode":"event_broadcast","count":2}, # broadcast [Game Over v], broadcast [jump v] + {"opcode":"data_setvariableto","count":3}, # set [score v] to (1), set [speed v] to (1), set [other sprite X v] to ( (x position) of [Sprite2 v] ) + {"opcode":"data_showvariable","count":2}, # show variable [score v], show variable [speed v] + {"opcode":"operator_add","count":2}, # For set [var] to ((var) + (val)), (number 1) + (number 2) + {"opcode":"data_variable","count":10}, # Increased count for general variables + {"opcode":"looks_sayforsecs","count":2}, # For "say [Hello!] for (2) seconds", say [You win!] for (2) seconds + {"opcode":"looks_say","count":2}, # For "say [Hello! v]", say [Welcome to my game! v] + {"opcode":"motion_movesteps","count":3}, # For "move (10) steps" + {"opcode":"control_wait","count":3}, # For "wait (0.1) seconds", wait (0.5) seconds, wait (1) seconds + {"opcode":"motion_changeyby","count":2}, # For "change y by (10)" + {"opcode":"motion_pointindirection","count":1}, # For "point in direction (90)" + {"opcode":"event_whenkeypressed","count":3}, # For "when [space v] key pressed", when [up arrow v] key pressed, when [right arrow v] key pressed, when [left arrow v] key pressed + {"opcode":"control_repeat","count":2}, # For "repeat (10)" + {"opcode":"event_whenthisspriteclicked","count":2}, # For "when this sprite clicked" + {"opcode":"looks_costumenumbername","count":1}, # For "(costume [name v])" + {"opcode":"operator_join","count":3}, # For "join [Hello ] (answer)", join (length of [shopping list v]) [ items in the list.], join [Hello, ] (username) + {"opcode":"sensing_answer","count":1}, # For "(answer)" + {"opcode":"looks_hide","count":2}, # For "hide" + {"opcode":"control_create_clone_of","count":2}, # For "create clone of [myself v]" + {"opcode":"control_start_as_clone","count":2}, # For "when I start as a clone" + {"opcode":"operator_random","count":1}, # For "pick random -240 to 240" + {"opcode":"motion_ifonedgebounce","count":2}, # For "if on edge, bounce" + {"opcode":"operator_gt","count":2}, # For "if <(score) > (10)> then", if <(item # of [Dog] in [myList v])> (0)> then + {"opcode":"control_if_else","count":1}, # For "if <(score) > (10)> then else" + {"opcode":"sound_play","count":2}, # Changed from sound_start to sound_play + {"opcode":"sensing_loudness","count":2}, # For "(loudness)" + {"opcode":"event_whengreaterthan","count":1}, # For "when [loudness v] > (70)" + {"opcode":"control_repeat_until","count":1}, # For "repeat until " + {"opcode":"looks_cleargraphiceffects","count":1}, # For "clear graphic effects" + {"opcode":"looks_changeeffectby","count":2}, # For "change [color v] effect by (50)", change [fisheye v] effect by (5) + {"opcode":"looks_seteffectto","count":1}, # For "set [ghost v] effect to (75)" + {"opcode":"looks_setsizeto","count":2}, # Increased count + {"opcode":"looks_changesizeby","count":1}, # For "change size by (5)" + {"opcode":"looks_nextcostume","count":2}, # For "next costume" + {"opcode":"looks_switchbackdroptowait","count":1}, # For "switch backdrop to [game over v] and wait" + {"opcode":"looks_nextbackdrop","count":1}, # For "next backdrop" + {"opcode":"sound_playuntildone","count":2}, # For "play sound [fanfare v] until done" + {"opcode":"sound_stopallsounds","count":2}, # For "stop all sounds" + {"opcode":"sound_changevolumeby","count":1}, # For "change volume by (-5)" + {"opcode":"sound_setvolumeto","count":1}, # For "set volume to (50) %" + {"opcode":"sensing_resettimer","count":2}, # For "reset timer" + {"opcode":"sensing_setdragmode","count":2}, # For "set drag mode [not draggable v]" + {"opcode":"data_addtolist","count":2}, # Increased count + {"opcode":"data_deleteoflist","count":1}, # For "delete (all) of [my list v]" + {"opcode":"data_insertatlist","count":1}, # For "insert [orange] at (2) of [fruits v]" + {"opcode":"data_replaceitemoflist","count":1}, # For "replace item (1) of [colors v] with [blue]" + {"opcode":"data_listcontainsitem","count":1}, # For "<[inventory v] contains [key]?>" + {"opcode":"data_itemoflist","count":1}, # For "(item (2) of [myList v])" + {"opcode":"data_lengthoflist","count":2}, # For "(length of [shopping list v])" + {"opcode":"data_itemnumoflist","count":1}, # For "(item # of [Dog] in [myList v])" + {"opcode":"sensing_touchingcolor","count":1}, # For "" + {"opcode":"sensing_coloristouchingcolor","count":1}, # For "" + {"opcode":"operator_and","count":1}, # For "< and >" + {"opcode":"operator_or","count":1}, # For "< or >" + {"opcode":"operator_not","count":1}, # For ">" + {"opcode":"operator_contains","count":1}, # For "<[answer] contains [yes]?>" + {"opcode":"procedures_call","count":1}, # For "jump (50)" + {"opcode":"procedures_definition","count":1}, # For "define jump (height)" + {"opcode":"sensing_of","count":1}, # For "(x position) of [Sprite2 v]" + {"opcode":"sensing_current","count":1}, # For "(current [hour v])" + {"opcode":"sensing_mousex","count":1}, # For "(mouse x)" + {"opcode":"sensing_mousey","count":1}, # For "(mouse y)" + {"opcode":"operator_subtract","count":1}, # For "((10) - (4))" + {"opcode":"operator_multiply","count":1}, # For "(6) * (7)" + {"opcode":"operator_divide","count":1}, # For "((20) / (5))" + {"opcode":"data_list","count":1}, # For "[my list v]" + {"opcode":"looks_gotofrontback","count":1}, # For "go to [front v] layer" + {"opcode":"looks_goforwardbackwardlayers","count":1}, # For "go [forward v] (1) layers" + {"opcode":"sound_sounds_menu","count":2}, # For sound menus + {"opcode":"motion_goto_menu","count":1}, # For motion_goto menu + {"opcode":"motion_glideto_menu","count":1}, # For motion_glideto menu + {"opcode":"motion_pointtowards_menu","count":1}, # For motion_pointtowards menu + {"opcode":"sensing_keyoptions","count":1}, # For sensing_keypressed menu + {"opcode":"sensing_of_object_menu","count":1}, # For sensing_of menu + {"opcode":"control_create_clone_of_menu","count":1}, # For control_create_clone_of menu + {"opcode":"looks_costume","count":1}, # For looks_switchcostumeto menu + {"opcode":"looks_backdrops","count":1}, # For looks_switchbackdropto menu + {"opcode":"sensing_distanceto","count":1} # Added sensing_distanceto +] +# +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + +# Example pseudo-code inputs from the JSON files +pseudo_code_examples = [ + """ +when green flag clicked + go to x: (0) y: (0) + point in direction (90) + move (50) steps +end + """, + """ +when [right arrow v] key pressed + turn right (15) degrees +end + """, + """ +when this sprite clicked + go to [mouse-pointer v] + """, + """ +when green flag clicked + glide (2) secs to x: (150) y: (-100) + glide (2) secs to x: (-150) y: (100) +end + """, + """ +when green flag clicked + forever + point towards [mouse-pointer v] + move (5) steps + end +end + """, + """ +when [right arrow v] key pressed + change x by (10) +end + """, + """ +when green flag clicked + set x to (0) + set y to (0) +end + """, + """ +when [up arrow v] key pressed + change y by (10) +end + """, + """ +when green flag clicked + forever + move (10) steps + if on edge, bounce + end +end + """, + """ +when green flag clicked + set rotation style [left-right v] + forever + move (10) steps + if on edge, bounce + end +end + """, + # From looks_block.json + """ +when green flag clicked + say [Grr] for (3) seconds + say [Have you seen my honey? v] for (3) seconds +end + """, + """ +when green flag clicked + say [Welcome to my game! v] + wait (2) seconds + say [] +end + """, + """ +when this sprite clicked + think [What should I do? v] for (2) seconds +end + """, + """ +when I receive [correct answer v] + think [That's right! v] + wait (1) seconds + think [good v] +end + """, + """ +when I receive [explosion v] + repeat (5) + next costume + end + hide +end + """, + """ +when green flag clicked + forever + next costume + wait (0.2) seconds + end +end + """, + """ +when green flag clicked + switch backdrop to [start screen v] +end + """, + """ +broadcast [game over v] + switch backdrop to [game over v] and wait + stop [all v] +end + """, + """ +when [space v] key pressed + next backdrop +end + """, + """ +when green flag clicked + repeat (10) + change size by (5) + wait (0.1) seconds + end +end + """, + """ +when green flag clicked + set size to (50) % + wait (1) seconds + set size to (100) % +end + """, + """ +when green flag clicked + forever + change [color v] effect by (5) + wait (0.1) seconds + end +end + """, + """ +when green flag clicked + set [ghost v] effect to (75) +end + """, + """ +when green flag clicked + change [color v] effect by (50) + wait (2) seconds + clear graphic effects +end + """, + """ +when green flag clicked + hide +when I receive [start game v] + show +end + """, + """ +when green flag clicked + hide +end + """, + """ +when green flag clicked + go to [front v] layer +end + """, + """ +when this sprite clicked + go [forward v] (1) layers +end + """, + """ +say join [I am costume ] (costume [name v]) + """, + """ +say join [Current backdrop: ] (backdrop [name v]) for (2) seconds + """, + """ +set size to ( (size) + (10) ) + """, + # From sound_block.json + """ +when backdrop switches to [winning screen v] + play sound [fanfare v] until done + say [You won!] for (2) seconds +end + """, + """ +forever + play sound [Music v] until done +end + """, + """ +when this sprite clicked + start sound [Pop v] + change [score v] by (1) +end + """, + """ +when I receive [game over v] + stop all sounds +end + """, + """ +when [down arrow v] key pressed + change volume by (-5) +end + """, + """ +when green flag clicked + set volume to (50) % +end + """, + """ +say join [Current volume: ] (volume) + """, + # From event_block.json + """ +when green flag clicked + go to x: (0) y: (0) + say [Hello!] for (2) seconds +end + """, + """ +when [space v] key pressed + repeat (10) + change y by (10) + wait (0.1) seconds + change y by (-10) + end +end + """, + """ +when [right arrow v] key pressed + point in direction (90) + move (10) steps +end + """, + """ +when this sprite clicked + say [Ouch!] for (1) seconds + change [score v] by (-1) +end + """, + """ +when backdrop switches to [game over v] + stop [all v] +end + """, + """ +when [loudness v] > (70) + start sound [scream v] +end + """, + """ +when I receive [start game v] + show + go to x: (0) y: (0) +end + """, + """ +when I receive [game over v] + set score to 0 + stop [all v] +end + """, + """ +if then + broadcast [jump v] +end + """, + """ +broadcast [initialize sprites v] and wait + say [Game Started!] for (2) seconds + """, + # From control_block.json + """ +say [Hello!] for (1) seconds + wait (0.5) seconds + say [Goodbye!] for (1) seconds + """, + """ +when green flag clicked + repeat (10) + move (10) steps + wait (0.1) seconds + end +end + """, + """ +when green flag clicked + forever + move (5) steps + if on edge, bounce + end +end + """, + """ +forever + if then + stop [this script v] + end +end + """, + """ +if <(score) > (10)> then + say [You win!] for (2) seconds +else + say [Keep trying!] for (2) seconds +end + """, + """ +repeat until + move (5) steps +end + """, + """ +if <(health) = (0)> then + stop [all v] +end + """, + """ +when I start as a clone + wait until + delete this clone +end + """, + """ +when I start as a clone + go to x: (pick random -240 to 240) y: (pick random -180 to 180) + show + forever + move (10) steps + if on edge, bounce + end +end + """, + """ +when I start as a clone + wait (5) seconds + delete this clone +end + """, + """ +when green flag clicked + hide + forever + create clone of [myself v] + wait (1) seconds + end + """, + # From data_block.json + """ +when green flag clicked + set [score v] to (0) + set [player name v] to [Guest] +end + """, + """ +when this sprite clicked + change [score v] by (1) +end + """, + """ +when green flag clicked + add [apple] to [shopping list v] + add [banana] to [shopping list v] +end + """, + """ +when green flag clicked + delete (all) of [my list v] +end + """, + """ +insert [orange] at (2) of [fruits v] + """, + """ +replace item (1) of [colors v] with [blue] + """, + """ +when green flag clicked + show variable [score v] +end + """, + """ +when I receive [game over v] + hide variable [score v] +end + """, + """ +when green flag clicked + show list [shopping list v] +end + """, + """ +when I receive [game over v] + hide list [shopping list v] +end + """, + """ +say ([score v]) for (2) seconds + """, + """ +say ([my list v]) + """, + """ +say (item (2) of [myList v]) for 2 seconds + """, + """ +say join (length of [shopping list v]) [ items in the list.] + """, + """ +if <(item # of [Dog] in [myList v])> (0)> then + say join [Dog found at position ] (item # of [Dog] in [my list v]) +end + """, + # From reporter_blocks.json (some already covered by other categories) + """ +when green flag clicked + say (x position) for (2) seconds +end + """, + """ +set [worms v] to (y position) + """, + """ +when green flag clicked + say (direction) for (2) seconds +end + """, + """ +say join [I am costume ] (costume [name v]) + """, + """ +set size to ( (size) + (10) ) + """, + """ +say join [Current backdrop: ] (backdrop [name v]) for (2) seconds + """, + """ +say join [Current volume: ] (volume) + """, + """ +if <(distance to [Sprite2 v]) < (50)> then + say [Too close!] +end + """, + """ +ask [What is your name?] and wait + say join [Hello ] (answer) + """, + """ +go to x: (mouse x) y: (mouse y) + """, + """ +if <(mouse y) < (0)> then + say [Below center] +end + """, + """ +when green flag clicked + forever + if <(loudness) > (30)> then + start sound [pop v] +end + """, + """ +when green flag clicked + reset timer + say join [Time elapsed: ] (timer) +end + """, + """ +set [other sprite X v] to ( (x position) of [Sprite2 v] ) + """, + """ +say join [The current hour is ] (current [hour v]) + """, + """ +say join [Days passed: ] (days since 2000) + """, + """ +say join [Hello, ] (username) + """, + """ +set [total v] to ( (number 1) + (number 2) ) + """, + """ +set [difference v] to ( (number 1) - (number 2) ) + """, + """ +set [area v] to ( (length) * (width) ) + """, + """ +set [average v] to ( (total score) / (number of students) ) + """, + """ +go to x: (pick random -240 to 240) y: (pick random -180 to 180) + """, + """ +say (join [Hello ][World!]) + """, + """ +say (letter (1) of [apple]) + """, + """ +say (length of [banana]) + """, + """ +if <([number v] mod (2) = (0))> then + say [Even number] +end + """, + """ +set [rounded score v] to (round (score)) + """, + """ +set [distance v] to ([sqrt v] of ( ( (x position) * (x position) ) + ( (y position) * (y position) ) )) + """, + # From boolean_blocks.json (conditions already covered by parse_condition) + """ +if <(score) < (10)> then + say [Keep trying!] +end + """, + """ +if <(answer) = (5)> then + say [Correct!] +end + """, + """ +if <([health v]) > (0)> then + move (10) steps +else + stop [all v] +end + """, + """ +if < and > then + say [You're clicking me!] +end + """, + """ +if < or > then + change x by (-10) +end + """, + """ +if > then + say [I'm safe!] +end + """, + """ +if <[answer] contains [yes]?> then + say [Great!] +end + """, + """ +if then + broadcast [Game Over v] +end + """, + """ +if then + bounce off edge +end + """, + """ +if then + change [health v] by (-1) +end + """, + """ +if then + say [Collision!] +end + """, + """ +forever + if then + broadcast [shoot v] + end +end + """, + """ +if then + go to mouse-pointer +end + """, + """ +if <[inventory v] contains [key]?> then + say [You have the key!] +end + """, + # Custom block example + """ +define jump (height) + change y by (height) + wait (0.5) seconds + change y by (0 - (height)) +end + +when green flag clicked + jump (50) +end + """, + # User's custom procedure example + """ +// Define custom procedure "moveBall" +procedure moveBall + change x by vx + change y by vy + + if (x position > 240) or (x position < -240) then + vx = -vx + + if (y position > 180) or (y position < -180) then + vy = -vy +end procedure + +// Main program +when green flag clicked +set x position to 0 +set y position to 0 +set vx to 5 +set vy to 4 + +forever + call moveBall + wait 0.02 seconds +end forever + """ +] +# +# Process each example and print the plan +txt="" +trace="" +# Process each example and print the plan +for i, pseudo_code_input in enumerate(pseudo_code_examples): + print(f"\n--- Processing Example {i+1} ---") + print(pseudo_code_input.strip()) + try: + # Regenerate blocks and opcode_occurrences for each run to ensure fresh keys + # This is important because pick_key uses a defaultdict that persists state. + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) + print(json.dumps(plan, indent=2)) + txt += str(plan) + " \n" + except Exception as e: + print(f"Error processing example: {e}") + import traceback + #traceback.print_exc() + trace += str(e) + " \n" + +with open("all_analysis.txt", "w", encoding="utf-8") as f: + f.write(txt) + +with open("all_analysis_trace.txt", "w", encoding="utf-8") as f: + f.write(trace) \ No newline at end of file diff --git a/utils/plan_generator_4.py b/utils/plan_generator_4.py new file mode 100644 index 0000000000000000000000000000000000000000..8872189deedbcf405aac6336ade43138a69f0d48 --- /dev/null +++ b/utils/plan_generator_4.py @@ -0,0 +1,2796 @@ +import json +import copy +import re +from collections import defaultdict + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False + + generated_blocks[main_key] = main_block_data + + # Handle menus + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_key + + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 1 and \ + main_block_data["inputs"][input_name][0] == 1: + + main_block_data["inputs"][input_name][1] = menu_key + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +# Consolidated block definitions from all JSON files +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_distanceto": { # Added sensing_distanceto + "block_name": "(distance to ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto", + "functionality": "Reports the distance from the sprite to the mouse-pointer or another specified sprite.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": False, "topLevel": True + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": [ + { + "name": "PROCCONTAINER", + "type": "block_prototype" + } + ], + "fields": {}, + "shadow": False, + "topLevel": True + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": [], # Inputs are dynamic based on definition + "fields": {}, + "shadow": False, + "topLevel": True + } +} + +def unparen(s): + s = s.strip() + # keep peeling off *all* matching outer parens + while True: + m = re.fullmatch(r"\((.*)\)", s) + if not m: + break + s = m.group(1).strip() + return s + +def _register_block(opcode, parent_key, is_shadow, pick_key_func, all_generated_blocks, inputs=None, fields=None): + """ + Helper to create and register a block in all_generated_blocks. + Returns the key of the newly created block. + """ + key = pick_key_func(opcode) # Use the passed pick_key_func + block_data = copy.deepcopy(all_block_definitions[opcode]) + block_data["id"] = key + block_data["parent"] = parent_key + block_data["next"] = None # Nested blocks as inputs don't have a 'next' + block_data["topLevel"] = False + block_data["shadow"] = is_shadow + + # Initialize inputs and fields if not provided to prevent KeyError later + if "inputs" not in block_data: + block_data["inputs"] = {} + if "fields" not in block_data: + block_data["fields"] = {} + + if inputs: + block_data["inputs"].update(inputs) # Use update to merge, not overwrite + if fields: + block_data["fields"].update(fields) # Use update to merge, not overwrite + + all_generated_blocks[key] = block_data + return key + +def _auto_balance(text): + # if there are more "(" than ")", append the missing ")" + diff = text.count("(") - text.count(")") + if diff > 0: + text = text + ")"*diff + # same for square brackets + diff = text.count("[") - text.count("]") + if diff > 0: + text = text + "]"*diff + return text + +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, parent_key, pick_key_func, all_generated_blocks): + text = _auto_balance(text.strip()) + text = unparen(text.strip()) + # Check for numeric literal (including parenthesized numbers like "(0)" or "(10)") + m_num = re.fullmatch(r"\(?\s*(-?\d+(\.\d+)?)\s*\)?", text) + if m_num: + val_str = m_num.group(1) + return {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # # Check for string literal (e.g., "[Hello!]") + # if text.startswith('[') and text.endswith(']'): + # return {"kind": "value", "value": text[1:-1]} + # Variable reporter: [score v], [health v], etc. + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, + fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + + # Now catch other bracketed values as literal strings + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + # Check for simple reporters, potentially with outer parentheses + m_simple_reporter = re.fullmatch(r"\((.+?)\)", text) + if m_simple_reporter: + inner_text = m_simple_reporter.group(1).strip() + if inner_text in simple_reporters: + block_id = _register_block(simple_reporters[inner_text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + # Also check for simple reporters without parentheses (e.g., if passed directly) + if text in simple_reporters: + block_id = _register_block(simple_reporters[text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + + # Variable reporter: [score v] or (score) or just "score" + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + # Ensure it's not a simple reporter already handled, or a number + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [potential_var_name, None]}) + return {"kind": "block", "block": block_id} + # Handle plain variable names like "score", "number 1", "total score" + if re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_ ]*", text): # Allow spaces for "number 1" etc. + # Exclude known simple reporters that don't have 'v' or parentheses + if text not in simple_reporters: + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [text, None]}) + return {"kind": "block", "block": block_id} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + return {"kind": "block", "block": block_id} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + max_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + inputs = {"FROM": min_val_obj, "TO": max_val_obj} + block_id = _register_block("operator_random", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if min_val_obj.get("kind") == "block": all_generated_blocks[min_val_obj["block"]]["parent"] = block_id + if max_val_obj.get("kind") == "block": all_generated_blocks[max_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (join ()()) (operator_join) - handle both [] and () for inputs + + # m = re.search(r"join \((.+?)\) \((.+?)\)", text) # Try (val) (val) + # if not m: + # m = re.search(r"join \[(.+?)\] \[(.+?)\]", text) # Try [val] [val] + # if m: + # str1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + # str2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + # inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + # block_id = _register_block("operator_join", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # if str1_obj.get("kind") == "block": all_generated_blocks[str1_obj["block"]]["parent"] = block_id + # if str2_obj.get("kind") == "block": all_generated_blocks[str2_obj["block"]]["parent"] = block_id + # return {"kind": "block", "block": block_id} + + #m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s+(\[.+?\]|\(.+?\))", text) # Try (val) (val) + m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s*(\[.+?\]|\(.+?\))", text) + if m: + part1_txt = m.group(1).strip() + part2_txt = m.group(2).strip() + str1_obj = parse_reporter_or_value(part1_txt, None, pick_key_func, all_generated_blocks) + str2_obj = parse_reporter_or_value(part2_txt, None, pick_key_func, all_generated_blocks) + inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + block_id = _register_block("operator_join", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs) + # set parents if nested blocks + for obj in (str1_obj, str2_obj): + if obj.get("kind") == "block": + all_generated_blocks[obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # letter () of () (operator_letterof) - handle both [] and () for inputs + m = re.search(r"letter \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + string_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"LETTER": index_obj, "STRING": string_val_obj} + block_id = _register_block("operator_letterof", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + if string_val_obj.get("kind") == "block": all_generated_blocks[string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (length of ()) (operator_length) - handle both [] and () for inputs + #m = re.search(r"length of \((.+?)\)", text) + m = re.search(r"length of\s*(?:\((.+?)\)|\[(.+?)\])", text) + if not m: + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + arg_txt = (m.group(1) or m.group(2)).strip() + list_or_string_val_obj = parse_reporter_or_value(arg_txt, None, pick_key_func, all_generated_blocks) + #list_or_string_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"STRING": list_or_string_val_obj} + block_id = _register_block("operator_length", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if list_or_string_val_obj.get("kind") == "block": all_generated_blocks[list_or_string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (() mod ()) (operator_mod) + # m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + m = re.search(r"\[([^\]]+)\s*v\]\s*mod\s*\(?\s*(.+?)\s*\)?", text) + if m: + num1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + num2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM1": num1_obj, "NUM2": num2_obj} + block_id = _register_block("operator_mod", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num1_obj.get("kind") == "block": all_generated_blocks[num1_obj["block"]]["parent"] = block_id + if num2_obj.get("kind") == "block": all_generated_blocks[num2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": num_obj} + block_id = _register_block("operator_round", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num_obj.get("kind") == "block": all_generated_blocks[num_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (() of ()) (operator_mathop) - handle variable for function type + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) # e.g. [sqrt v] of ((x pos) * (x pos)) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + # Also handle direct string for function type (e.g., "abs of (x)") + m = re.search(r"([a-zA-Z]+)\s*of\s*\((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + # This regex is designed to handle nested parentheses correctly. + # It looks for an opening parenthesis, then non-parenthesis characters or balanced parentheses, + # followed by an operator, and then the second operand. + # This is a simplified approach; a full-fledged parser would use a stack. + # arithmetic_match = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + arithmetic_match = re.search(r"\(?\s*(.+?)\s*\)?\s*([\+\-\*/])\s*\(?\s*(.+?)\s*\)?", text) + if not arithmetic_match: + # Try to match without outer parentheses for the operands, but still with an operator + arithmetic_match = re.search(r"(.+?)\s*([+\-*/])\s*(.+)", text) + + if arithmetic_match: + op1_str = arithmetic_match.group(1).strip() + operator_symbol = arithmetic_match.group(2).strip() + op2_str = arithmetic_match.group(3).strip() + + op1_obj = parse_reporter_or_value(op1_str, None, pick_key_func, all_generated_blocks) + op2_obj = parse_reporter_or_value(op2_str, None, pick_key_func, all_generated_blocks) + + opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + inputs = {"NUM1": op1_obj, "NUM2": op2_obj} + block_id = _register_block(opcode_map[operator_symbol], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if op1_obj.get("kind") == "block": all_generated_blocks[op1_obj["block"]]["parent"] = block_id + if op2_obj.get("kind") == "block": all_generated_blocks[op2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (costume ()) (looks_costumenumbername) - handle with or without 'v' + m = re.search(r"costume \((.+?)\)", text) + if not m: + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_costumenumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (backdrop ()) (looks_backdropnumbername) - handle with or without 'v' + m = re.search(r"backdrop \((.+?)\)", text) + if not m: + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_backdropnumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (distance to ()) (sensing_distanceto) - handle with or without 'v' + m = re.search(r"distance to \((.+?)\)", text) + if not m: + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + inputs = {"TARGET": [target_val, None]} # This is a direct value, not a block + block_id = _register_block("sensing_distanceto", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # (current ()) (sensing_current) - handle with or without 'v' + m = re.search(r"current \((.+?)\)", text) + if not m: + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + fields = {"CURRENTMENU": [unit.upper(), None]} + block_id = _register_block("sensing_current", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (() of ()) (sensing_of) - handle both variable and non-variable properties, and objects + # Updated regex to correctly capture the property and object, including nested reporters for property + m = re.search(r"\((.+?)\)\s*of\s*\((.+?)\)", text) # (prop) of (obj) + if not m: + m = re.search(r"\((.+?)\)\s*of\s*\[([^\]]+)\s*v\]", text) # (prop) of [obj v] + if m: + prop_str = m.group(1).strip() + obj = m.group(2).strip() + + # Map common property names to their internal Scratch representation + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + property_value = prop_map.get(prop_str, prop_str) # Use mapped value or original string + + # The object can be a sprite name or "_stage_" + obj_kind = "menu" + if obj.lower() == "stage": obj_val = "_stage_" + elif obj.lower() == "myself": obj_val = "_myself_" + else: obj_val = obj # Assume it's a sprite name + + # Create the menu block for OBJECT input + object_menu_id = _register_block("sensing_of_object_menu", parent_key, True, pick_key_func, all_generated_blocks, fields={"OBJECT": [obj_val, None]}) + + # inputs = {"OBJECT": [1, object_menu_id]} # Link to the menu block + # block_id = _register_block("sensing_of", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + + # # Update parent for the menu block + # all_generated_blocks[object_menu_id]["parent"] = block_id + + # return {"kind": "block", "block": block_id} + of_fields = {"OBJECT": [obj_val, None]} + inputs = {"OBJECT": [1, object_menu_id]} + block_id = _register_block("sensing_of", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs, + fields=of_fields) + all_generated_blocks[object_menu_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item (index) of [list v]) (data_itemoflist) - handle with or without 'v' and parentheses for index + m = re.search(r"item \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + inputs = {"INDEX": index_obj} + fields = {"LIST": [list_name, None]} + block_id = _register_block("data_itemoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item # of [item] in [list v]) (data_itemnumoflist) - handle with or without 'v' and parentheses for item + m = re.search(r"item # of \((.+?)\) in \((.+?)\)", text) + if not m: + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + inputs = {"ITEM": item_obj} + fields = {"LIST": [list_name, None]} + block_id = _register_block("data_itemnumoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if item_obj.get("kind") == "block": all_generated_blocks[item_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # inside parse_reporter_or_value + m = re.search(r"\(\s*(.+?)\s*\)\s*([\+\-\*/])\s*\(\s*(.+?)\s*\)", text) + if m: + left = parse_reporter_or_value(f"({m.group(1).strip()})", parent_key, pick_key_func, all_generated_blocks) + right = parse_reporter_or_value(f"({m.group(3).strip()})", parent_key, pick_key_func, all_generated_blocks) + op_map = {"+": "operator_add", "-": "operator_subtract", "*": "operator_multiply", "/": "operator_divide"} + opcode = op_map[m.group(2).strip()] + inputs = {"NUM1": left, "NUM2": right} + block_id = _register_block(opcode, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + for side in (left, right): + if side.get("kind") == "block": + all_generated_blocks[side["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + raise ValueError(f"Can't parse reporter or value: {text}") + +def parse_condition(stmt, parent_key, pick_key_func, all_generated_blocks): + """ + Parse Scratch-style boolean conditions, handling comparisons (<, =, >), + boolean operators (and, or, not), and other sensing conditions. + """ + s = stmt.lower().strip() + while s.startswith("<") and s.endswith(">"): + s = s[1:-1].strip() + s = s.lower() + + # 1a) Comparisons with explicit angle wrappers: < (...) op (...) > + m = re.fullmatch( + r"\s*<\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*>\s*", + s, + re.VERBOSE + ) + if m: + left_txt, right_txt = m.group(1), m.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), None, pick_key_func, all_generated_blocks) + operand2_obj = parse_reporter_or_value(unparen(right_txt), None, pick_key_func, all_generated_blocks) + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 1b) Simple comparisons without angle wrappers: A op B + m_simple = re.fullmatch(r"\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*", s) + if m_simple: + left_txt, right_txt = m_simple.group(1), m_simple.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), None, pick_key_func, all_generated_blocks) + operand2_obj = parse_reporter_or_value(unparen(right_txt), None, pick_key_func, all_generated_blocks) + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m_simple.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # 2) Boolean AND / OR + m = re.fullmatch(r"\s*<\s*(.+?)\s+(and|or)\s+(.+?)\s*>\s*", s) + if m: + cond1_obj = parse_condition(m.group(1), None, pick_key_func, all_generated_blocks) + cond2_obj = parse_condition(m.group(3), None, pick_key_func, all_generated_blocks) + op_block = 'operator_and' if m.group(2) == 'and' else 'operator_or' + + inputs = {"OPERAND1": cond1_obj, "OPERAND2": cond2_obj} + block_id = _register_block(op_block, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if cond1_obj.get("kind") == "block": all_generated_blocks[cond1_obj["block"]]["parent"] = block_id + if cond2_obj.get("kind") == "block": all_generated_blocks[cond2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 3) Boolean NOT + m = re.fullmatch(r"\s*<\s*not\s+(.+?)\s*>\s*", s) + if m: + inner_obj = parse_condition(m.group(1), None, pick_key_func, all_generated_blocks) + inputs = {"OPERAND": inner_obj} + block_id = _register_block("operator_not", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if inner_obj.get("kind") == "block": all_generated_blocks[inner_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 4) Contains: <[list v] contains [item]?> + m = re.search(r"\[([^\]]+)\s*v\] contains \[(.+?)\]\?", s) + if m: + list_name = m.group(1).strip() + item_val = {"kind": "value", "value": m.group(2).strip()} # Item can be a value or a block + + # Create the data_list reporter block + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + + inputs = {"LIST": {"kind": "block", "block": list_block_id}, "ITEM": item_val} + block_id = _register_block("data_listcontainsitem", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 5) Touching object: + # m = re.search(r"touching \[([^\]]+)\s*v\]\?", s) + # if m: + # opt = m.group(1).strip() + # opt_val = {'mouse-pointer':'_mouse_','edge':'_edge_'}.get(opt, opt) + + # # Create the menu block for TOUCHINGOBJECTMENU input + # menu_block_id = _register_block("sensing_touchingobjectmenu", parent_key, True, pick_key_func, all_generated_blocks, fields={"TOUCHINGOBJECTMENU": [opt_val, None]}) + + # inputs = {"TOUCHINGOBJECTMENU": [1, menu_block_id]} # Link to the menu block + # block_id = _register_block("sensing_touchingobject", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # all_generated_blocks[menu_block_id]["parent"] = block_id + # return {"kind": "block", "block": block_id} + m = re.search(r"touching\s+\[\s*([^\]]+?)\s*v\s*\]\?", s) + if m: + opt = m.group(1).strip() + opt_val = {'mouse-pointer':'_mouse_', 'edge':'_edge_'}.get(opt, opt) + + # Create the menu block for TOUCHINGOBJECTMENU input + menu_block_id = _register_block("sensing_touchingobjectmenu", parent_key, True, pick_key_func, all_generated_blocks, fields={"TOUCHINGOBJECTMENU": [opt_val, None]}) + + inputs = {"TOUCHINGOBJECTMENU": [1, menu_block_id]} # Link to the menu block + block_id = _register_block("sensing_touchingobject", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + all_generated_blocks[menu_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 6) Touching color: + m = re.search(r"touching color \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR": [1, [9, m.group(1)]]} # Color input is special, often a list [type, value] + block_id = _register_block("sensing_touchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # 7) Color is touching color: + m = re.search(r"color \[(#[0-9A-Fa-f]{6})\] is touching \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR1": [1, [9, m.group(1)]], "COLOR2": [1, [9, m.group(2)]]} + block_id = _register_block("sensing_coloristouchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # 8) Key pressed: + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", s) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", parent_key, True, pick_key_func, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) + + inputs = {"KEY_OPTION": [1, menu_block_id]} + block_id = _register_block("sensing_keypressed", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + all_generated_blocks[menu_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 9) Mouse down?: mouse down? + if s == "mouse down?": + block_id = _register_block("sensing_mousedown", parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + val_obj = parse_reporter_or_value(unparen(stmt), parent_key, pick_key_func, all_generated_blocks) + if val_obj: + return val_obj + + raise ValueError(f"Can't parse condition: {stmt}") + +def classify(line): + """ + Classifies a pseudo-code line into its corresponding Scratch opcode and block type. + Order of checks matters: more specific patterns should come before more general ones. + """ + l = line.lower().strip() + + # Ignore comments + if l.startswith("//"): return None, None + + # Hat Blocks (most specific first) + if re.match(r"when green flag click(ed)?", l): return "event_whenflagclicked", "hat" + if re.match(r"when (.+?) key press(ed)?", l): return "event_whenkeypressed", "hat" + if re.match(r"when this sprite click(ed)?", l): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if re.match(r"when i start as a clo(ne)?", l): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + if l.startswith("procedure "): return "procedures_definition", "hat" # For "procedure moveBall" + + # Motion Blocks + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + # IMPORTANT: More specific glide block before less specific one + if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if re.match(r"set x to\s*\(.+\)", l): return "motion_setx", "stack" # Specific for set x + if l.startswith("change y by"): return "motion_changeyby", "stack" + if re.match(r"set y to\s*\(.+\)", l): return "motion_sety", "stack" # Specific for set y + #if re.match(r"if on edge, bounce( off edge)?", l): return "motion_ifonedgebounce", "stack" + if re.match(r"if on edge,\s*bounc(e)?(\s+off\s+edge)?", l.strip(), re.IGNORECASE): return "motion_ifonedgebounce", "stack" + if re.match(r"bounce off edg(e)?", l): return "motion_ifonedgebounce", "stack" # Alias + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + + + # Looks Blocks + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if re.match(r"next costum(e)?", l): return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + # Updated regex for change/set effect by/to + if re.match(r"change\s*(\[.+?v\]|\(.+?\))?\s*effect by", l): return "looks_changeeffectby", "stack" + if re.match(r"set\s*(\[.+?v\]|\(.+?\))?\s*effect to", l): return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + # Sound Blocks + if re.match(r"play sound (.+?) until do(ne)?", l): return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + # Event Blocks (broadcasts) + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + # Control Blocks + if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + # Updated regex for stop block to handle different options + if re.match(r"stop \[(all|this script|other scripts in sprite)\s*v\]", l): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + # Data Blocks + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + # Updated regex for delete of list + if re.match(r"delete \((.+?)\) of \[([^\]]+)\s*v\]", l): return "data_deleteoflist", "stack" + if l.startswith("delete all of [" ): return "data_deletealloflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + # Sensing Blocks + if re.match(r"ask (.+?) and wai(t)?", l): return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom Blocks (procedures_call) - specific rule for "call" + if l.startswith("call "): + return "procedures_call", "stack" + + # Custom Blocks (procedures_call) - LAST RESORT (generic match) + # This should be the very last check for stack-type blocks to avoid conflicts. + # It tries to match anything that looks like a function call with or without arguments. + custom_block_match = re.match(r"([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", l) + if custom_block_match: + # Before returning, ensure it's not a known simple reporter or variable name + # that might have been missed or is being used standalone. + # This is a heuristic; a full parser would be more robust. + potential_name = custom_block_match.group(1).strip() + if potential_name not in ["x position", "y position", "direction", "mouse x", "mouse y", "loudness", "timer", "days since 2000", "username", "answer", "size", "volume"] and \ + not re.fullmatch(r"\[[^\]]+\]", potential_name) and \ + not re.fullmatch(r"\[[^\]]+\]\s*v", potential_name): + return "procedures_call", "stack" + + + raise ValueError(f"Unknown statement: {line!r}") + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key -> block_data (pre-generated block definitions) + • opcode_keys: dict of opcode -> list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... list of block dictionaries ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + all_generated_blocks = {} + for key, block_data in generated_input.items(): + all_generated_blocks[key] = copy.deepcopy(block_data) + # Ensure initial blocks have no next/parent/topLevel set from previous runs + all_generated_blocks[key]["next"] = None + all_generated_blocks[key]["parent"] = None + all_generated_blocks[key]["topLevel"] = False # Will be set to True for Hat blocks + + + # Stack stores (indent, parent_block_key, first_block_in_this_chain, last_block_in_this_chain) + # The last element of the stack is always the currently active scope. + # Sentinel: global scope (indent -1, no parent, no blocks yet in this "chain") + stack = [(-1, None, None, None)] + + top_level_script_keys = [] + + lines = pseudo_code.splitlines() + i = 0 + while i < len(lines): + raw_line = lines[i] + stripped_line = raw_line.strip() + + # Skip empty lines and comments + if not stripped_line or stripped_line.startswith("//"): + i += 1 + continue + + current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2 + + # Handle 'else' and 'end' keywords first, as they control scope + if stripped_line.lower() == "else": + # Pop the 'then' substack's scope + # This pop should always be safe as 'else' implies an 'if' scope exists + popped_indent, popped_parent_key, first_then_block, last_then_block = stack.pop() + if last_then_block: + all_generated_blocks[last_then_block]["next"] = None + + # Link the 'then' substack to its 'if-else' parent + if popped_parent_key and all_generated_blocks[popped_parent_key]["op_code"] == "control_if_else": + all_generated_blocks[popped_parent_key]["inputs"]["SUBSTACK"] = [2, first_then_block] if first_then_block else [2, None] + # Push a new scope for the 'else' substack, with the same parent + stack.append((current_indent, popped_parent_key, None, None)) + i += 1 + continue + + if stripped_line.lower() == "end": + # Pop the current substack's scope + # This pop should always be safe if pseudo-code is balanced + popped_indent, popped_parent_key, first_substack_block, last_substack_block = stack.pop() + if last_substack_block: + all_generated_blocks[last_substack_block]["next"] = None + + if popped_parent_key: + parent_block = all_generated_blocks[popped_parent_key] + # Determine which substack input to set (SUBSTACK or SUBSTACK2 for if-else) + if parent_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in parent_block["inputs"] and \ + parent_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in parent_block["inputs"]: + parent_block["inputs"]["SUBSTACK2"] = [2, first_substack_block] if first_substack_block else [2, None] + elif parent_block["block_shape"] == "C-Block" or parent_block["op_code"] == "procedures_definition": + parent_block["inputs"]["SUBSTACK"] = [2, first_substack_block] if first_substack_block else [2, None] + i += 1 + continue + + # Adjust stack based on indentation + # Pop scopes whose indentation is greater than or equal to the current line's indentation + # The `len(stack) > 1` prevents popping the sentinel. + while len(stack) > 1 and stack[-1][0] >= current_indent: + popped_indent, popped_parent_key, first_chain_block, last_chain_block = stack.pop() + if last_chain_block: + all_generated_blocks[last_chain_block]["next"] = None + # If this popped scope was a substack, ensure its parent links to it. + # This part is crucial for correctly linking chains after indentation changes. + if popped_parent_key: + parent_block = all_generated_blocks[popped_parent_key] + if parent_block["block_shape"] == "C-Block" or parent_block["op_code"] == "procedures_definition": + if parent_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in parent_block["inputs"] and \ + parent_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in parent_block["inputs"]: + parent_block["inputs"]["SUBSTACK2"] = [2, first_chain_block] if first_chain_block else [2, None] + else: + parent_block["inputs"]["SUBSTACK"] = [2, first_chain_block] if first_chain_block else [2, None] + + # Get the current active scope from the stack (guaranteed not empty due to sentinel) + current_scope_indent, current_parent_key, first_active_block_in_chain, last_active_block_in_chain = stack[-1] + + # Classify the statement + stmt_for_parse = stripped_line.rstrip("then").strip() + opcode, ntype = classify(stmt_for_parse) + + if opcode is None: + i += 1 + continue + + # Create the new block + key = pick_key(opcode) + info = copy.deepcopy(all_block_definitions[opcode]) + info["id"] = key + info["next"] = None # Default + + if "inputs" not in info: + info["inputs"] = {} + if "fields" not in info: + info["fields"] = {} + + # Parse inputs and fields + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if opcode == "motion_movesteps": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["STEPS"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_turnright" or opcode == "motion_turnleft": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DEGREES"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_gotoxy": + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_glidesecstoxy": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt_for_parse, re.IGNORECASE) + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_secs: info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_pointindirection": + m = re.search(r"direction\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DIRECTION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_changexby", "motion_changeyby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DX" if opcode == "motion_changexby" else "DY"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_setx", "motion_sety"]: + m = re.search(r"(?:set x to|set y to)\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["X" if opcode == "motion_setx" else "Y"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_changesizeby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_setsizeto": + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SIZE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_changeeffectby", "sound_changeeffectby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE" if opcode == "looks_changeeffectby" else "VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE" if opcode == "looks_seteffectto" else "VOLUME"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["NUM"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "control_wait": + m = re.search(r"wait\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DURATION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "control_repeat": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["TIMES"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "data_changevariableby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "data_deleteoflist": + m = re.search(r"delete\s*\((.+?)\)\s*of", stmt_for_parse, re.IGNORECASE) + if m: + val_str = m.group(1).strip() + if val_str.isdigit(): + info["inputs"]["INDEX"] = {"kind": "value", "value": int(val_str)} + else: # "all", "last", "random" + info["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + elif opcode == "data_insertatlist": + m_item = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt_for_parse, re.IGNORECASE) + m_index = re.search(r"at\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + elif opcode == "data_replaceitemoflist": + m_index = re.search(r"replace item\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + m_item = re.search(r"with\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + elif opcode == "event_whengreaterthan": + m = re.search(r">\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + + # String inputs + elif opcode == "looks_sayforsecs": + m = re.search(r"say\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_say": + m = re.search(r"say\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = parse_reporter_or_value(m.group(1).strip(), key, pick_key, all_generated_blocks) + elif opcode == "looks_thinkforsecs": + m = re.search(r"think\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_think": + m = re.search(r"think\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "sensing_askandwait": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["QUESTION"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["ITEM"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_setvariableto": + m_var = re.search(r"set\s*\[([^\]]+)\s*v\]\s*to\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m_var: + var_name = m_var.group(1).strip() + value_str = m_var.group(2).strip() + info["fields"]["VARIABLE"] = [var_name, None] + info["inputs"]["VALUE"] = parse_reporter_or_value(value_str, key, pick_key, all_generated_blocks) + + # Dropdown/Menu inputs + elif opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_glideto": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m_secs: + info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + option = m_secs.group(2).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TOWARDS"] = {"kind": "menu", "option": option} + elif opcode == "sensing_keypressed": # For boolean block + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["KEY_OPTION"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "sensing_touchingobject": # For boolean block + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + elif option == "edge": option = "_edge_" + info["inputs"]["TOUCHINGOBJECTMENU"] = {"kind": "menu", "option": option} + elif opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "myself": option = "_myself_" + info["inputs"]["CLONE_OPTION"] = {"kind": "menu", "option": option} + elif opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SOUND_MENU"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["COSTUME"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait"]: + m = re.search(r"switch backdrop to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BACKDROP"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BROADCAST_INPUT"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "event_whenbroadcastreceived": + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info.setdefault("fields", {})["BROADCAST_OPTION"] = [m.group(1).strip(), None] + + # Conditional inputs (Boolean blocks) + elif opcode in ["control_if", "control_if_else", "control_wait_until", "control_repeat_until"]: + cond_match = re.search(r"<(.*?)>", stmt_for_parse) + if cond_match: + # Pass pick_key to parse_condition + info["inputs"]["CONDITION"] = parse_condition(cond_match.group(1).strip(), key, pick_key, all_generated_blocks) + elif opcode in ["operator_and", "operator_or", "operator_not", "operator_contains", + "sensing_touchingcolor", "sensing_coloristouchingcolor", "sensing_mousedown"]: + # These are handled by parse_condition directly, which returns the nested structure + # No need to re-parse inputs here, as they are part of the condition structure + pass # Inputs are set when parse_condition is called for the parent block + + + # Fields parsing + if "VARIABLE" in info["fields"]: + m = re.search(r"\[([^\]]+)\s*v\]", stmt_for_parse) + if m: + var_name = m.group(1).strip() + info["fields"]["VARIABLE"] = [var_name, None] + if "LIST" in info["fields"]: + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["LIST"] = [m.group(1), None] + if "STOP_OPTION" in info["fields"]: + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STOP_OPTION"] = [m.group(1), None] + if "STYLE" in info["fields"]: + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STYLE"] = [m.group(1), None] + if "DRAG_MODE" in info["fields"]: + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["DRAG_MODE"] = [m.group(1), None] + if "EFFECT" in info["fields"] and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["EFFECT"] = [m.group(1).upper(), None] + if "NUMBER_NAME" in info["fields"] and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["NUMBER_NAME"] = [m.group(1), None] + if "FRONT_BACK" in info["fields"] and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FRONT_BACK"] = [m.group(1), None] + if "FORWARD_BACKWARD" in info["fields"] and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + if "OPERATOR" in info["fields"] and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["OPERATOR"] = [m.group(1).upper(), None] + if "CURRENTMENU" in info["fields"] and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + if "PROPERTY" in info["fields"] and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt_for_parse, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + info["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + if "WHENGREATERTHANMENU" in info["fields"] and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + if "KEY_OPTION" in info["fields"] and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["KEY_OPTION"] = [m.group(1), None] + if "BACKDROP" in info["fields"] and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BACKDROP"] = [m.group(1), None] + if "BROADCAST_OPTION" in info["fields"] and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + # Custom block specific parsing + if opcode == "procedures_definition": + proc_def_match = re.match(r"(?:define|procedure)\s+([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))?", stmt_for_parse, re.IGNORECASE) + if proc_def_match: + proc_name = proc_def_match.group(1).strip() + args_str = proc_def_match.group(2) + info["procedure_name"] = proc_name + info["is_custom_definition"] = True + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + info["inputs"]["PROCCONTAINER"] = { + "kind": "block_prototype", + "name": proc_name, + "arguments": [{"name": arg, "type": "any"} for arg in args] + } + # For a define block, its children form its body, so push a new scope + # This is handled below in the stack update logic. + + elif opcode == "procedures_call": + call_match = re.match(r"(?:call\s+)?([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", stmt_for_parse, re.IGNORECASE) + if call_match: + custom_block_name = call_match.group(1).strip() + args_str = call_match.group(2) + info["custom_block_name"] = custom_block_name + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for idx, arg_val_str in enumerate(args): + info["inputs"][f"argument_name_{idx+1}"] = parse_reporter_or_value(arg_val_str, key, pick_key, all_generated_blocks) + + # Add the fully constructed block to the global collection + all_generated_blocks[key] = info + + # Update stack based on block type AFTER the block is created and linked + if ntype == "hat": + info["topLevel"] = True + info["parent"] = None # Hat blocks have no parent + top_level_script_keys.append(key) # Add to top-level scripts + + # This hat block *starts* a new chain. + # The next block will be its child. + # Push a new scope for the children of this hat block. + # The `first_active_block_in_chain` for this new scope will be set + # when the *first child* is encountered. + stack.append((current_indent, key, None, None)) # New scope for children + + elif ntype == "c_block" or opcode == "procedures_definition": + info["topLevel"] = False + info["parent"] = current_parent_key + + # This C-block is the 'next' of the previous block in the current chain + if last_active_block_in_chain: + all_generated_blocks[last_active_block_in_chain]["next"] = key + else: # This C-block is the first block in its chain (e.g., first block after a hat) + # If the parent is a hat block, this block is its 'next'. + if current_parent_key and all_generated_blocks[current_parent_key]["block_shape"] == "Hat Block": + all_generated_blocks[current_parent_key]["next"] = key + # Also update the first_active_block_in_chain for the current scope + stack[-1] = (current_scope_indent, current_parent_key, key, last_active_block_in_chain) + + + # Update the last_active_block_in_chain of the *current* scope to this C-block. + stack[-1] = (current_scope_indent, current_parent_key, first_active_block_in_chain if first_active_block_in_chain else key, key) + + # Now, push a new scope for the C-block's/define block's substack. + stack.append((current_indent, key, None, None)) # New scope for children of this C-block + + else: # Regular stack block or reporter/boolean block + info["topLevel"] = False + info["parent"] = current_parent_key + + # This block is the 'next' of the previous block in the current chain + if last_active_block_in_chain: + all_generated_blocks[last_active_block_in_chain]["next"] = key + else: # This block is the first block in its chain (e.g., first block after a hat) + # If the parent is a hat block, this block is its 'next'. + if current_parent_key and all_generated_blocks[current_parent_key]["block_shape"] == "Hat Block": + all_generated_blocks[current_parent_key]["next"] = key + # Also update the first_active_block_in_chain for the current scope + stack[-1] = (current_scope_indent, current_parent_key, key, last_active_block_in_chain) + + # Update the last block in the current chain. + stack[-1] = (current_scope_indent, current_parent_key, first_active_block_in_chain if first_active_block_in_chain else key, key) + + i += 1 # Move to the next line + + # Final pass to link substacks and ensure last blocks have next: None + # This loop now also handles setting the 'next' for hat blocks if they have children. + while len(stack) > 1: # Keep the initial sentinel + popped_indent, popped_parent_key, first_substack_block, last_substack_block = stack.pop() + if last_substack_block: + all_generated_blocks[last_substack_block]["next"] = None + + if popped_parent_key: + parent_block = all_generated_blocks[popped_parent_key] + # If the parent is a Hat block and this is its first child, set its 'next' + if parent_block["block_shape"] == "Hat Block" and parent_block["next"] is None: + parent_block["next"] = first_substack_block + + # Link the substack to its parent if it's a C-block or procedure definition + if parent_block["block_shape"] == "C-Block" or parent_block["op_code"] == "procedures_definition": + if parent_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in parent_block["inputs"] and \ + parent_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in parent_block["inputs"]: + parent_block["inputs"]["SUBSTACK2"] = [2, first_substack_block] if first_substack_block else [2, None] + else: + parent_block["inputs"]["SUBSTACK"] = [2, first_substack_block] if first_substack_block else [2, None] + + + print(f"[ALL OPCODE BLCOKS KEY 2]: {all_generated_blocks}") + # Construct the final flow output based on the collected top-level keys + # Recursively build the block structure for each top-level script + def build_script_flow(current_block_key, visited=None): + if visited is None: + visited = set() + + script_flow = [] + current_iter_key = current_block_key + + while current_iter_key: + # Detect cyclic reference + if current_iter_key in visited: + script_flow.append({ + "block_key": current_iter_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected, stopping recursion" + }) + break + + visited.add(current_iter_key) + block = all_generated_blocks.get(current_iter_key) + if not block: + break # Should not happen if keys are correct + + output_block = { + "block_key": block["id"], + "opcode": block["op_code"], + "type": block["block_shape"].replace(" Block", "").lower().replace("c-", "c_"), + "inputs": {}, + "fields": {} + } + + # Handle all input types + for inp_name, inp_val in block.get("inputs", {}).items(): + if inp_name in ["SUBSTACK", "SUBSTACK2"]: + if inp_val and len(inp_val) > 1 and inp_val[1] in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(inp_val[1], visited.copy()) + else: + output_block["inputs"][inp_name] = [] + elif inp_name == "PROCCONTAINER" and block.get("is_custom_definition"): + output_block["inputs"][inp_name] = inp_val + elif isinstance(inp_val, dict) and inp_val.get("kind") == "block": + # Recursively build nested reporter/boolean blocks + nested_block_key = inp_val["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val # Keep original if not found (shouldn't happen) + elif isinstance(inp_val, dict) and inp_val.get("kind") == "nested_reporter": + nested_block_key = inp_val["reporter"]["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val + else: + output_block["inputs"][inp_name] = inp_val + + for field_name, field_val in block.get("fields", {}).items(): + output_block["fields"][field_name] = field_val + + if block.get("custom_block_name"): + output_block["custom_block_name"] = block["custom_block_name"] + + if block.get("procedure_name"): + output_block["procedure_name"] = block["procedure_name"] + output_block["is_custom_definition"] = True + + script_flow.append(output_block) + + # Proceed to the next block in sequence + next_key = block.get("next") + if next_key in visited: + script_flow.append({ + "block_key": next_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected in 'next' pointer" + }) + break + + current_iter_key = next_key + + return script_flow + + final_flow_output = [] + for key in top_level_script_keys: + final_flow_output.extend(build_script_flow(key)) + return {"flow": final_flow_output} + +# Example input with opcodes for the initial generation +# Example input with opcodes for the initial generation +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_glidesecstoxy","count":1}, + {"opcode":"motion_xposition","count":3}, # Used multiple times in conditions + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":2}, # Two if blocks + {"opcode":"control_stop","count":2}, # stop all v, stop this script v + {"opcode":"operator_lt","count":1}, # Used in condition + {"opcode":"sensing_touchingobject","count":2}, # Used in condition, and for if on edge, bounce + {"opcode":"sensing_touchingobjectmenu","count":2}, # Menu for touchingobject + {"opcode":"event_broadcast","count":2}, # broadcast [Game Over v], broadcast [jump v] + {"opcode":"data_setvariableto","count":3}, # set [score v] to (1), set [speed v] to (1), set [other sprite X v] to ( (x position) of [Sprite2 v] ) + {"opcode":"data_showvariable","count":2}, # show variable [score v], show variable [speed v] + {"opcode":"operator_add","count":2}, # For set [var] to ((var) + (val)), (number 1) + (number 2) + {"opcode":"data_variable","count":5}, # For variable reporters like (score) or [score v] + {"opcode":"looks_sayforsecs","count":2}, # For "say [Hello!] for (2) seconds", say [You win!] for (2) seconds + {"opcode":"looks_say","count":2}, # For "say [Hello! v]", say [Welcome to my game! v] + {"opcode":"motion_movesteps","count":3}, # For "move (10) steps" + {"opcode":"control_wait","count":3}, # For "wait (0.1) seconds", wait (0.5) seconds, wait (1) seconds + {"opcode":"motion_changeyby","count":2}, # For "change y by (10)" + {"opcode":"motion_pointindirection","count":1}, # For "point in direction (90)" + {"opcode":"event_whenkeypressed","count":3}, # For "when [space v] key pressed", when [up arrow v] key pressed, when [right arrow v] key pressed, when [left arrow v] key pressed + {"opcode":"control_repeat","count":2}, # For "repeat (10)" + {"opcode":"event_whenthisspriteclicked","count":2}, # For "when this sprite clicked" + {"opcode":"looks_costumenumbername","count":1}, # For "(costume [name v])" + {"opcode":"operator_join","count":3}, # For "join [Hello ] (answer)", join (length of [shopping list v]) [ items in the list.], join [Hello, ] (username) + {"opcode":"sensing_answer","count":1}, # For "(answer)" + {"opcode":"looks_hide","count":2}, # For "hide" + {"opcode":"control_create_clone_of","count":2}, # For "create clone of [myself v]" + {"opcode":"control_start_as_clone","count":2}, # For "when I start as a clone" + {"opcode":"operator_random","count":1}, # For "pick random -240 to 240" + {"opcode":"motion_ifonedgebounce","count":2}, # For "if on edge, bounce" + {"opcode":"operator_gt","count":2}, # For "if <(score) > (10)> then", if <(item # of [Dog] in [myList v])> (0)> then + {"opcode":"control_if_else","count":1}, # For "if <(score) > (10)> then else" + {"opcode":"sound_play","count":2}, # Changed from sound_start to sound_play + {"opcode":"sensing_loudness","count":2}, # For "(loudness)" + {"opcode":"event_whengreaterthan","count":1}, # For "when [loudness v] > (70)" + {"opcode":"control_repeat_until","count":1}, # For "repeat until " + {"opcode":"looks_cleargraphiceffects","count":1}, # For "clear graphic effects" + {"opcode":"looks_changeeffectby","count":2}, # For "change [color v] effect by (50)", change [fisheye v] effect by (5) + {"opcode":"looks_seteffectto","count":1}, # For "set [ghost v] effect to (75)" + {"opcode":"looks_setsizeto","count":1}, # For "set size to (50) %" + {"opcode":"looks_changesizeby","count":1}, # For "change size by (5)" + {"opcode":"looks_nextcostume","count":2}, # For "next costume" + {"opcode":"looks_switchbackdroptowait","count":1}, # For "switch backdrop to [game over v] and wait" + {"opcode":"looks_nextbackdrop","count":1}, # For "next backdrop" + {"opcode":"sound_playuntildone","count":2}, # For "play sound [fanfare v] until done" + {"opcode":"sound_stopallsounds","count":2}, # For "stop all sounds" + {"opcode":"sound_changevolumeby","count":1}, # For "change volume by (-5)" + {"opcode":"sound_setvolumeto","count":1}, # For "set volume to (50) %" + {"opcode":"sensing_resettimer","count":2}, # For "reset timer" + {"opcode":"sensing_setdragmode","count":2}, # For "set drag mode [not draggable v]" + {"opcode":"data_addtolist","count":1}, # For "add [apple] to [shopping list v]" + {"opcode":"data_deleteoflist","count":1}, # For "delete (all) of [my list v]" + {"opcode":"data_insertatlist","count":1}, # For "insert [orange] at (2) of [fruits v]" + {"opcode":"data_replaceitemoflist","count":1}, # For "replace item (1) of [colors v] with [blue]" + {"opcode":"data_listcontainsitem","count":1}, # For "<[inventory v] contains [key]?>" + {"opcode":"data_itemoflist","count":1}, # For "(item (2) of [myList v])" + {"opcode":"data_lengthoflist","count":2}, # For "(length of [shopping list v])" + {"opcode":"data_itemnumoflist","count":1}, # For "(item # of [Dog] in [myList v])" + {"opcode":"sensing_touchingcolor","count":1}, # For "" + {"opcode":"sensing_coloristouchingcolor","count":1}, # For "" + {"opcode":"operator_and","count":1}, # For "< and >" + {"opcode":"operator_or","count":1}, # For "< or >" + {"opcode":"operator_not","count":1}, # For ">" + {"opcode":"operator_contains","count":1}, # For "<[answer] contains [yes]?>" + {"opcode":"procedures_call","count":1}, # For "jump (50)" + {"opcode":"procedures_definition","count":1}, # For "define jump (height)" + {"opcode":"sensing_of","count":1}, # For "(x position) of [Sprite2 v]" + {"opcode":"sensing_current","count":1}, # For "(current [hour v])" + {"opcode":"sensing_mousex","count":1}, # For "(mouse x)" + {"opcode":"sensing_mousey","count":1}, # For "(mouse y)" + {"opcode":"operator_subtract","count":1}, # For "((10) - (4))" + {"opcode":"operator_multiply","count":1}, # For "(6) * (7)" + {"opcode":"operator_divide","count":1}, # For "((20) / (5))" + {"opcode":"data_list","count":1}, # For "[my list v]" + {"opcode":"looks_gotofrontback","count":1}, # For "go to [front v] layer" + {"opcode":"looks_goforwardbackwardlayers","count":1}, # For "go [forward v] (1) layers" + {"opcode":"sound_sounds_menu","count":2}, # For sound menus + {"opcode":"motion_goto_menu","count":1}, # For motion_goto menu + {"opcode":"motion_glideto_menu","count":1}, # For motion_glideto menu + {"opcode":"motion_pointtowards_menu","count":1}, # For motion_pointtowards menu + {"opcode":"sensing_keyoptions","count":1}, # For sensing_keypressed menu + {"opcode":"sensing_of_object_menu","count":1}, # For sensing_of menu + {"opcode":"control_create_clone_of_menu","count":1}, # For control_create_clone_of menu + {"opcode":"looks_costume","count":1}, # For looks_switchcostumeto menu + {"opcode":"looks_backdrops","count":1}, # For looks_switchbackdropto menu +] + +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_xposition","count":1}, + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":2}, + {"opcode":"control_stop","count":1}, + {"opcode":"operator_lt","count":1}, + {"opcode":"sensing_touchingobject","count":1}, + {"opcode":"sensing_touchingobjectmenu","count":1}, + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":2}, + {"opcode":"data_showvariable","count":2}, +] +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + +# Example pseudo-code inputs from the JSON files +pseudo_code_examples = [ + # From motion_block.json + """ +when green flag clicked + go to x: (0) y: (0) + point in direction (90) + move (50) steps +end + """, + """ +when [right arrow v] key pressed + turn right (15) degrees +end + """, + """ +when this sprite clicked + go to [mouse-pointer v] + """, + """ +when green flag clicked + glide (2) secs to x: (150) y: (-100) + glide (2) secs to x: (-150) y: (100) +end + """, + """ +when green flag clicked + forever + point towards [mouse-pointer v] + move (5) steps + end +end + """, + """ +when [right arrow v] key pressed + change x by (10) +end + """, + """ +when green flag clicked + set x to (0) + set y to (0) +end + """, + """ +when [up arrow v] key pressed + change y by (10) +end + """, + """ +when green flag clicked + forever + move (10) steps + if on edge, bounce + end +end + """, + """ +when green flag clicked + set rotation style [left-right v] + forever + move (10) steps + if on edge, bounce + end +end + """, + # From looks_block.json + """ +when green flag clicked + say [Grr] for (3) seconds + say [Have you seen my honey? v] for (3) seconds +end + """, + """ +when green flag clicked + say [Welcome to my game! v] + wait (2) seconds + say [] +end + """, + """ +when this sprite clicked + think [What should I do? v] for (2) seconds +end + """, + """ +when I receive [correct answer v] + think [That's right! v] + wait (1) seconds + think [good v] +end + """, + """ +when I receive [explosion v] + repeat (5) + next costume + end + hide +end + """, + """ +when green flag clicked + forever + next costume + wait (0.2) seconds + end +end + """, + """ +when green flag clicked + switch backdrop to [start screen v] +end + """, + """ +broadcast [game over v] + switch backdrop to [game over v] and wait + stop [all v] +end + """, + """ +when [space v] key pressed + next backdrop +end + """, + """ +when green flag clicked + repeat (10) + change size by (5) + wait (0.1) seconds + end +end + """, + """ +when green flag clicked + set size to (50) % + wait (1) seconds + set size to (100) % +end + """, + """ +when green flag clicked + forever + change [color v] effect by (5) + wait (0.1) seconds + end +end + """, + """ +when green flag clicked + set [ghost v] effect to (75) +end + """, + """ +when green flag clicked + change [color v] effect by (50) + wait (2) seconds + clear graphic effects +end + """, + """ +when green flag clicked + hide +when I receive [start game v] + show +end + """, + """ +when green flag clicked + hide +end + """, + """ +when green flag clicked + go to [front v] layer +end + """, + """ +when this sprite clicked + go [forward v] (1) layers +end + """, + """ +say join [I am costume ] (costume [name v]) + """, + """ +say join [Current backdrop: ] (backdrop [name v]) for (2) seconds + """, + """ +set size to ( (size) + (10) ) + """, + # From sound_block.json + """ +when backdrop switches to [winning screen v] + play sound [fanfare v] until done + say [You won!] for (2) seconds +end + """, + """ +forever + play sound [Music v] until done +end + """, + """ +when this sprite clicked + start sound [Pop v] + change [score v] by (1) +end + """, + """ +when I receive [game over v] + stop all sounds +end + """, + """ +when [down arrow v] key pressed + change volume by (-5) +end + """, + """ +when green flag clicked + set volume to (50) % +end + """, + """ +say join [Current volume: ] ([volume v]) + """, + # From event_block.json + """ +when green flag clicked + go to x: (0) y: (0) + say [Hello!] for (2) seconds +end + """, + """ +when [space v] key pressed + repeat (10) + change y by (10) + wait (0.1) seconds + change y by (-10) + end +end + """, + """ +when [right arrow v] key pressed + point in direction (90) + move (10) steps +end + """, + """ +when this sprite clicked + say [Ouch!] for (1) seconds + change [score v] by (-1) +end + """, + """ +when backdrop switches to [game over v] + stop [all v] +end + """, + """ +when [loudness v] > (70) + start sound [scream v] +end + """, + """ +when I receive [start game v] + show + go to x: (0) y: (0) +end + """, + """ +when I receive [game over v] + set score to 0 + stop [all v] +end + """, + """ +if then + broadcast [jump v] +end + """, + """ +broadcast [initialize sprites v] and wait + say [Game Started!] for (2) seconds + """, + # From control_block.json + """ +say [Hello!] for (1) seconds + wait (0.5) seconds + say [Goodbye!] for (1) seconds + """, + """ +when green flag clicked + repeat (10) + move (10) steps + wait (0.1) seconds + end +end + """, + """ +when green flag clicked + forever + move (5) steps + if on edge, bounce + end +end + """, + """ +forever + if then + stop [this script v] + end +end + """, + """ +if <([score v]) > (10)> then + say [You win!] for (2) seconds +else + say [Keep trying!] for (2) seconds +end + """, + """ +repeat until + move (5) steps +end + """, + """ +if <(health) = (0)> then + stop [all v] +end + """, + """ +when I start as a clone + wait until + delete this clone +end + """, + """ +when I start as a clone + go to x: (pick random -240 to 240) y: (pick random -180 to 180) + show + forever + move (10) steps + if on edge, bounce + end +end + """, + """ +when I start as a clone + wait (5) seconds + delete this clone +end + """, + """ +when green flag clicked + hide + forever + create clone of [myself v] + wait (1) seconds + end + """, + # From data_block.json + """ +when green flag clicked + set [score v] to (0) + set [player name v] to [Guest] +end + """, + """ +when this sprite clicked + change [score v] by (1) +end + """, + """ +when green flag clicked + add [apple] to [shopping list v] + add [banana] to [shopping list v] +end + """, + """ +when green flag clicked + delete (all) of [my list v] +end + """, + """ +insert [orange] at (2) of [fruits v] + """, + """ +replace item (1) of [colors v] with [blue] + """, + """ +when green flag clicked + show variable [score v] +end + """, + """ +when I receive [game over v] + hide variable [score v] +end + """, + """ +when green flag clicked + show list [shopping list v] +end + """, + """ +when I receive [game over v] + hide list [shopping list v] +end + """, + """ +say ([score v]) for (2) seconds + """, + """ +say ([my list v]) + """, + """ +say (item (2) of [myList v]) for 2 seconds + """, + """ +say join (length of [shopping list v]) [ items in the list.] + """, + """ +if <(item # of [Dog] in [myList v])> (0)> then + say join [Dog found at position ] (item # of [Dog] in [my list v]) +end + """, + # From reporter_blocks.json (some already covered by other categories) + """ +when green flag clicked + say (x position) for (2) seconds +end + """, + """ +set [worms v] to (y position) + """, + """ +when green flag clicked + say (direction) for (2) seconds +end + """, + """ +say join [I am costume ] (costume [name v]) + """, + """ +set size to ( (size) + (10) ) + """, + """ +say join [Current backdrop: ] (backdrop [name v]) for (2) seconds + """, + """ +say join [Current volume: ] (volume) + """, + """ +if <(distance to [Sprite2 v]) < (50)> then + say [Too close!] +end + """, + """ +ask [What is your name?] and wait + say join [Hello ] (answer) + """, + """ +go to x: (mouse x) y: (mouse y) + """, + """ +if <(mouse y) < (0)> then + say [Below center] +end + """, + """ +when green flag clicked + forever + if <(loudness) > (30)> then + start sound [pop v] +end + """, + """ +when green flag clicked + reset timer + wait (5) seconds + say join [Time elapsed: ] (timer) +end + """, + """ +set [other sprite X v] to ( (x position) of [Sprite2 v] ) + """, + """ +say join [The current hour is ] (current [hour v]) + """, + """ +say join [Days passed: ] (days since 2000) + """, + """ +say join [Hello, ] (username) + """, + """ +set [total v] to ( (number 1) + (number 2) ) + """, + """ +set [difference v] to ( (number 1) - (number 2) ) + """, + """ +set [area v] to ( (length) * (width) ) + """, + """ +set [average v] to ( (total score) / (number of students) ) + """, + """ +go to x: (pick random -240 to 240) y: (pick random -180 to 180) + """, + """ +say (join [Hello ][World!]) + """, + """ +say (letter (1) of [apple]) + """, + """ +say (length of [banana]) + """, + """ +if <([number v] mod (2) = (0))> then + say [Even number] +end + """, + """ +set [rounded score v] to (round (score)) + """, + """ +set [distance v] to ([sqrt v] of (((x position) * (x position)) + ((y position) * (y position)))) + """, + # From boolean_blocks.json (conditions already covered by parse_condition) + """ +if <(score) < (10)> then + say [Keep trying!] +end + """, + """ +if <(answer) = (5)> then + say [Correct!] +end + """, + """ +if <([health v]) > (0)> then + move (10) steps +else + stop [all v] +end + """, + """ +if < and > then + say [You're clicking me!] +end + """, + """ +if < or > then + change x by (-10) +end + """, + """ +if > then + say [I'm safe!] +end + """, + """ +if <[answer] contains [yes]?> then + say [Great!] +end + """, + """ +if then + broadcast [Game Over v] +end + """, + """ +if then + bounce off edge +end + """, + """ +if then + change [health v] by (-1) +end + """, + """ +if then + say [Collision!] +end + """, + """ +forever + if then + broadcast [shoot v] + end +end + """, + """ +if then + go to mouse-pointer +end + """, + """ +if <[inventory v] contains [key]?> then + say [You have the key!] +end + """, + # Custom block example + """ +define jump (height) + change y by (height) + wait (0.5) seconds + change y by (0 - (height)) +end + +when green flag clicked + jump (50) +end + """ +] +# pseudo_code_examples = [""" +# when green flag clicked +# go to x: (240) y: (-135) +# set [score v] to (1) +# set [speed v] to (1) +# show variable [score v] +# show variable [speed v] +# forever +# if <((x position)) < (-235)> then +# set x to (240) +# end +# if then +# broadcast [Game Over v] +# stop [all v] +# end +# end +# end +# """,] + +txt="" +trace="" +# Process each example and print the plan +for i, pseudo_code_input in enumerate(pseudo_code_examples): + print(f"\n--- Processing Example {i+1} ---") + print(pseudo_code_input.strip()) + try: + # Regenerate blocks and opcode_occurrences for each run to ensure fresh keys + # This is important because pick_key uses a defaultdict that persists state. + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) + print(json.dumps(plan, indent=2)) + txt += str(plan) + " \n" + except Exception as e: + print(f"Error processing example: {e}") + import traceback + #traceback.print_exc() + trace += str(e) +" "+str(pseudo_code_input)+" \n" + +with open("all_analysis.txt", "w", encoding="utf-8") as f: + f.write(txt) + +with open("all_analysis_trace.txt", "w", encoding="utf-8") as f: + f.write(trace) \ No newline at end of file diff --git a/utils/plan_generator_5.py b/utils/plan_generator_5.py new file mode 100644 index 0000000000000000000000000000000000000000..21d9f16005862f19c4bcc45f30654ad7a00a2e61 --- /dev/null +++ b/utils/plan_generator_5.py @@ -0,0 +1,1988 @@ +import json +import copy +import re +from collections import defaultdict + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False + + generated_blocks[main_key] = main_block_data + + # Handle menus + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_key + + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 1 and \ + main_block_data["inputs"][input_name][0] == 1: + + main_block_data["inputs"][input_name][1] = menu_key + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +# Consolidated block definitions from all JSON files +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_distanceto": { # Added sensing_distanceto + "block_name": "(distance to ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto", + "functionality": "Reports the distance from the sprite to the mouse-pointer or another specified sprite.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": False, "topLevel": True + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": [ + { + "name": "PROCCONTAINER", + "type": "block_prototype" + } + ], + "fields": {}, + "shadow": False, + "topLevel": True + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": [], # Inputs are dynamic based on definition + "fields": {}, + "shadow": False, + "topLevel": True + } +} + +def unparen(s): + s = s.strip() + m = re.fullmatch(r"\((.*)\)", s) + return m.group(1) if m else s + +def _register_block(opcode, parent_key, is_shadow, pick_key_func, all_generated_blocks, inputs=None, fields=None): + """ + Helper to create and register a block in all_generated_blocks. + Returns the key of the newly created block. + """ + key = pick_key_func(opcode) # Use the passed pick_key_func + block_data = copy.deepcopy(all_block_definitions[opcode]) + block_data["id"] = key + block_data["parent"] = parent_key + block_data["next"] = None # Nested blocks as inputs don't have a 'next' + block_data["topLevel"] = False + block_data["shadow"] = is_shadow + + # Initialize inputs and fields if not provided to prevent KeyError later + if "inputs" not in block_data: + block_data["inputs"] = {} + if "fields" not in block_data: + block_data["fields"] = {} + + if inputs: + block_data["inputs"].update(inputs) # Use update to merge, not overwrite + if fields: + block_data["fields"].update(fields) # Use update to merge, not overwrite + + all_generated_blocks[key] = block_data + return key + + +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, parent_key, pick_key_func, all_generated_blocks): + text = text.strip() + + # Check for numeric literal (including parenthesized numbers like "(0)" or "(10)") + m_num = re.fullmatch(r"\(?\s*(-?\d+(\.\d+)?)\s*\)?", text) + if m_num: + val_str = m_num.group(1) + return {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # Check for string literal (e.g., "[Hello!]") + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + # Check for simple reporters, potentially with outer parentheses + m_simple_reporter = re.fullmatch(r"\((.+?)\)", text) + if m_simple_reporter: + inner_text = m_simple_reporter.group(1).strip() + if inner_text in simple_reporters: + block_id = _register_block(simple_reporters[inner_text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + # Also check for simple reporters without parentheses (e.g., if passed directly) + if text in simple_reporters: + block_id = _register_block(simple_reporters[text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + + # Variable reporter: [score v] or (score) or just "score" + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + # Ensure it's not a simple reporter already handled, or a number + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [potential_var_name, None]}) + return {"kind": "block", "block": block_id} + # Handle plain variable names like "score", "number 1", "total score" + if re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_ ]*", text): # Allow spaces for "number 1" etc. + # Exclude known simple reporters that don't have 'v' or parentheses + if text not in simple_reporters: + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [text, None]}) + return {"kind": "block", "block": block_id} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + return {"kind": "block", "block": block_id} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + max_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + inputs = {"FROM": min_val_obj, "TO": max_val_obj} + block_id = _register_block("operator_random", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if min_val_obj.get("kind") == "block": all_generated_blocks[min_val_obj["block"]]["parent"] = block_id + if max_val_obj.get("kind") == "block": all_generated_blocks[max_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (join ()()) (operator_join) - handle both [] and () for inputs + m = re.search(r"join \((.+?)\) \((.+?)\)", text) # Try (val) (val) + if not m: + m = re.search(r"join \[(.+?)\] \[(.+?)\]", text) # Try [val] [val] + if m: + str1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + str2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + block_id = _register_block("operator_join", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if str1_obj.get("kind") == "block": all_generated_blocks[str1_obj["block"]]["parent"] = block_id + if str2_obj.get("kind") == "block": all_generated_blocks[str2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # letter () of () (operator_letterof) - handle both [] and () for inputs + m = re.search(r"letter \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + string_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"LETTER": index_obj, "STRING": string_val_obj} + block_id = _register_block("operator_letterof", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + if string_val_obj.get("kind") == "block": all_generated_blocks[string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (length of ()) (operator_length) - handle both [] and () for inputs + m = re.search(r"length of \((.+?)\)", text) + if not m: + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + list_or_string_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"STRING": list_or_string_val_obj} + block_id = _register_block("operator_length", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if list_or_string_val_obj.get("kind") == "block": all_generated_blocks[list_or_string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (() mod ()) (operator_mod) + m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + if m: + num1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + num2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM1": num1_obj, "NUM2": num2_obj} + block_id = _register_block("operator_mod", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num1_obj.get("kind") == "block": all_generated_blocks[num1_obj["block"]]["parent"] = block_id + if num2_obj.get("kind") == "block": all_generated_blocks[num2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": num_obj} + block_id = _register_block("operator_round", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num_obj.get("kind") == "block": all_generated_blocks[num_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (() of ()) (operator_mathop) - handle variable for function type + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) # e.g. [sqrt v] of ((x pos) * (x pos)) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + # Also handle direct string for function type (e.g., "abs of (x)") + m = re.search(r"([a-zA-Z]+)\s*of\s*\((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + # This regex is designed to handle nested parentheses correctly. + # It looks for an opening parenthesis, then non-parenthesis characters or balanced parentheses, + # followed by an operator, and then the second operand. + # This is a simplified approach; a full-fledged parser would use a stack. + arithmetic_match = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + if not arithmetic_match: + # Try to match without outer parentheses for the operands, but still with an operator + arithmetic_match = re.search(r"(.+?)\s*([+\-*/])\s*(.+)", text) + + if arithmetic_match: + op1_str = arithmetic_match.group(1).strip() + operator_symbol = arithmetic_match.group(2).strip() + op2_str = arithmetic_match.group(3).strip() + + op1_obj = parse_reporter_or_value(op1_str, None, pick_key_func, all_generated_blocks) + op2_obj = parse_reporter_or_value(op2_str, None, pick_key_func, all_generated_blocks) + + opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + inputs = {"NUM1": op1_obj, "NUM2": op2_obj} + block_id = _register_block(opcode_map[operator_symbol], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if op1_obj.get("kind") == "block": all_generated_blocks[op1_obj["block"]]["parent"] = block_id + if op2_obj.get("kind") == "block": all_generated_blocks[op2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (costume ()) (looks_costumenumbername) - handle with or without 'v' + m = re.search(r"costume \((.+?)\)", text) + if not m: + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_costumenumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (backdrop ()) (looks_backdropnumbername) - handle with or without 'v' + m = re.search(r"backdrop \((.+?)\)", text) + if not m: + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_backdropnumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (distance to ()) (sensing_distanceto) - handle with or without 'v' + m = re.search(r"distance to \((.+?)\)", text) + if not m: + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + inputs = {"TARGET": [target_val, None]} # This is a direct value, not a block + block_id = _register_block("sensing_distanceto", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # (current ()) (sensing_current) - handle with or without 'v' + m = re.search(r"current \((.+?)\)", text) + if not m: + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + fields = {"CURRENTMENU": [unit.upper(), None]} + block_id = _register_block("sensing_current", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (() of ()) (sensing_of) - handle both variable and non-variable properties, and objects + # Updated regex to correctly capture the property and object, including nested reporters for property + m = re.search(r"\((.+?)\)\s*of\s*\((.+?)\)", text) # (prop) of (obj) + if not m: + m = re.search(r"\((.+?)\)\s*of\s*\[([^\]]+)\s*v\]", text) # (prop) of [obj v] + if m: + prop_str = m.group(1).strip() + obj = m.group(2).strip() + + # Map common property names to their internal Scratch representation + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + property_value = prop_map.get(prop_str, prop_str) # Use mapped value or original string + + # The object can be a sprite name or "_stage_" + obj_kind = "menu" + if obj.lower() == "stage": obj_val = "_stage_" + elif obj.lower() == "myself": obj_val = "_myself_" + else: obj_val = obj # Assume it's a sprite name + + # Create the menu block for OBJECT input + object_menu_id = _register_block("sensing_of_object_menu", parent_key, True, pick_key_func, all_generated_blocks, fields={"OBJECT": [obj_val, None]}) + + inputs = {"OBJECT": [1, object_menu_id]} # Link to the menu block + block_id = _register_block("sensing_of", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + + # Update parent for the menu block + all_generated_blocks[object_menu_id]["parent"] = block_id + + return {"kind": "block", "block": block_id} + + # (item (index) of [list v]) (data_itemoflist) - handle with or without 'v' and parentheses for index + m = re.search(r"item \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + inputs = {"INDEX": index_obj} + fields = {"LIST": [list_name, None]} + block_id = _register_block("data_itemoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item # of [item] in [list v]) (data_itemnumoflist) - handle with or without 'v' and parentheses for item + m = re.search(r"item # of \((.+?)\) in \((.+?)\)", text) + if not m: + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + inputs = {"ITEM": item_obj} + fields = {"LIST": [list_name, None]} + block_id = _register_block("data_itemnumoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if item_obj.get("kind") == "block": all_generated_blocks[item_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + raise ValueError(f"Can't parse reporter or value: {text}") + +def parse_condition(stmt, parent_key, pick_key_func, all_generated_blocks): + """ + Parse Scratch-style boolean conditions, handling comparisons (<, =, >), + boolean operators (and, or, not), and other sensing conditions. + """ + s = stmt.lower().strip() + + # 1a) Comparisons with explicit angle wrappers: < (...) op (...) > + m = re.fullmatch( + r"\s*<\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*>\s*", + s, + re.VERBOSE + ) + if m: + left_txt, right_txt = m.group(1), m.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), None, pick_key_func, all_generated_blocks) + operand2_obj = parse_reporter_or_value(unparen(right_txt), None, pick_key_func, all_generated_blocks) + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 1b) Simple comparisons without angle wrappers: A op B + m_simple = re.fullmatch(r"\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*", s) + if m_simple: + left_txt, right_txt = m_simple.group(1), m_simple.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), None, pick_key_func, all_generated_blocks) + operand2_obj = parse_reporter_or_value(unparen(right_txt), None, pick_key_func, all_generated_blocks) + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m_simple.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 2) Boolean AND / OR + m = re.fullmatch(r"\s*<\s*(.+?)\s+(and|or)\s+(.+?)\s*>\s*", s) + if m: + cond1_obj = parse_condition(m.group(1), None, pick_key_func, all_generated_blocks) + cond2_obj = parse_condition(m.group(3), None, pick_key_func, all_generated_blocks) + op_block = 'operator_and' if m.group(2) == 'and' else 'operator_or' + + inputs = {"OPERAND1": cond1_obj, "OPERAND2": cond2_obj} + block_id = _register_block(op_block, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if cond1_obj.get("kind") == "block": all_generated_blocks[cond1_obj["block"]]["parent"] = block_id + if cond2_obj.get("kind") == "block": all_generated_blocks[cond2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 3) Boolean NOT + m = re.fullmatch(r"\s*<\s*not\s+(.+?)\s*>\s*", s) + if m: + inner_obj = parse_condition(m.group(1), None, pick_key_func, all_generated_blocks) + inputs = {"OPERAND": inner_obj} + block_id = _register_block("operator_not", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if inner_obj.get("kind") == "block": all_generated_blocks[inner_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 4) Contains: <[list v] contains [item]?> + m = re.search(r"\[([^\]]+)\s*v\] contains \[(.+?)\]\?", s) + if m: + list_name = m.group(1).strip() + item_val = {"kind": "value", "value": m.group(2).strip()} # Item can be a value or a block + + # Create the data_list reporter block + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + + inputs = {"LIST": {"kind": "block", "block": list_block_id}, "ITEM": item_val} + block_id = _register_block("data_listcontainsitem", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 5) Touching object: + m = re.search(r"touching \[([^\]]+)\s*v\]\?", s) + if m: + opt = m.group(1).strip() + opt_val = {'mouse-pointer':'_mouse_','edge':'_edge_'}.get(opt, opt) + + # Create the menu block for TOUCHINGOBJECTMENU input + menu_block_id = _register_block("sensing_touchingobjectmenu", parent_key, True, pick_key_func, all_generated_blocks, fields={"TOUCHINGOBJECTMENU": [opt_val, None]}) + + inputs = {"TOUCHINGOBJECTMENU": [1, menu_block_id]} # Link to the menu block + block_id = _register_block("sensing_touchingobject", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + all_generated_blocks[menu_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 6) Touching color: + m = re.search(r"touching color \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR": [1, [9, m.group(1)]]} # Color input is special, often a list [type, value] + block_id = _register_block("sensing_touchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # 7) Color is touching color: + m = re.search(r"color \[(#[0-9A-Fa-f]{6})\] is touching \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR1": [1, [9, m.group(1)]], "COLOR2": [1, [9, m.group(2)]]} + block_id = _register_block("sensing_coloristouchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # 8) Key pressed: + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", s) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", parent_key, True, pick_key_func, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) + + inputs = {"KEY_OPTION": [1, menu_block_id]} + block_id = _register_block("sensing_keypressed", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + all_generated_blocks[menu_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 9) Mouse down?: mouse down? + if s == "mouse down?": + block_id = _register_block("sensing_mousedown", parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + raise ValueError(f"Can't parse condition: {stmt}") + +def classify(line): + """ + Classifies a pseudo-code line into its corresponding Scratch opcode and block type. + Order of checks matters: more specific patterns should come before more general ones. + """ + l = line.lower().strip() + + # Ignore comments + if l.startswith("//"): return None, None + + # Hat Blocks (most specific first) + if re.match(r"when green flag click(ed)?", l): return "event_whenflagclicked", "hat" + if re.match(r"when (.+?) key press(ed)?", l): return "event_whenkeypressed", "hat" + if re.match(r"when this sprite click(ed)?", l): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if re.match(r"when i start as a clo(ne)?", l): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + if l.startswith("procedure "): return "procedures_definition", "hat" # For "procedure moveBall" + + # Motion Blocks + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + # IMPORTANT: More specific glide block before less specific one + if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if re.match(r"set x to\s*\(.+\)", l): return "motion_setx", "stack" # Specific for set x + if l.startswith("change y by"): return "motion_changeyby", "stack" + if re.match(r"set y to\s*\(.+\)", l): return "motion_sety", "stack" # Specific for set y + if re.match(r"if on edge, bounce( off edge)?", l): return "motion_ifonedgebounce", "stack" + if re.match(r"bounce off edg(e)?", l): return "motion_ifonedgebounce", "stack" # Alias + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + + + # Looks Blocks + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if re.match(r"next costum(e)?", l): return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + # Updated regex for change/set effect by/to + if re.match(r"change\s*(\[.+?v\]|\(.+?\))?\s*effect by", l): return "looks_changeeffectby", "stack" + if re.match(r"set\s*(\[.+?v\]|\(.+?\))?\s*effect to", l): return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + # Sound Blocks + if re.match(r"play sound (.+?) until do(ne)?", l): return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + # Event Blocks (broadcasts) + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + # Control Blocks + if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + # Updated regex for stop block to handle different options + if re.match(r"stop \[(all|this script|other scripts in sprite)\s*v\]", l): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + # Data Blocks + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + # Updated regex for delete of list + if re.match(r"delete \((.+?)\) of \[([^\]]+)\s*v\]", l): return "data_deleteoflist", "stack" + if l.startswith("delete all of [" ): return "data_deletealloflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + # Sensing Blocks + if re.match(r"ask (.+?) and wai(t)?", l): return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom Blocks (procedures_call) - specific rule for "call" + if l.startswith("call "): + return "procedures_call", "stack" + + # Custom Blocks (procedures_call) - LAST RESORT (generic match) + # This should be the very last check for stack-type blocks to avoid conflicts. + # It tries to match anything that looks like a function call with or without arguments. + custom_block_match = re.match(r"([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", l) + if custom_block_match: + # Before returning, ensure it's not a known simple reporter or variable name + # that might have been missed or is being used standalone. + # This is a heuristic; a full parser would be more robust. + potential_name = custom_block_match.group(1).strip() + if potential_name not in ["x position", "y position", "direction", "mouse x", "mouse y", "loudness", "timer", "days since 2000", "username", "answer", "size", "volume"] and \ + not re.fullmatch(r"\[[^\]]+\]", potential_name) and \ + not re.fullmatch(r"\[[^\]]+\]\s*v", potential_name): + return "procedures_call", "stack" + + + raise ValueError(f"Unknown statement: {line!r}") + + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key -> block_data (pre-generated block definitions) + • opcode_keys: dict of opcode -> list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... list of block dictionaries ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + all_generated_blocks = {} + for key, block_data in generated_input.items(): + all_generated_blocks[key] = copy.deepcopy(block_data) + # Ensure initial blocks have no next/parent/topLevel set from previous runs + all_generated_blocks[key]["next"] = None + all_generated_blocks[key]["parent"] = None + all_generated_blocks[key]["topLevel"] = False # Will be set to True for Hat blocks + + + # Stack stores (indent, parent_block_key, first_block_in_this_chain, last_block_in_this_chain) + # The last element of the stack is always the currently active scope. + # Sentinel: global scope (indent -1, no parent, no blocks yet in this "chain") + stack = [(-1, None, None, None)] + + top_level_script_keys = [] + + lines = pseudo_code.splitlines() + i = 0 + while i < len(lines): + raw_line = lines[i] + stripped_line = raw_line.strip() + + # Skip empty lines and comments + if not stripped_line or stripped_line.startswith("//"): + i += 1 + continue + + current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2 + + # Handle 'else' and 'end' keywords first, as they control scope + if stripped_line.lower() == "else": + # Pop the 'then' substack's scope + # This pop should always be safe as 'else' implies an 'if' scope exists + popped_indent, popped_parent_key, first_then_block, last_then_block = stack.pop() + if last_then_block: + all_generated_blocks[last_then_block]["next"] = None + + # Link the 'then' substack to its 'if-else' parent + if popped_parent_key and all_generated_blocks[popped_parent_key]["op_code"] == "control_if_else": + all_generated_blocks[popped_parent_key]["inputs"]["SUBSTACK"] = [2, first_then_block] if first_then_block else [2, None] + # Push a new scope for the 'else' substack, with the same parent + stack.append((current_indent, popped_parent_key, None, None)) + i += 1 + continue + + if stripped_line.lower() == "end": + # Pop the current substack's scope + # This pop should always be safe if pseudo-code is balanced + popped_indent, popped_parent_key, first_substack_block, last_substack_block = stack.pop() + if last_substack_block: + all_generated_blocks[last_substack_block]["next"] = None + + if popped_parent_key: + parent_block = all_generated_blocks[popped_parent_key] + # Determine which substack input to set (SUBSTACK or SUBSTACK2 for if-else) + if parent_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in parent_block["inputs"] and \ + parent_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in parent_block["inputs"]: + parent_block["inputs"]["SUBSTACK2"] = [2, first_substack_block] if first_substack_block else [2, None] + elif parent_block["block_shape"] == "C-Block" or parent_block["op_code"] == "procedures_definition": + parent_block["inputs"]["SUBSTACK"] = [2, first_substack_block] if first_substack_block else [2, None] + i += 1 + continue + + # Adjust stack based on indentation + # Pop scopes whose indentation is greater than or equal to the current line's indentation + # The `len(stack) > 1` prevents popping the sentinel. + while len(stack) > 1 and stack[-1][0] >= current_indent: + popped_indent, popped_parent_key, first_chain_block, last_chain_block = stack.pop() + if last_chain_block: + all_generated_blocks[last_chain_block]["next"] = None + # If this popped scope was a substack, ensure its parent links to it. + # This part is crucial for correctly linking chains after indentation changes. + if popped_parent_key: + parent_block = all_generated_blocks[popped_parent_key] + if parent_block["block_shape"] == "C-Block" or parent_block["op_code"] == "procedures_definition": + if parent_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in parent_block["inputs"] and \ + parent_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in parent_block["inputs"]: + parent_block["inputs"]["SUBSTACK2"] = [2, first_chain_block] if first_chain_block else [2, None] + else: + parent_block["inputs"]["SUBSTACK"] = [2, first_chain_block] if first_chain_block else [2, None] + + + # Get the current active scope from the stack (guaranteed not empty due to sentinel) + current_scope_indent, current_parent_key, first_active_block_in_chain, last_active_block_in_chain = stack[-1] + + # Classify the statement + stmt_for_parse = stripped_line.rstrip("then").strip() + opcode, ntype = classify(stmt_for_parse) + + if opcode is None: + i += 1 + continue + + # Create the new block + key = pick_key(opcode) + info = copy.deepcopy(all_block_definitions[opcode]) + info["id"] = key + info["next"] = None # Default + + if "inputs" not in info: + info["inputs"] = {} + if "fields" not in info: + info["fields"] = {} + + # Parse inputs and fields + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if opcode == "motion_movesteps": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["STEPS"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_turnright" or opcode == "motion_turnleft": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DEGREES"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_gotoxy": + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_glidesecstoxy": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt_for_parse, re.IGNORECASE) + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_secs: info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m.group(1))} + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m.group(1))} + elif opcode == "motion_pointindirection": + m = re.search(r"direction\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DIRECTION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_changexby", "motion_changeyby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DX" if opcode == "motion_changexby" else "DY"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_setx", "motion_sety"]: + m = re.search(r"(?:set x to|set y to)\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["X" if opcode == "motion_setx" else "Y"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_changesizeby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_setsizeto": + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SIZE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_changeeffectby", "sound_changeeffectby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE" if opcode == "looks_changeeffectby" else "VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE" if opcode == "looks_seteffectto" else "VOLUME"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["NUM"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "control_wait": + m = re.search(r"wait\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DURATION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "control_repeat": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["TIMES"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "data_changevariableby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "data_deleteoflist": + m = re.search(r"delete\s*\((.+?)\)\s*of", stmt_for_parse, re.IGNORECASE) + if m: + val_str = m.group(1).strip() + if val_str.isdigit(): + info["inputs"]["INDEX"] = {"kind": "value", "value": int(val_str)} + else: # "all", "last", "random" + info["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + elif opcode == "data_insertatlist": + m_item = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt_for_parse, re.IGNORECASE) + m_index = re.search(r"at\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + elif opcode == "data_replaceitemoflist": + m_index = re.search(r"replace item\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + m_item = re.search(r"with\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + elif opcode == "event_whengreaterthan": + m = re.search(r">\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + + # String inputs + elif opcode == "looks_sayforsecs": + m = re.search(r"say\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_say": + m = re.search(r"say\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = parse_reporter_or_value(m.group(1).strip(), key, pick_key, all_generated_blocks) + elif opcode == "looks_thinkforsecs": + m = re.search(r"think\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_think": + m = re.search(r"think\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "sensing_askandwait": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["QUESTION"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["ITEM"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_setvariableto": + m_var = re.search(r"set\s*\[([^\]]+)\s*v\]\s*to\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m_var: + var_name = m_var.group(1).strip() + value_str = m_var.group(2).strip() + info["fields"]["VARIABLE"] = [var_name, None] + info["inputs"]["VALUE"] = parse_reporter_or_value(value_str, key, pick_key, all_generated_blocks) + + # Dropdown/Menu inputs + elif opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_glideto": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m_secs: + info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + option = m_secs.group(2).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TOWARDS"] = {"kind": "menu", "option": option} + elif opcode == "sensing_keypressed": # For boolean block + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["KEY_OPTION"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "sensing_touchingobject": # For boolean block + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + elif option == "edge": option = "_edge_" + info["inputs"]["TOUCHINGOBJECTMENU"] = {"kind": "menu", "option": option} + elif opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "myself": option = "_myself_" + info["inputs"]["CLONE_OPTION"] = {"kind": "menu", "option": option} + elif opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SOUND_MENU"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["COSTUME"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait"]: + m = re.search(r"switch backdrop to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BACKDROP"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BROADCAST_INPUT"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "event_whenbroadcastreceived": + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BROADCAST_OPTION"] = {"kind": "menu", "option": m.group(1).strip()} + + # Conditional inputs (Boolean blocks) + elif opcode in ["control_if", "control_if_else", "control_wait_until", "control_repeat_until"]: + cond_match = re.search(r"<(.*?)>", stmt_for_parse) + if cond_match: + # Pass pick_key to parse_condition + info["inputs"]["CONDITION"] = parse_condition(cond_match.group(1).strip(), key, pick_key, all_generated_blocks) + elif opcode in ["operator_and", "operator_or", "operator_not", "operator_contains", + "sensing_touchingcolor", "sensing_coloristouchingcolor", "sensing_mousedown"]: + # These are handled by parse_condition directly, which returns the nested structure + # No need to re-parse inputs here, as they are part of the condition structure + pass # Inputs are set when parse_condition is called for the parent block + + + # Fields parsing + if "VARIABLE" in info["fields"]: + m = re.search(r"\[([^\]]+)\s*v\]", stmt_for_parse) + if m: + var_name = m.group(1).strip() + info["fields"]["VARIABLE"] = [var_name, None] + if "LIST" in info["fields"]: + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["LIST"] = [m.group(1), None] + if "STOP_OPTION" in info["fields"]: + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STOP_OPTION"] = [m.group(1), None] + if "STYLE" in info["fields"]: + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STYLE"] = [m.group(1), None] + if "DRAG_MODE" in info["fields"]: + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["DRAG_MODE"] = [m.group(1), None] + if "EFFECT" in info["fields"] and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["EFFECT"] = [m.group(1).upper(), None] + if "NUMBER_NAME" in info["fields"] and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["NUMBER_NAME"] = [m.group(1), None] + if "FRONT_BACK" in info["fields"] and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FRONT_BACK"] = [m.group(1), None] + if "FORWARD_BACKWARD" in info["fields"] and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + if "OPERATOR" in info["fields"] and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["OPERATOR"] = [m.group(1).upper(), None] + if "CURRENTMENU" in info["fields"] and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + if "PROPERTY" in info["fields"] and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt_for_parse, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + info["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + if "WHENGREATERTHANMENU" in info["fields"] and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + if "KEY_OPTION" in info["fields"] and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["KEY_OPTION"] = [m.group(1), None] + if "BACKDROP" in info["fields"] and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BACKDROP"] = [m.group(1), None] + if "BROADCAST_OPTION" in info["fields"] and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + # Custom block specific parsing + if opcode == "procedures_definition": + proc_def_match = re.match(r"(?:define|procedure)\s+([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))?", stmt_for_parse, re.IGNORECASE) + if proc_def_match: + proc_name = proc_def_match.group(1).strip() + args_str = proc_def_match.group(2) + info["procedure_name"] = proc_name + info["is_custom_definition"] = True + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + info["inputs"]["PROCCONTAINER"] = { + "kind": "block_prototype", + "name": proc_name, + "arguments": [{"name": arg, "type": "any"} for arg in args] + } + # For a define block, its children form its body, so push a new scope + # This is handled below in the stack update logic. + + elif opcode == "procedures_call": + call_match = re.match(r"(?:call\s+)?([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", stmt_for_parse, re.IGNORECASE) + if call_match: + custom_block_name = call_match.group(1).strip() + args_str = call_match.group(2) + info["custom_block_name"] = custom_block_name + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for idx, arg_val_str in enumerate(args): + info["inputs"][f"argument_name_{idx+1}"] = parse_reporter_or_value(arg_val_str, key, pick_key, all_generated_blocks) + + # Add the fully constructed block to the global collection + all_generated_blocks[key] = info + + # Update stack based on block type AFTER the block is created and linked + if ntype == "hat": + info["topLevel"] = True + info["parent"] = None # Hat blocks have no parent + top_level_script_keys.append(key) # Add to top-level scripts + + # This hat block *starts* a new chain. + # The next block will be its child. + # Push a new scope for the children of this hat block. + # The `first_active_block_in_chain` for this new scope will be set + # when the *first child* is encountered. + stack.append((current_indent, key, None, None)) # New scope for children + + elif ntype == "c_block" or opcode == "procedures_definition": + info["topLevel"] = False + info["parent"] = current_parent_key + + # This C-block is the 'next' of the previous block in the current chain + if last_active_block_in_chain: + all_generated_blocks[last_active_block_in_chain]["next"] = key + else: # This C-block is the first block in its chain (e.g., first block after a hat) + # If the parent is a hat block, this block is its 'next'. + if current_parent_key and all_generated_blocks[current_parent_key]["block_shape"] == "Hat Block": + all_generated_blocks[current_parent_key]["next"] = key + # Also update the first_active_block_in_chain for the current scope + stack[-1] = (current_scope_indent, current_parent_key, key, last_active_block_in_chain) + + + # Update the last_active_block_in_chain of the *current* scope to this C-block. + stack[-1] = (current_scope_indent, current_parent_key, first_active_block_in_chain if first_active_block_in_chain else key, key) + + # Now, push a new scope for the C-block's/define block's substack. + stack.append((current_indent, key, None, None)) # New scope for children of this C-block + + else: # Regular stack block or reporter/boolean block + info["topLevel"] = False + info["parent"] = current_parent_key + + # This block is the 'next' of the previous block in the current chain + if last_active_block_in_chain: + all_generated_blocks[last_active_block_in_chain]["next"] = key + else: # This block is the first block in its chain (e.g., first block after a hat) + # If the parent is a hat block, this block is its 'next'. + if current_parent_key and all_generated_blocks[current_parent_key]["block_shape"] == "Hat Block": + all_generated_blocks[current_parent_key]["next"] = key + # Also update the first_active_block_in_chain for the current scope + stack[-1] = (current_scope_indent, current_parent_key, key, last_active_block_in_chain) + + # Update the last block in the current chain. + stack[-1] = (current_scope_indent, current_parent_key, first_active_block_in_chain if first_active_block_in_chain else key, key) + + i += 1 # Move to the next line + + # Final pass to link substacks and ensure last blocks have next: None + # This loop now also handles setting the 'next' for hat blocks if they have children. + while len(stack) > 1: # Keep the initial sentinel + popped_indent, popped_parent_key, first_substack_block, last_substack_block = stack.pop() + if last_substack_block: + all_generated_blocks[last_substack_block]["next"] = None + + if popped_parent_key: + parent_block = all_generated_blocks[popped_parent_key] + # If the parent is a Hat block and this is its first child, set its 'next' + if parent_block["block_shape"] == "Hat Block" and parent_block["next"] is None: + parent_block["next"] = first_substack_block + + # Link the substack to its parent if it's a C-block or procedure definition + if parent_block["block_shape"] == "C-Block" or parent_block["op_code"] == "procedures_definition": + if parent_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in parent_block["inputs"] and \ + parent_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in parent_block["inputs"]: + parent_block["inputs"]["SUBSTACK2"] = [2, first_substack_block] if first_substack_block else [2, None] + else: + parent_block["inputs"]["SUBSTACK"] = [2, first_substack_block] if first_substack_block else [2, None] + + + print(f"[ALL OPCODE BLCOKS KEY 2]: {all_generated_blocks}") + # Construct the final flow output based on the collected top-level keys + # Recursively build the block structure for each top-level script + def build_script_flow(current_block_key, visited=None): + if visited is None: + visited = set() + + script_flow = [] + current_iter_key = current_block_key + + while current_iter_key: + # Detect cyclic reference + if current_iter_key in visited: + script_flow.append({ + "block_key": current_iter_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected, stopping recursion" + }) + break + + visited.add(current_iter_key) + block = all_generated_blocks.get(current_iter_key) + if not block: + break # Should not happen if keys are correct + + output_block = { + "block_key": block["id"], + "opcode": block["op_code"], + "type": block["block_shape"].replace(" Block", "").lower().replace("c-", "c_"), + "inputs": {}, + "fields": {} + } + + # Handle all input types + for inp_name, inp_val in block.get("inputs", {}).items(): + if inp_name in ["SUBSTACK", "SUBSTACK2"]: + if inp_val and len(inp_val) > 1 and inp_val[1] in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(inp_val[1], visited.copy()) + else: + output_block["inputs"][inp_name] = [] + elif inp_name == "PROCCONTAINER" and block.get("is_custom_definition"): + output_block["inputs"][inp_name] = inp_val + elif isinstance(inp_val, dict) and inp_val.get("kind") == "block": + # Recursively build nested reporter/boolean blocks + nested_block_key = inp_val["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val # Keep original if not found (shouldn't happen) + elif isinstance(inp_val, dict) and inp_val.get("kind") == "nested_reporter": + nested_block_key = inp_val["reporter"]["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val + else: + output_block["inputs"][inp_name] = inp_val + + for field_name, field_val in block.get("fields", {}).items(): + output_block["fields"][field_name] = field_val + + if block.get("custom_block_name"): + output_block["custom_block_name"] = block["custom_block_name"] + + if block.get("procedure_name"): + output_block["procedure_name"] = block["procedure_name"] + output_block["is_custom_definition"] = True + + script_flow.append(output_block) + + # Proceed to the next block in sequence + next_key = block.get("next") + if next_key in visited: + script_flow.append({ + "block_key": next_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected in 'next' pointer" + }) + break + + current_iter_key = next_key + + return script_flow + + final_flow_output = [] + for key in top_level_script_keys: + final_flow_output.extend(build_script_flow(key)) + return {"flow": final_flow_output} + +# Example input with opcodes for the initial generation +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_xposition","count":1}, + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":2}, + {"opcode":"control_stop","count":1}, + {"opcode":"operator_lt","count":1}, + {"opcode":"sensing_touchingobject","count":1}, + {"opcode":"sensing_touchingobjectmenu","count":1}, + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":2}, + {"opcode":"data_showvariable","count":2}, +] + +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + +# Example pseudo-code inputs from the JSON files +pseudo_code_examples = [""" +when green flag clicked + go to x: (240) y: (-135) + set [score v] to (1) + set [speed v] to (1) + show variable [score v] + show variable [speed v] + forever + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end +end +""",] + +# Process each example and print the plan +for i, pseudo_code_input in enumerate(pseudo_code_examples): + print(f"\n--- Processing Example {i+1} ---") + print(pseudo_code_input.strip()) + try: + # Regenerate blocks and opcode_occurrences for each run to ensure fresh keys + # This is important because pick_key uses a defaultdict that persists state. + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) + print(json.dumps(plan, indent=2)) + except Exception as e: + print(f"Error processing example: {e}") + import traceback + traceback.print_exc() \ No newline at end of file diff --git a/utils/plan_generator_6.py b/utils/plan_generator_6.py new file mode 100644 index 0000000000000000000000000000000000000000..94bf35fd5500e843639758b722af13eb78a2bed0 --- /dev/null +++ b/utils/plan_generator_6.py @@ -0,0 +1,2254 @@ +import json +import copy +import re +from collections import defaultdict + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False + + # Ensure inputs and fields are dictionaries, even if they were None or list in definition + if "inputs" not in main_block_data or not isinstance(main_block_data["inputs"], dict): + main_block_data["inputs"] = {} + if "fields" not in main_block_data or not isinstance(main_block_data["fields"], dict): + main_block_data["fields"] = {} + + generated_blocks[main_key] = main_block_data + + # Handle menus + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_key + + # Ensure inputs and fields are dictionaries for menu blocks too + if "inputs" not in menu_block_data or not isinstance(menu_block_data["inputs"], dict): + menu_block_data["inputs"] = {} + if "fields" not in menu_block_data or not isinstance(menu_block_data["fields"], dict): + menu_block_data["fields"] = {} + + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 1 and \ + main_block_data["inputs"][input_name][0] == 1: + + main_block_data["inputs"][input_name][1] = menu_key + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +# Consolidated block definitions from all JSON files +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": False + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "motion_yposition": { + "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 + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_distanceto": { # Added sensing_distanceto + "block_name": "(distance to ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto", + "functionality": "Reports the distance from the sprite to the mouse-pointer or another specified sprite.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": False, "topLevel": False + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": False + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": False + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": False + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": False + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": False + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": False + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": False, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": False + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": False + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": False + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": False + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": False + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": False + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": False + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": False + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": False + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": False + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": False + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": False + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": False + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": False + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": False + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": False + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": False + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": False + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": False + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": False + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": False + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": False + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": False + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": False + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": False, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": False, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": False + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": False + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": False + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": False + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": False + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": False + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": False + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": False + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": False + } +} + +def unparen(s): + s = s.strip() + # keep peeling off *all* matching outer parens + while True: + m = re.fullmatch(r"\((.*)\)", s) + if not m: + break + s = m.group(1).strip() + return s + +def _register_block(opcode, parent_key, is_shadow, pick_key_func, all_generated_blocks, inputs=None, fields=None): + """ + Helper to create and register a block in all_generated_blocks. + Returns the key of the newly created block. + """ + key = pick_key_func(opcode) # Use the passed pick_key_func + block_data = copy.deepcopy(all_block_definitions[opcode]) + block_data["id"] = key + block_data["parent"] = parent_key + block_data["next"] = None # Nested blocks as inputs don't have a 'next' + block_data["topLevel"] = False + block_data["shadow"] = is_shadow + + # Initialize inputs and fields if not provided to prevent KeyError later + if "inputs" not in block_data: + block_data["inputs"] = {} + if "fields" not in block_data: + block_data["fields"] = {} + + if inputs: + block_data["inputs"].update(inputs) # Use update to merge, not overwrite + if fields: + block_data["fields"].update(fields) # Use update to merge, not overwrite + + all_generated_blocks[key] = block_data + return key + +def _auto_balance(text): + # if there are more "(" than ")", append the missing ")" + diff = text.count("(") - text.count(")") + if diff > 0: + text = text + ")"*diff + # same for square brackets + diff = text.count("[") - text.count("]") + if diff > 0: + text = text + "]"*diff + return text + +def strip_outer_angle_brackets(text): + """ + Strip exactly one balanced pair of outer <...> brackets, only if they wrap the whole string. + """ + text = text.strip() + if text.startswith("<") and text.endswith(">"): + depth = 0 + for i, char in enumerate(text): + if char == '<': + depth += 1 + elif char == '>': + depth -= 1 + if depth == 0 and i == len(text) - 1: + return text[1:-1].strip() + return text + +def extract_condition_balanced(stmt): + # 1. Remove "if" and "then" + stmt = stmt.strip() + if stmt.lower().startswith("if "): + stmt = stmt[3:].strip() + if stmt.lower().startswith("repeat until"): + stmt = stmt[12:].strip() + if stmt.lower().startswith("wait until "): + stmt = stmt[11:].strip() + if stmt.lower().endswith(" then"): + stmt = stmt[:-5].strip() + + # Helper to detect and strip single outer balanced angle brackets + def unwrap_balanced(s): + if s.startswith("<") and s.endswith(">"): + depth = 0 + for i in range(len(s)): + if s[i] == "<": + depth += 1 + elif s[i] == ">": + depth -= 1 + if depth == 0 and i < len(s) - 1: + return s # Early balance → not a single outer wrapper + if depth == 0: + return s[1:-1].strip() + return s + + # Recursively simplify things like > to not + def simplify(s): + s = unwrap_balanced(s) + s = s.strip() + + # Match > pattern + m = re.fullmatch(r"not\s*<(.+)>", s, re.IGNORECASE) + if m: + inner = m.group(1).strip() + inner = simplify(inner) + return f"not <{inner}>" + + # Match comparison operators like <(x position) < (100)> + m_comp = re.fullmatch(r"<\s*\(([^<>]+?)\)\s*([<>=])\s*\(([^<>]+?)\)\s*>", stmt) + if m_comp: + return f"({m_comp.group(1).strip()}) {m_comp.group(2)} ({m_comp.group(3).strip()})" + + return s + + return simplify(stmt) + +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, parent_key, pick_key_func, all_generated_blocks): + text = _auto_balance(text.strip()) + text = unparen(text.strip()) + # Check for numeric literal (including parenthesized numbers like "(0)" or "(10)") + m_num = re.fullmatch(r"\(?\s*(-?\d+(\.\d+)?)\s*\)?", text) + if m_num: + val_str = m_num.group(1) + return {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # # Check for string literal (e.g., "[Hello!]") + # if text.startswith('[') and text.endswith(']'): + # return {"kind": "value", "value": text[1:-1]} + # Variable reporter: [score v], [health v], etc. + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, + fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + + # Now catch other bracketed values as literal strings + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + # Check for simple reporters, potentially with outer parentheses + m_simple_reporter = re.fullmatch(r"\((.+?)\)", text) + if m_simple_reporter: + inner_text = m_simple_reporter.group(1).strip() + if inner_text in simple_reporters: + block_id = _register_block(simple_reporters[inner_text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + # Also check for simple reporters without parentheses (e.g., if passed directly) + if text in simple_reporters: + block_id = _register_block(simple_reporters[text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + + # Variable reporter: [score v] or (score) or just "score" + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + # Ensure it's not a simple reporter already handled, or a number + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [potential_var_name, None]}) + return {"kind": "block", "block": block_id} + # Handle plain variable names like "score", "number 1", "total score" + if re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_ ]*", text): # Allow spaces for "number 1" etc. + # Exclude known simple reporters that don't have 'v' or parentheses + if text not in simple_reporters: + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [text, None]}) + return {"kind": "block", "block": block_id} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + return {"kind": "block", "block": block_id} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + max_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + inputs = {"FROM": min_val_obj, "TO": max_val_obj} + block_id = _register_block("operator_random", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if min_val_obj.get("kind") == "block": all_generated_blocks[min_val_obj["block"]]["parent"] = block_id + if max_val_obj.get("kind") == "block": all_generated_blocks[max_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (join ()()) (operator_join) - handle both [] and () for inputs + + # m = re.search(r"join \((.+?)\) \((.+?)\)", text) # Try (val) (val) + # if not m: + # m = re.search(r"join \[(.+?)\] \[(.+?)\]", text) # Try [val] [val] + # if m: + # str1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + # str2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + # inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + # block_id = _register_block("operator_join", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # if str1_obj.get("kind") == "block": all_generated_blocks[str1_obj["block"]]["parent"] = block_id + # if str2_obj.get("kind") == "block": all_generated_blocks[str2_obj["block"]]["parent"] = block_id + # return {"kind": "block", "block": block_id} + + #m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s+(\[.+?\]|\(.+?\))", text) # Try (val) (val) + m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s*(\[.+?\]|\(.+?\))", text) + if m: + part1_txt = m.group(1).strip() + part2_txt = m.group(2).strip() + str1_obj = parse_reporter_or_value(part1_txt, None, pick_key_func, all_generated_blocks) + str2_obj = parse_reporter_or_value(part2_txt, None, pick_key_func, all_generated_blocks) + inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + block_id = _register_block("operator_join", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs) + # set parents if nested blocks + for obj in (str1_obj, str2_obj): + if obj.get("kind") == "block": + all_generated_blocks[obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # letter () of () (operator_letterof) - handle both [] and () for inputs + m = re.search(r"letter \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + string_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"LETTER": index_obj, "STRING": string_val_obj} + block_id = _register_block("operator_letterof", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + if string_val_obj.get("kind") == "block": all_generated_blocks[string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (length of ()) (operator_length) - handle both [] and () for inputs + #m = re.search(r"length of \((.+?)\)", text) + m = re.search(r"length of\s*(?:\((.+?)\)|\[(.+?)\])", text) + if not m: + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + arg_txt = (m.group(1) or m.group(2)).strip() + list_or_string_val_obj = parse_reporter_or_value(arg_txt, None, pick_key_func, all_generated_blocks) + #list_or_string_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"STRING": list_or_string_val_obj} + block_id = _register_block("operator_length", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if list_or_string_val_obj.get("kind") == "block": all_generated_blocks[list_or_string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (() mod ()) (operator_mod) + # m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + m = re.search(r"\[([^\]]+)\s*v\]\s*mod\s*\(?\s*(.+?)\s*\)?", text) + if m: + num1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + num2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM1": num1_obj, "NUM2": num2_obj} + block_id = _register_block("operator_mod", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num1_obj.get("kind") == "block": all_generated_blocks[num1_obj["block"]]["parent"] = block_id + if num2_obj.get("kind") == "block": all_generated_blocks[num2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": num_obj} + block_id = _register_block("operator_round", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num_obj.get("kind") == "block": all_generated_blocks[num_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (() of ()) (operator_mathop) - handle variable for function type + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) # e.g. [sqrt v] of ((x pos) * (x pos)) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + # Also handle direct string for function type (e.g., "abs of (x)") + m = re.search(r"([a-zA-Z]+)\s*of\s*\((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + # This regex is designed to handle nested parentheses correctly. + # It looks for an opening parenthesis, then non-parenthesis characters or balanced parentheses, + # followed by an operator, and then the second operand. + # This is a simplified approach; a full-fledged parser would use a stack. + # arithmetic_match = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + arithmetic_match = re.search(r"\(?\s*(.+?)\s*\)?\s*([\+\-\*/])\s*\(?\s*(.+?)\s*\)?", text) + if not arithmetic_match: + # Try to match without outer parentheses for the operands, but still with an operator + arithmetic_match = re.search(r"(.+?)\s*([+\-*/])\s*(.+)", text) + + if arithmetic_match: + op1_str = arithmetic_match.group(1).strip() + operator_symbol = arithmetic_match.group(2).strip() + op2_str = arithmetic_match.group(3).strip() + + op1_obj = parse_reporter_or_value(op1_str, None, pick_key_func, all_generated_blocks) + op2_obj = parse_reporter_or_value(op2_str, None, pick_key_func, all_generated_blocks) + + opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + inputs = {"NUM1": op1_obj, "NUM2": op2_obj} + block_id = _register_block(opcode_map[operator_symbol], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if op1_obj.get("kind") == "block": all_generated_blocks[op1_obj["block"]]["parent"] = block_id + if op2_obj.get("kind") == "block": all_generated_blocks[op2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (costume ()) (looks_costumenumbername) - handle with or without 'v' + m = re.search(r"costume \((.+?)\)", text) + if not m: + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_costumenumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (backdrop ()) (looks_backdropnumbername) - handle with or without 'v' + m = re.search(r"backdrop \((.+?)\)", text) + if not m: + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_backdropnumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (distance to ()) (sensing_distanceto) - handle with or without 'v' + m = re.search(r"distance to \((.+?)\)", text) + if not m: + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + inputs = {"TARGET": [target_val, None]} # This is a direct value, not a block + block_id = _register_block("sensing_distanceto", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # (current ()) (sensing_current) - handle with or without 'v' + m = re.search(r"current \((.+?)\)", text) + if not m: + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + fields = {"CURRENTMENU": [unit.upper(), None]} + block_id = _register_block("sensing_current", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (() of ()) (sensing_of) - handle both variable and non-variable properties, and objects + # Updated regex to correctly capture the property and object, including nested reporters for property + m = re.search(r"\((.+?)\)\s*of\s*\((.+?)\)", text) # (prop) of (obj) + if not m: + m = re.search(r"\((.+?)\)\s*of\s*\[([^\]]+)\s*v\]", text) # (prop) of [obj v] + if m: + prop_str = m.group(1).strip() + obj = m.group(2).strip() + + # Map common property names to their internal Scratch representation + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + property_value = prop_map.get(prop_str, prop_str) # Use mapped value or original string + + # The object can be a sprite name or "_stage_" + obj_kind = "menu" + if obj.lower() == "stage": obj_val = "_stage_" + elif obj.lower() == "myself": obj_val = "_myself_" + else: obj_val = obj # Assume it's a sprite name + + # Create the menu block for OBJECT input + object_menu_id = _register_block("sensing_of_object_menu", parent_key, True, pick_key_func, all_generated_blocks, fields={"OBJECT": [obj_val, None]}) + + # inputs = {"OBJECT": [1, object_menu_id]} # Link to the menu block + # block_id = _register_block("sensing_of", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + + # # Update parent for the menu block + # all_generated_blocks[object_menu_id]["parent"] = block_id + + # return {"kind": "block", "block": block_id} + of_fields = {"OBJECT": [obj_val, None]} + inputs = {"OBJECT": [1, object_menu_id]} + block_id = _register_block("sensing_of", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs, + fields=of_fields) + all_generated_blocks[object_menu_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item (index) of [list v]) (data_itemoflist) - handle with or without 'v' and parentheses for index + m = re.search(r"item \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + inputs = {"INDEX": index_obj} + fields = {"LIST": [list_name, None]} + block_id = _register_block("data_itemoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item # of [item] in [list v]) (data_itemnumoflist) - handle with or without 'v' and parentheses for item + m = re.search(r"item # of \((.+?)\) in \((.+?)\)", text) + if not m: + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + inputs = {"ITEM": item_obj} + fields = {"LIST": [list_name, None]} + block_id = _register_block("data_itemnumoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if item_obj.get("kind") == "block": all_generated_blocks[item_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # inside parse_reporter_or_value + m = re.search(r"\(\s*(.+?)\s*\)\s*([\+\-\*/])\s*\(\s*(.+?)\s*\)", text) + if m: + left = parse_reporter_or_value(f"({m.group(1).strip()})", parent_key, pick_key_func, all_generated_blocks) + right = parse_reporter_or_value(f"({m.group(3).strip()})", parent_key, pick_key_func, all_generated_blocks) + op_map = {"+": "operator_add", "-": "operator_subtract", "*": "operator_multiply", "/": "operator_divide"} + opcode = op_map[m.group(2).strip()] + inputs = {"NUM1": left, "NUM2": right} + block_id = _register_block(opcode, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + for side in (left, right): + if side.get("kind") == "block": + all_generated_blocks[side["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + raise ValueError(f"Can't parse reporter or value: {text}") + +def parse_condition(stmt, parent_key, pick_key_func, all_generated_blocks): + """ + Parse Scratch-style boolean conditions, handling comparisons (<, =, >), + boolean operators (and, or, not), and other sensing conditions. + """ + s = stmt.strip() + s = extract_condition_balanced(s) + s = s.lower() + + print(f"the stmt was this {stmt} and parsed was this {s}") + # 1) Boolean NOT: `not <...>` + #m_not = re.fullmatch(r'not\s+<\s*(.+?)\s*>', s, re.IGNORECASE) + #m_not = re.fullmatch(r"\s*<\s*not\s+(.+?)\s*>\s*", s, re.IGNORECASE) + m_not = re.fullmatch(r"\s*(?:<\s*)?not\s+(.+?)(?:\s*>)?\s*",s, re.IGNORECASE) + if m_not: + inner = m_not.group(1).strip() + print(f"[2]the stmt was this {stmt} and parsed was this {s}") + inner_obj = parse_condition(inner, None, pick_key_func, all_generated_blocks) + bid = _register_block("operator_not", parent_key, False, pick_key_func, all_generated_blocks, + inputs={"OPERAND": inner_obj}) + if inner_obj.get("kind") == "block": + all_generated_blocks[inner_obj["block"]]["parent"] = bid + return {"kind": "block", "block": bid} + + # 2) Boolean AND / OR + #m_andor = re.fullmatch(r"<\s*(.+?)\s+(and|or)\s+(.+?)\s*>", s, re.IGNORECASE) + m_andor = re.fullmatch(r"\s*(.+?)\s+(and|or)\s+(.+?)\s*", s, re.IGNORECASE) + if m_andor: + cond1_obj = parse_condition(m_andor.group(1).strip(), None, pick_key_func, all_generated_blocks) + cond2_obj = parse_condition(m_andor.group(3).strip(), None, pick_key_func, all_generated_blocks) + op_block = 'operator_and' if m_andor.group(2).lower() == 'and' else 'operator_or' + print(f"The cond1: {cond1_obj} and the cond2: {cond2_obj} [for testing]") + inputs = {"OPERAND1": cond1_obj, "OPERAND2": cond2_obj} + block_id = _register_block(op_block, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if cond1_obj.get("kind") == "block": all_generated_blocks[cond1_obj["block"]]["parent"] = block_id + if cond2_obj.get("kind") == "block": all_generated_blocks[cond2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # 1a) Comparisons with explicit angle wrappers: < (...) op (...) > + m = re.fullmatch( + r"\s*<\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*>\s*", + s, + re.VERBOSE + ) + if m: + left_txt, right_txt = m.group(1), m.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), None, pick_key_func, all_generated_blocks) + operand2_obj = parse_reporter_or_value(unparen(right_txt), None, pick_key_func, all_generated_blocks) + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 1b) Simple comparisons without angle wrappers: A op B + m_simple = re.fullmatch(r"\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*", s) + if m_simple: + left_txt, right_txt = m_simple.group(1), m_simple.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), None, pick_key_func, all_generated_blocks) + operand2_obj = parse_reporter_or_value(unparen(right_txt), None, pick_key_func, all_generated_blocks) + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m_simple.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # # 2) Boolean NOT + # # m = re.fullmatch(r"\s*<\s*not\s+(.+?)\s*>\s*", s) + # # if m: + # # inner_obj = parse_condition(m.group(1), None, pick_key_func, all_generated_blocks) + # # inputs = {"OPERAND": inner_obj} + # # block_id = _register_block("operator_not", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # # if inner_obj.get("kind") == "block": all_generated_blocks[inner_obj["block"]]["parent"] = block_id + # # return {"kind": "block", "block": block_id} + # #m_not = re.fullmatch(r"not\s*?", s) + # m_not = re.fullmatch(r'not\s*<\s*(.+?)\s*>', s) + # if m_not: + # inner = m_not.group(1).strip() + # #logic for inside brackets + # # while inner.startswith("<") and inner.endswith(">"): + # # inner = inner[1:-1].strip() + # # # 4) Strip any stray unmatched '<' or '>' on the edges + # # while inner.startswith("<") and not inner.endswith(">"): + # # inner = inner[1:].strip() + # # while inner.endswith(">") and not inner.startswith("<"): + # # inner = inner[:-1].strip() + # inner_obj = parse_condition(inner, None, pick_key_func, all_generated_blocks) + # bid = _register_block("operator_not", parent_key, False, pick_key_func, all_generated_blocks, + # inputs={"OPERAND": inner_obj}) + # if inner_obj.get("kind") == "block": + # all_generated_blocks[inner_obj["block"]]["parent"] = bid + # return {"kind":"block","block":bid} + + # # 3) Boolean AND / OR + # m = re.fullmatch(r"\s*<\s*(.+?)\s+(and|or)\s+(.+?)\s*>\s*", s) + # if m: + # cond1_obj = parse_condition(m.group(1), None, pick_key_func, all_generated_blocks) + # cond2_obj = parse_condition(m.group(3), None, pick_key_func, all_generated_blocks) + # op_block = 'operator_and' if m.group(2) == 'and' else 'operator_or' + + # inputs = {"OPERAND1": cond1_obj, "OPERAND2": cond2_obj} + # block_id = _register_block(op_block, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # if cond1_obj.get("kind") == "block": all_generated_blocks[cond1_obj["block"]]["parent"] = block_id + # if cond2_obj.get("kind") == "block": all_generated_blocks[cond2_obj["block"]]["parent"] = block_id + # return {"kind": "block", "block": block_id} + + # 4) Contains: <[list v] contains [item]?> + #m = re.search(r"\[([^\]]+)\s*v\] contains \[(.+?)\]\?", s) + m = re.fullmatch(r"\s*\[(.+?)\]\s+contains\s+\[(.+?)\]\?\s*", s) + if m: + list_name = m.group(1).strip() + item_val = {"kind": "value", "value": m.group(2).strip()} # Item can be a value or a block + + # Create the data_list reporter block + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + + inputs = {"LIST": {"kind": "block", "block": list_block_id}, "ITEM": item_val} + block_id = _register_block("data_listcontainsitem", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 5) Touching object: + # m = re.search(r"touching \[([^\]]+)\s*v\]\?", s) + # if m: + # opt = m.group(1).strip() + # opt_val = {'mouse-pointer':'_mouse_','edge':'_edge_'}.get(opt, opt) + + # # Create the menu block for TOUCHINGOBJECTMENU input + # menu_block_id = _register_block("sensing_touchingobjectmenu", parent_key, True, pick_key_func, all_generated_blocks, fields={"TOUCHINGOBJECTMENU": [opt_val, None]}) + + # inputs = {"TOUCHINGOBJECTMENU": [1, menu_block_id]} # Link to the menu block + # block_id = _register_block("sensing_touchingobject", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # all_generated_blocks[menu_block_id]["parent"] = block_id + # return {"kind": "block", "block": block_id} + #m_touch = re.fullmatch(r"touching\s*\[\s*([^\]]+)\s*v\]\?", s) + #m_touch = re.fullmatch(r"touching\s*\[\s*([^\]]+)\s*v\]\??", s, re.IGNORECASE) + #m_touch = re.fullmatch(r"\s*(?:<\s*)?touching\s*\[\s*([^\]]+)\s*v\?\s*(?:\s*>)?\s*", s, re.IGNORECASE) + m_touch = re.fullmatch(r""" + \s* # leading space + (?:<\s*)? # optional '<' + touching # literal + \s*\[\s* + (?P[^\]]+?) # capture the sprite name + \s*v\]\? # close the [sprite v]? + (?:\s*>)? # optional '>' + \s* # trailing space + """, s, re.IGNORECASE | re.VERBOSE) + if m_touch: + sprite = m_touch.group('sprite').strip() + val = {'mouse-pointer':'_mouse_', 'edge':'_edge_'}.get(sprite, sprite) + + mid = _register_block( + "sensing_touchingobjectmenu", parent_key, True, pick_key_func, all_generated_blocks, + fields={"TOUCHINGOBJECTMENU":[val, None]} + ) + bid = _register_block( + "sensing_touchingobject", parent_key, False, pick_key_func, all_generated_blocks, + inputs={"TOUCHINGOBJECTMENU":[1, mid]} + ) + all_generated_blocks[mid]["parent"] = bid + return {"kind":"block","block":bid} + + # 6) Touching color: + m = re.search(r"touching color \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR": [1, [9, m.group(1)]]} # Color input is special, often a list [type, value] + block_id = _register_block("sensing_touchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # 7) Color is touching color: + m = re.search(r"color \[(#[0-9A-Fa-f]{6})\] is touching \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR1": [1, [9, m.group(1)]], "COLOR2": [1, [9, m.group(2)]]} + block_id = _register_block("sensing_coloristouchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # 8) Key pressed: + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", s) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", parent_key, True, pick_key_func, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) + + inputs = {"KEY_OPTION": [1, menu_block_id]} + block_id = _register_block("sensing_keypressed", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + all_generated_blocks[menu_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 9) Mouse down?: mouse down? + if s == "mouse down?": + block_id = _register_block("sensing_mousedown", parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + val_obj = parse_reporter_or_value(unparen(stmt), parent_key, pick_key_func, all_generated_blocks) + if val_obj: + return val_obj + + raise ValueError(f"Can't parse condition: {stmt}") + +def classify(line): + """ + Classifies a pseudo-code line into its corresponding Scratch opcode and block type. + Order of checks matters: more specific patterns should come before more general ones. + """ + l = line.lower().strip() + + # Ignore comments + if l.startswith("//"): return None, None + + # Hat Blocks (most specific first) + if re.match(r"when green flag click(ed)?", l): return "event_whenflagclicked", "hat" + if re.match(r"when (.+?) key press(ed)?", l): return "event_whenkeypressed", "hat" + if re.match(r"when this sprite click(ed)?", l): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if re.match(r"when i start as a clo(ne)?", l): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + if l.startswith("procedure "): return "procedures_definition", "hat" # For "procedure moveBall" + + # Motion Blocks + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + # IMPORTANT: More specific glide block before less specific one + if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if re.match(r"set x to\s*\(.+\)", l): return "motion_setx", "stack" # Specific for set x + if l.startswith("change y by"): return "motion_changeyby", "stack" + if re.match(r"set y to\s*\(.+\)", l): return "motion_sety", "stack" # Specific for set y + #if re.match(r"if on edge, bounce( off edge)?", l): return "motion_ifonedgebounce", "stack" + if re.match(r"if on edge,\s*bounc(e)?(\s+off\s+edge)?", l.strip(), re.IGNORECASE): return "motion_ifonedgebounce", "stack" + if re.match(r"bounce off edg(e)?", l): return "motion_ifonedgebounce", "stack" # Alias + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + + + # Looks Blocks + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if re.match(r"next costum(e)?", l): return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + # Updated regex for change/set effect by/to + if re.match(r"change\s*(\[.+?v\]|\(.+?\))?\s*effect by", l): return "looks_changeeffectby", "stack" + if re.match(r"set\s*(\[.+?v\]|\(.+?\))?\s*effect to", l): return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + # Sound Blocks + if re.match(r"play sound (.+?) until do(ne)?", l): return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + # Event Blocks (broadcasts) + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + # Control Blocks + if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + # Updated regex for stop block to handle different options + if re.match(r"stop \[(all|this script|other scripts in sprite)\s*v\]", l): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + # Data Blocks + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + # Updated regex for delete of list + if re.match(r"delete \((.+?)\) of \[([^\]]+)\s*v\]", l): return "data_deleteoflist", "stack" + if l.startswith("delete all of [" ): return "data_deletealloflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + # Sensing Blocks + if re.match(r"ask (.+?) and wai(t)?", l): return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom Blocks (procedures_call) - specific rule for "call" + if l.startswith("call "): + return "procedures_call", "stack" + + # Custom Blocks (procedures_call) - LAST RESORT (generic match) + # This should be the very last check for stack-type blocks to avoid conflicts. + # It tries to match anything that looks like a function call with or without arguments. + custom_block_match = re.match(r"([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", l) + if custom_block_match: + # Before returning, ensure it's not a known simple reporter or variable name + # that might have been missed or is being used standalone. + # This is a heuristic; a full parser would be more robust. + potential_name = custom_block_match.group(1).strip() + if potential_name not in ["x position", "y position", "direction", "mouse x", "mouse y", "loudness", "timer", "days since 2000", "username", "answer", "size", "volume"] and \ + not re.fullmatch(r"\[[^\]]+\]", potential_name) and \ + not re.fullmatch(r"\[[^\]]+\]\s*v", potential_name): + return "procedures_call", "stack" + + + raise ValueError(f"Unknown statement: {line!r}") + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key -> block_data (pre-generated block definitions) + • opcode_keys: dict of opcode -> list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... list of block dictionaries ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + all_generated_blocks = {} + for key, block_data in generated_input.items(): + all_generated_blocks[key] = copy.deepcopy(block_data) + # Ensure initial blocks have no next/parent/topLevel set from previous runs + all_generated_blocks[key]["next"] = None + all_generated_blocks[key]["parent"] = None + all_generated_blocks[key]["topLevel"] = False # Will be set to True for Hat blocks + + + # Stack stores (indent, parent_block_key, first_block_in_this_chain, last_block_in_this_chain) + # The last element of the stack is always the currently active scope. + # Sentinel: global scope (indent -1, no parent, no blocks yet in this "chain") + stack = [(-1, None, None, None)] + + top_level_script_keys = [] + + lines = pseudo_code.splitlines() + i = 0 + while i < len(lines): + raw_line = lines[i] + stripped_line = raw_line.strip() + + # Skip empty lines and comments + if not stripped_line or stripped_line.startswith("//"): + i += 1 + continue + + current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2 + + # Handle 'else' and 'end' keywords first, as they control scope + if stripped_line.lower() == "else": + # Pop the 'then' substack's scope + # This pop should always be safe as 'else' implies an 'if' scope exists + popped_indent, popped_parent_key, first_then_block, last_then_block = stack.pop() + if last_then_block: + all_generated_blocks[last_then_block]["next"] = None + + # Link the 'then' substack to its 'if-else' parent + if popped_parent_key and all_generated_blocks[popped_parent_key]["op_code"] == "control_if_else": + all_generated_blocks[popped_parent_key]["inputs"]["SUBSTACK"] = [2, first_then_block] if first_then_block else [2, None] + # Push a new scope for the 'else' substack, with the same parent + stack.append((current_indent, popped_parent_key, None, None)) + i += 1 + continue + + if stripped_line.lower() == "end": + # Pop the current substack's scope + # This pop should always be safe if pseudo-code is balanced + popped_indent, popped_parent_key, first_substack_block, last_substack_block = stack.pop() + if last_substack_block: + all_generated_blocks[last_substack_block]["next"] = None + + if popped_parent_key: + parent_block = all_generated_blocks[popped_parent_key] + # Determine which substack input to set (SUBSTACK or SUBSTACK2 for if-else) + if parent_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in parent_block["inputs"] and \ + parent_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in parent_block["inputs"]: + parent_block["inputs"]["SUBSTACK2"] = [2, first_substack_block] if first_substack_block else [2, None] + elif parent_block["block_shape"] == "C-Block" or parent_block["op_code"] == "procedures_definition": + parent_block["inputs"]["SUBSTACK"] = [2, first_substack_block] if first_substack_block else [2, None] + i += 1 + continue + + # Adjust stack based on indentation + # Pop scopes whose indentation is greater than or equal to the current line's indentation + # The `len(stack) > 1` prevents popping the sentinel. + while len(stack) > 1 and stack[-1][0] >= current_indent: + popped_indent, popped_parent_key, first_chain_block, last_chain_block = stack.pop() + if last_chain_block: + all_generated_blocks[last_chain_block]["next"] = None + # If this popped scope was a substack, ensure its parent links to it. + # This part is crucial for correctly linking chains after indentation changes. + if popped_parent_key: + parent_block = all_generated_blocks[popped_parent_key] + if parent_block["block_shape"] == "C-Block" or parent_block["op_code"] == "procedures_definition": + if parent_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in parent_block["inputs"] and \ + parent_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in parent_block["inputs"]: + parent_block["inputs"]["SUBSTACK2"] = [2, first_chain_block] if first_chain_block else [2, None] + else: + parent_block["inputs"]["SUBSTACK"] = [2, first_chain_block] if first_chain_block else [2, None] + + # Get the current active scope from the stack (guaranteed not empty due to sentinel) + current_scope_indent, current_parent_key, first_active_block_in_chain, last_active_block_in_chain = stack[-1] + + # Classify the statement + stmt_for_parse = stripped_line.rstrip("then").strip() + opcode, ntype = classify(stmt_for_parse) + + if opcode is None: + i += 1 + continue + + # Create the new block + key = pick_key(opcode) + info = copy.deepcopy(all_block_definitions[opcode]) + info["id"] = key + info["next"] = None # Default + + if "inputs" not in info or not isinstance(info["inputs"], dict): + info["inputs"] = {} + if "fields" not in info or not isinstance(info["fields"], dict): + info["fields"] = {} + + # Parse inputs and fields + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if opcode == "motion_movesteps": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["STEPS"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_turnright" or opcode == "motion_turnleft": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DEGREES"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_gotoxy": + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_glidesecstoxy": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt_for_parse, re.IGNORECASE) + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_secs: info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_pointindirection": + m = re.search(r"direction\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DIRECTION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_changexby", "motion_changeyby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DX" if opcode == "motion_changexby" else "DY"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_setx", "motion_sety"]: + m = re.search(r"(?:set x to|set y to)\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["X" if opcode == "motion_setx" else "Y"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_changesizeby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_setsizeto": + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SIZE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_changeeffectby", "sound_changeeffectby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE" if opcode == "looks_changeeffectby" else "VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE" if opcode == "looks_seteffectto" else "VOLUME"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["NUM"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "control_wait": + m = re.search(r"wait\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DURATION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "control_repeat": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["TIMES"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "data_changevariableby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "data_deleteoflist": + m = re.search(r"delete\s*\((.+?)\)\s*of", stmt_for_parse, re.IGNORECASE) + if m: + val_str = m.group(1).strip() + if val_str.isdigit(): + info["inputs"]["INDEX"] = {"kind": "value", "value": int(val_str)} + else: # "all", "last", "random" + info["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + elif opcode == "data_insertatlist": + m_item = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt_for_parse, re.IGNORECASE) + m_index = re.search(r"at\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + elif opcode == "data_replaceitemoflist": + m_index = re.search(r"replace item\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + m_item = re.search(r"with\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + elif opcode == "event_whengreaterthan": + m = re.search(r">\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + + # String inputs + elif opcode == "looks_sayforsecs": + m = re.search(r"say\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_say": + m = re.search(r"say\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = parse_reporter_or_value(m.group(1).strip(), key, pick_key, all_generated_blocks) + elif opcode == "looks_thinkforsecs": + m = re.search(r"think\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_think": + m = re.search(r"think\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "sensing_askandwait": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["QUESTION"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["ITEM"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_setvariableto": + m_var = re.search(r"set\s*\[([^\]]+)\s*v\]\s*to\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m_var: + var_name = m_var.group(1).strip() + value_str = m_var.group(2).strip() + info["fields"]["VARIABLE"] = [var_name, None] + info["inputs"]["VALUE"] = parse_reporter_or_value(value_str, key, pick_key, all_generated_blocks) + + # Dropdown/Menu inputs + elif opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_glideto": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m_secs: + info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + option = m_secs.group(2).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TOWARDS"] = {"kind": "menu", "option": option} + elif opcode == "sensing_keypressed": # For boolean block + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["KEY_OPTION"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "sensing_touchingobject": # For boolean block + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + elif option == "edge": option = "_edge_" + info["inputs"]["TOUCHINGOBJECTMENU"] = {"kind": "menu", "option": option} + elif opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "myself": option = "_myself_" + info["inputs"]["CLONE_OPTION"] = {"kind": "menu", "option": option} + elif opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SOUND_MENU"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["COSTUME"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait"]: + m = re.search(r"switch backdrop to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BACKDROP"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BROADCAST_INPUT"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "event_whenbroadcastreceived": + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info.setdefault("fields", {})["BROADCAST_OPTION"] = [m.group(1).strip(), None] + + # Conditional inputs (Boolean blocks) + elif opcode in ["control_if", "control_if_else", "control_wait_until", "control_repeat_until"]: + #cond_match = re.search(r"<(.*?)>", stmt_for_parse) + cond_match = extract_condition_balanced(stmt_for_parse) + print(f"[THE CONDA MATCH]------------->{cond_match}") + if cond_match: + # Pass pick_key to parse_condition + # info["inputs"]["CONDITION"] = parse_condition(cond_match.group(1).strip(), key, pick_key, all_generated_blocks) + info["inputs"]["CONDITION"] = parse_condition(cond_match.strip(), key, pick_key, all_generated_blocks) + elif opcode in ["operator_and", "operator_or", "operator_not", "operator_contains", + "sensing_touchingcolor", "sensing_coloristouchingcolor", "sensing_mousedown"]: + # These are handled by parse_condition directly, which returns the nested structure + # No need to re-parse inputs here, as they are part of the condition structure + pass # Inputs are set when parse_condition is called for the parent block + + + # Fields parsing + if "VARIABLE" in info["fields"]: + m = re.search(r"\[([^\]]+)\s*v\]", stmt_for_parse) + if m: + var_name = m.group(1).strip() + info["fields"]["VARIABLE"] = [var_name, None] + if "LIST" in info["fields"]: + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["LIST"] = [m.group(1), None] + if "STOP_OPTION" in info["fields"]: + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STOP_OPTION"] = [m.group(1), None] + if "STYLE" in info["fields"]: + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STYLE"] = [m.group(1), None] + if "DRAG_MODE" in info["fields"]: + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["DRAG_MODE"] = [m.group(1), None] + if "EFFECT" in info["fields"] and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["EFFECT"] = [m.group(1).upper(), None] + if "NUMBER_NAME" in info["fields"] and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["NUMBER_NAME"] = [m.group(1), None] + if "FRONT_BACK" in info["fields"] and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FRONT_BACK"] = [m.group(1), None] + if "FORWARD_BACKWARD" in info["fields"] and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + if "OPERATOR" in info["fields"] and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["OPERATOR"] = [m.group(1).upper(), None] + if "CURRENTMENU" in info["fields"] and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + if "PROPERTY" in info["fields"] and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt_for_parse, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + info["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + if "WHENGREATERTHANMENU" in info["fields"] and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + if "KEY_OPTION" in info["fields"] and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["KEY_OPTION"] = [m.group(1), None] + if "BACKDROP" in info["fields"] and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BACKDROP"] = [m.group(1), None] + if "BROADCAST_OPTION" in info["fields"] and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + # Custom block specific parsing + if opcode == "procedures_definition": + proc_def_match = re.match(r"(?:define|procedure)\s+([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))?", stmt_for_parse, re.IGNORECASE) + if proc_def_match: + proc_name = proc_def_match.group(1).strip() + args_str = proc_def_match.group(2) + info["procedure_name"] = proc_name + info["is_custom_definition"] = True + + # Create the special mutation block for the definition + mutation_block = { + "tagName": "mutation", + "children": [], + "proccode": proc_name, + "argumentids": [], + "argumentnames": [], + "argumentdefaults": [], + "warp": False # Assuming non-warp by default + } + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for arg in args: + arg_id = f"%s" # Scratch uses %s for string args, %n for number args + # For simplicity, we'll just use a generic ID for now, or match Scratch's pattern + # For the plan, we just need the names and order. + mutation_block["argumentids"].append(arg_id) + mutation_block["argumentnames"].append(arg) + mutation_block["argumentdefaults"].append("") + + info["mutation"] = mutation_block + + elif opcode == "procedures_call": + call_match = re.match(r"(?:call\s+)?([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", stmt_for_parse, re.IGNORECASE) + if call_match: + custom_block_name = call_match.group(1).strip() + args_str = call_match.group(2) + info["custom_block_name"] = custom_block_name + + info["mutation"] = { + "tagName": "mutation", + "children": [], + "proccode": custom_block_name, + "argumentids": [], + "argumentnames": [], + "warp": False + } + + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for idx, arg_val_str in enumerate(args): + arg_input_name = f"argument_name_{idx+1}" + info["mutation"]["argumentids"].append(arg_input_name) # Use the input name as argument ID + info["mutation"]["argumentnames"].append(f"arg{idx+1}") # Placeholder name for mutation + + info["inputs"][arg_input_name] = parse_reporter_or_value(arg_val_str, key, pick_key, all_generated_blocks) + + # Add the fully constructed block to the global collection + all_generated_blocks[key] = info + + # Update stack based on block type AFTER the block is created and linked + if ntype == "hat": + info["topLevel"] = True + info["parent"] = None # Hat blocks have no parent + top_level_script_keys.append(key) # Add to top-level scripts + + # This hat block *starts* a new chain. + # The next block will be its child. + # Push a new scope for the children of this hat block. + # The `first_active_block_in_chain` for this new scope will be set + # when the *first child* is encountered. + stack.append((current_indent, key, None, None)) # New scope for children + + elif ntype == "c_block" or opcode == "procedures_definition": + info["topLevel"] = False + info["parent"] = current_parent_key + + # This C-block is the 'next' of the previous block in the current chain + if last_active_block_in_chain: + all_generated_blocks[last_active_block_in_chain]["next"] = key + else: # This C-block is the first block in its chain (e.g., first block after a hat) + # If the parent is a hat block, this block is its 'next'. + if current_parent_key and all_generated_blocks[current_parent_key]["block_shape"] == "Hat Block": + all_generated_blocks[current_parent_key]["next"] = key + # Also update the first_active_block_in_chain for the current scope + stack[-1] = (current_scope_indent, current_parent_key, key, last_active_block_in_chain) + + + # Update the last_active_block_in_chain of the *current* scope to this C-block. + stack[-1] = (current_scope_indent, current_parent_key, first_active_block_in_chain if first_active_block_in_chain else key, key) + + # Now, push a new scope for the C-block's/define block's substack. + stack.append((current_indent, key, None, None)) # New scope for children of this C-block + + else: # Regular stack block or reporter/boolean block + info["topLevel"] = False + info["parent"] = current_parent_key + + # This block is the 'next' of the previous block in the current chain + if last_active_block_in_chain: + all_generated_blocks[last_active_block_in_chain]["next"] = key + else: # This block is the first block in its chain (e.g., first block after a hat) + # If the parent is a hat block, this block is its 'next'. + if current_parent_key and all_generated_blocks[current_parent_key]["block_shape"] == "Hat Block": + all_generated_blocks[current_parent_key]["next"] = key + # Also update the first_active_block_in_chain for the current scope + stack[-1] = (current_scope_indent, current_parent_key, key, last_active_block_in_chain) + + # Update the last block in the current chain. + stack[-1] = (current_scope_indent, current_parent_key, first_active_block_in_chain if first_active_block_in_chain else key, key) + + i += 1 # Move to the next line + + # Final pass to link substacks and ensure last blocks have next: None + # This loop now also handles setting the 'next' for hat blocks if they have children. + while len(stack) > 1: # Keep the initial sentinel + popped_indent, popped_parent_key, first_substack_block, last_substack_block = stack.pop() + if last_substack_block: + all_generated_blocks[last_substack_block]["next"] = None + + if popped_parent_key: + parent_block = all_generated_blocks[popped_parent_key] + # If the parent is a Hat block and this is its first child, set its 'next' + if parent_block["block_shape"] == "Hat Block" and parent_block["next"] is None: + parent_block["next"] = first_substack_block + + # Link the substack to its parent if it's a C-block or procedure definition + if parent_block["block_shape"] == "C-Block" or parent_block["op_code"] == "procedures_definition": + if parent_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in parent_block["inputs"] and \ + parent_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in parent_block["inputs"]: + parent_block["inputs"]["SUBSTACK2"] = [2, first_substack_block] if first_substack_block else [2, None] + else: + parent_block["inputs"]["SUBSTACK"] = [2, first_substack_block] if first_substack_block else [2, None] + + + print(f"[ALL OPCODE BLCOKS KEY 2]: {all_generated_blocks}") + with open("all_generated_blocks.json", "w") as f: + json.dump(all_generated_blocks, f, indent=2) + # Construct the final flow output based on the collected top-level keys + # Recursively build the block structure for each top-level script + def build_script_flow(current_block_key, visited=None): + if visited is None: + visited = set() + + script_flow = [] + current_iter_key = current_block_key + + while current_iter_key: + # Detect cyclic reference + if current_iter_key in visited: + script_flow.append({ + "block_key": current_iter_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected, stopping recursion" + }) + break + + visited.add(current_iter_key) + block = all_generated_blocks.get(current_iter_key) + if not block: + break # Should not happen if keys are correct + + output_block = { + "block_key": block["id"], + "opcode": block["op_code"], + "type": block["block_shape"].replace(" Block", "").lower().replace("c-", "c_"), + "inputs": {}, + "fields": {} + } + + # Handle all input types + for inp_name, inp_val in block.get("inputs", {}).items(): + if inp_name in ["SUBSTACK", "SUBSTACK2"]: + if inp_val and len(inp_val) > 1 and inp_val[1] in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(inp_val[1], visited.copy()) + else: + output_block["inputs"][inp_name] = [] + elif inp_name == "PROCCONTAINER" and block.get("is_custom_definition"): + output_block["inputs"][inp_name] = inp_val + elif isinstance(inp_val, dict) and inp_val.get("kind") == "block": + # Recursively build nested reporter/boolean blocks + nested_block_key = inp_val["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val # Keep original if not found (shouldn't happen) + elif isinstance(inp_val, dict) and inp_val.get("kind") == "nested_reporter": + nested_block_key = inp_val["reporter"]["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val + else: + output_block["inputs"][inp_name] = inp_val + + for field_name, field_val in block.get("fields", {}).items(): + output_block["fields"][field_name] = field_val + + if block.get("custom_block_name"): + output_block["custom_block_name"] = block["custom_block_name"] + + if block.get("procedure_name"): + output_block["procedure_name"] = block["procedure_name"] + output_block["is_custom_definition"] = True + if "mutation" in block: # Include mutation for custom definitions + output_block["mutation"] = block["mutation"] + + + script_flow.append(output_block) + + # Proceed to the next block in sequence + next_key = block.get("next") + if next_key in visited: + script_flow.append({ + "block_key": next_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected in 'next' pointer" + }) + break + + current_iter_key = next_key + + return script_flow + + final_flow_output = [] + for key in top_level_script_keys: + final_flow_output.extend(build_script_flow(key)) + return {"flow": final_flow_output} + +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_xposition","count":1}, + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":2}, + {"opcode":"control_stop","count":1}, + {"opcode":"operator_lt","count":1}, + {"opcode":"sensing_touchingobject","count":1}, + {"opcode":"sensing_touchingobjectmenu","count":1}, + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":2}, + {"opcode":"data_showvariable","count":2}, +] + +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) +with open("generated_output_json.json", "w") as f: + json.dump(generated_output_json, f, indent=2) +# Example pseudo-code inputs from the JSON files +pseudo_code_examples = [""" +when green flag clicked + go to x: (240) y: (-135) + set [score v] to (1) + set [speed v] to (1) + show variable [score v] + show variable [speed v] + forever + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end +end +""",] +txt="" +trace="" +# Process each example and print the plan +for i, pseudo_code_input in enumerate(pseudo_code_examples): + print(f"\n--- Processing Example {i+1} ---") + #print(pseudo_code_input.strip()) + try: + # Regenerate blocks and opcode_occurrences for each run to ensure fresh keys + # This is important because pick_key uses a defaultdict that persists state. + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) + #print(json.dumps(plan, indent=2)) + txt += str(plan) + " \n" + except Exception as e: + #print(f"Error processing example: {e}") + import traceback + #traceback.print_exc() + trace += str(e) +" "+str(pseudo_code_input)+" \n" + +with open("all_analysis.txt", "w", encoding="utf-8") as f: + f.write(txt) + +with open("all_analysis_trace.txt", "w", encoding="utf-8") as f: + f.write(trace) diff --git a/utils/plan_generator_7.py b/utils/plan_generator_7.py new file mode 100644 index 0000000000000000000000000000000000000000..dd42edff5125d9174c3a9eb813389f44c5496368 --- /dev/null +++ b/utils/plan_generator_7.py @@ -0,0 +1,2251 @@ +import json +import copy +import re +from collections import defaultdict + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False + + # Ensure inputs and fields are dictionaries, even if they were None or list in definition + if "inputs" not in main_block_data or not isinstance(main_block_data["inputs"], dict): + main_block_data["inputs"] = {} + if "fields" not in main_block_data or not isinstance(main_block_data["fields"], dict): + main_block_data["fields"] = {} + + generated_blocks[main_key] = main_block_data + + # Handle menus + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_key + + # Ensure inputs and fields are dictionaries for menu blocks too + if "inputs" not in menu_block_data or not isinstance(menu_block_data["inputs"], dict): + menu_block_data["inputs"] = {} + if "fields" not in menu_block_data or not isinstance(menu_block_data["fields"], dict): + menu_block_data["fields"] = {} + + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 1 and \ + main_block_data["inputs"][input_name][0] == 1: + + main_block_data["inputs"][input_name][1] = menu_key + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +# Consolidated block definitions from all JSON files +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_distanceto": { # Added sensing_distanceto + "block_name": "(distance to ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto", + "functionality": "Reports the distance from the sprite to the mouse-pointer or another specified sprite.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": False, "topLevel": True + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True + } +} + +def unparen(s): + s = s.strip() + # keep peeling off *all* matching outer parens + while True: + m = re.fullmatch(r"\((.*)\)", s) + if not m: + break + s = m.group(1).strip() + return s + +def _register_block(opcode, parent_key_for_shadow_or_input, is_shadow, pick_key_func, all_generated_blocks, inputs=None, fields=None): + """ + Helper to create and register a block in all_generated_blocks. + Returns the key of the newly created block. + """ + key = pick_key_func(opcode) # Use the passed pick_key_func + block_data = copy.deepcopy(all_block_definitions[opcode]) + block_data["id"] = key + + # Initialize inputs and fields if not provided to prevent KeyError later + if "inputs" not in block_data or not isinstance(block_data["inputs"], dict): + block_data["inputs"] = {} + if "fields" not in block_data or not isinstance(block_data["fields"], dict): + block_data["fields"] = {} + + if inputs: + block_data["inputs"].update(inputs) # Use update to merge, not overwrite + if fields: + block_data["fields"].update(fields) # Use update to merge, not overwrite + + block_data["shadow"] = is_shadow + + if is_shadow: + block_data["parent"] = parent_key_for_shadow_or_input + block_data["next"] = None # Shadow blocks don't have a 'next' in the main chain + block_data["topLevel"] = False # Shadow blocks are never top-level + else: + # For non-shadow blocks, parent, next, and topLevel will be set by generate_plan + block_data["parent"] = None + block_data["next"] = None + block_data["topLevel"] = False # Default, will be set to True for Hat blocks by generate_plan + + all_generated_blocks[key] = block_data + return key + +def _auto_balance(text): + # if there are more "(" than ")", append the missing ")" + diff = text.count("(") - text.count(")") + if diff > 0: + text = text + ")"*diff + # same for square brackets + diff = text.count("[") - text.count("]") + if diff > 0: + text = text + "]"*diff + return text + +def strip_outer_angle_brackets(text): + """ + Strip exactly one balanced pair of outer <...> brackets, only if they wrap the whole string. + """ + text = text.strip() + if text.startswith("<") and text.endswith(">"): + depth = 0 + for i, char in enumerate(text): + if char == '<': + depth += 1 + elif char == '>': + depth -= 1 + if depth == 0 and i == len(text) - 1: + return text[1:-1].strip() + # If we exit the loop and depth is 0, it means the outer brackets were balanced and wrapped the whole string + if depth == 0: + return text[1:-1].strip() + return text + +def extract_condition_balanced(stmt): + # 1. Remove "if" and "then" + stmt = stmt.strip() + if stmt.lower().startswith("if "): + stmt = stmt[3:].strip() + if stmt.lower().startswith("repeat until"): + stmt = stmt[12:].strip() + if stmt.lower().startswith("wait until "): + stmt = stmt[11:].strip() + if stmt.lower().endswith(" then"): + stmt = stmt[:-5].strip() + + # Helper to detect and strip single outer balanced angle brackets + def unwrap_balanced(s): + if s.startswith("<") and s.endswith(">"): + depth = 0 + for i in range(len(s)): + if s[i] == "<": + depth += 1 + elif s[i] == ">": + depth -= 1 + if depth == 0 and i < len(s) - 1: + return s # Early balance → not a single outer wrapper + if depth == 0: + return s[1:-1].strip() + return s + + # Recursively simplify things like > to not + def simplify(s): + s = unwrap_balanced(s) + s = s.strip() + + # Match > pattern + m = re.fullmatch(r"not\s*<(.+)>", s, re.IGNORECASE) + if m: + inner = m.group(1).strip() + inner = simplify(inner) + return f"not <{inner}>" + + # Match comparison operators like <(x position) < (100)> + # This part might be redundant if the main parser handles it, but good for internal consistency + m_comp = re.fullmatch(r"<\s*\(([^<>]+?)\)\s*([<>=])\s*\(([^<>]+?)\)\s*>", stmt) + if m_comp: + return f"({m_comp.group(1).strip()}) {m_comp.group(2)} ({m_comp.group(3).strip()})" + + return s + + return simplify(stmt) + +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, parent_key, pick_key_func, all_generated_blocks): + text = _auto_balance(text.strip()) + text = unparen(text.strip()) + # Check for numeric literal (including parenthesized numbers like "(0)" or "(10)") + m_num = re.fullmatch(r"\(?\s*(-?\d+(\.\d+)?)\s*\)?", text) + if m_num: + val_str = m_num.group(1) + return {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # # Check for string literal (e.g., "[Hello!]") + # if text.startswith('[') and text.endswith(']'): + # return {"kind": "value", "value": text[1:-1]} + # Variable reporter: [score v], [health v], etc. + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, + fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + + # Now catch other bracketed values as literal strings + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + # Check for simple reporters, potentially with outer parentheses + m_simple_reporter = re.fullmatch(r"\((.+?)\)", text) + if m_simple_reporter: + inner_text = m_simple_reporter.group(1).strip() + if inner_text in simple_reporters: + block_id = _register_block(simple_reporters[inner_text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + # Also check for simple reporters without parentheses (e.g., if passed directly) + if text in simple_reporters: + block_id = _register_block(simple_reporters[text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + + # Variable reporter: [score v] or (score) or just "score" + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + # Ensure it's not a simple reporter already handled, or a number + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [potential_var_name, None]}) + return {"kind": "block", "block": block_id} + # Handle plain variable names like "score", "number 1", "total score" + if re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_ ]*", text): # Allow spaces for "number 1" etc. + # Exclude known simple reporters that don't have 'v' or parentheses + if text not in simple_reporters: + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [text, None]}) + return {"kind": "block", "block": block_id} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + return {"kind": "block", "block": block_id} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + max_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + inputs = {"FROM": min_val_obj, "TO": max_val_obj} + block_id = _register_block("operator_random", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if min_val_obj.get("kind") == "block": all_generated_blocks[min_val_obj["block"]]["parent"] = block_id + if max_val_obj.get("kind") == "block": all_generated_blocks[max_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (join ()()) (operator_join) - handle both [] and () for inputs + + # m = re.search(r"join \((.+?)\) \((.+?)\)", text) # Try (val) (val) + # if not m: + # m = re.search(r"join \[(.+?)\] \[(.+?)\]", text) # Try [val] [val] + # if m: + # str1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + # str2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + # inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + # block_id = _register_block("operator_join", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # if str1_obj.get("kind") == "block": all_generated_blocks[str1_obj["block"]]["parent"] = block_id + # if str2_obj.get("kind") == "block": all_generated_blocks[str2_obj["block"]]["parent"] = block_id + # return {"kind": "block", "block": block_id} + + #m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s+(\[.+?\]|\(.+?\))", text) # Try (val) (val) + m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s*(\[.+?\]|\(.+?\))", text) + if m: + part1_txt = m.group(1).strip() + part2_txt = m.group(2).strip() + str1_obj = parse_reporter_or_value(part1_txt, None, pick_key_func, all_generated_blocks) + str2_obj = parse_reporter_or_value(part2_txt, None, pick_key_func, all_generated_blocks) + inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + block_id = _register_block("operator_join", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs) + # set parents if nested blocks + for obj in (str1_obj, str2_obj): + if obj.get("kind") == "block": + all_generated_blocks[obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # letter () of () (operator_letterof) - handle both [] and () for inputs + m = re.search(r"letter \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + string_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"LETTER": index_obj, "STRING": string_val_obj} + block_id = _register_block("operator_letterof", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + if string_val_obj.get("kind") == "block": all_generated_blocks[string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (length of ()) (operator_length) - handle both [] and () for inputs + #m = re.search(r"length of \((.+?)\)", text) + m = re.search(r"length of\s*(?:\((.+?)\)|\[(.+?)\])", text) + if not m: + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + arg_txt = (m.group(1) or m.group(2)).strip() + list_or_string_val_obj = parse_reporter_or_value(arg_txt, None, pick_key_func, all_generated_blocks) + #list_or_string_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"STRING": list_or_string_val_obj} + block_id = _register_block("operator_length", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if list_or_string_val_obj.get("kind") == "block": all_generated_blocks[list_or_string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (() mod ()) (operator_mod) + # m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + m = re.search(r"\[([^\]]+)\s*v\]\s*mod\s*\(?\s*(.+?)\s*\)?", text) + if m: + num1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + num2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM1": num1_obj, "NUM2": num2_obj} + block_id = _register_block("operator_mod", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num1_obj.get("kind") == "block": all_generated_blocks[num1_obj["block"]]["parent"] = block_id + if num2_obj.get("kind") == "block": all_generated_blocks[num2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": num_obj} + block_id = _register_block("operator_round", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num_obj.get("kind") == "block": all_generated_blocks[num_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (() of ()) (operator_mathop) - handle variable for function type + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) # e.g. [sqrt v] of ((x pos) * (x pos)) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + # Also handle direct string for function type (e.g., "abs of (x)") + m = re.search(r"([a-zA-Z]+)\s*of\s*\((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + # This regex is designed to handle nested parentheses correctly. + # It looks for an opening parenthesis, then non-parenthesis characters or balanced parentheses, + # followed by an operator, and then the second operand. + # This is a simplified approach; a full-fledged parser would use a stack. + # arithmetic_match = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + arithmetic_match = re.search(r"\(?\s*(.+?)\s*\)?\s*([\+\-\*/])\s*\(?\s*(.+?)\s*\)?", text) + if not arithmetic_match: + # Try to match without outer parentheses for the operands, but still with an operator + arithmetic_match = re.search(r"(.+?)\s*([+\-*/])\s*(.+)", text) + + if arithmetic_match: + op1_str = arithmetic_match.group(1).strip() + operator_symbol = arithmetic_match.group(2).strip() + op2_str = arithmetic_match.group(3).strip() + + op1_obj = parse_reporter_or_value(op1_str, None, pick_key_func, all_generated_blocks) + op2_obj = parse_reporter_or_value(op2_str, None, pick_key_func, all_generated_blocks) + + opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + inputs = {"NUM1": op1_obj, "NUM2": op2_obj} + block_id = _register_block(opcode_map[operator_symbol], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if op1_obj.get("kind") == "block": all_generated_blocks[op1_obj["block"]]["parent"] = block_id + if op2_obj.get("kind") == "block": all_generated_blocks[op2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (costume ()) (looks_costumenumbername) - handle with or without 'v' + m = re.search(r"costume \((.+?)\)", text) + if not m: + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_costumenumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (backdrop ()) (looks_backdropnumbername) - handle with or without 'v' + m = re.search(r"backdrop \((.+?)\)", text) + if not m: + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_backdropnumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (distance to ()) (sensing_distanceto) - handle with or without 'v' + m = re.search(r"distance to \((.+?)\)", text) + if not m: + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + inputs = {"TARGET": [target_val, None]} # This is a direct value, not a block + block_id = _register_block("sensing_distanceto", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # (current ()) (sensing_current) - handle with or without 'v' + m = re.search(r"current \((.+?)\)", text) + if not m: + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + fields = {"CURRENTMENU": [unit.upper(), None]} + block_id = _register_block("sensing_current", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (() of ()) (sensing_of) - handle both variable and non-variable properties, and objects + # Updated regex to correctly capture the property and object, including nested reporters for property + m = re.search(r"\((.+?)\)\s*of\s*\((.+?)\)", text) # (prop) of (obj) + if not m: + m = re.search(r"\((.+?)\)\s*of\s*\[([^\]]+)\s*v\]", text) # (prop) of [obj v] + if m: + prop_str = m.group(1).strip() + obj = m.group(2).strip() + + # Map common property names to their internal Scratch representation + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + property_value = prop_map.get(prop_str, prop_str) # Use mapped value or original string + + # The object can be a sprite name or "_stage_" + obj_kind = "menu" + if obj.lower() == "stage": obj_val = "_stage_" + elif obj.lower() == "myself": obj_val = "_myself_" + else: obj_val = obj # Assume it's a sprite name + + # Create the menu block for OBJECT input + object_menu_id = _register_block("sensing_of_object_menu", parent_key, True, pick_key_func, all_generated_blocks, fields={"OBJECT": [obj_val, None]}) + + # inputs = {"OBJECT": [1, object_menu_id]} # Link to the menu block + # block_id = _register_block("sensing_of", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + + # # Update parent for the menu block + # all_generated_blocks[object_menu_id]["parent"] = block_id + + # return {"kind": "block", "block": block_id} + of_fields = {"OBJECT": [obj_val, None]} + inputs = {"OBJECT": [1, object_menu_id]} + block_id = _register_block("sensing_of", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs, + fields=of_fields) + all_generated_blocks[object_menu_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item (index) of [list v]) (data_itemoflist) - handle with or without 'v' and parentheses for index + m = re.search(r"item \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + inputs = {"INDEX": index_obj} + fields = {"LIST": [list_name, None]} + block_id = _register_block("data_itemoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item # of [item] in [list v]) (data_itemnumoflist) - handle with or without 'v' and parentheses for item + m = re.search(r"item # of \((.+?)\) in \((.+?)\)", text) + if not m: + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + inputs = {"ITEM": item_obj} + fields = {"LIST": [list_name, None]} + block_id = _register_block("data_itemnumoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if item_obj.get("kind") == "block": all_generated_blocks[item_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # inside parse_reporter_or_value + m = re.search(r"\(\s*(.+?)\s*\)\s*([\+\-\*/])\s*\(\s*(.+?)\s*\)", text) + if m: + left = parse_reporter_or_value(f"({m.group(1).strip()})", parent_key, pick_key_func, all_generated_blocks) + right = parse_reporter_or_value(f"({m.group(3).strip()})", parent_key, pick_key_func, all_generated_blocks) + op_map = {"+": "operator_add", "-": "operator_subtract", "*": "operator_multiply", "/": "operator_divide"} + opcode = op_map[m.group(2).strip()] + inputs = {"NUM1": left, "NUM2": right} + block_id = _register_block(opcode, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + for side in (left, right): + if side.get("kind") == "block": + all_generated_blocks[side["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + raise ValueError(f"Can't parse reporter or value: {text}") + +def parse_condition(stmt, parent_key, pick_key_func, all_generated_blocks): + """ + Parse Scratch-style boolean conditions, handling comparisons (<, =, >), + boolean operators (and, or, not), and other sensing conditions. + """ + s = stmt.strip() + s = extract_condition_balanced(s) + s = s.lower() + + print(f"the stmt was this {stmt} and parsed was this {s}") + # 1) Boolean NOT: `not <...>` + #m_not = re.fullmatch(r'not\s*<\s*(.+?)\s*>', s, re.IGNORECASE) + #m_not = re.fullmatch(r"\s*<\s*not\s+(.+?)\s*>\s*", s, re.IGNORECASE) + m_not = re.fullmatch(r"\s*(?:<\s*)?not\s+(.+?)(?:\s*>)?\s*",s, re.IGNORECASE) + if m_not: + inner = m_not.group(1).strip() + print(f"[2]the stmt was this {stmt} and parsed was this {s}") + inner_obj = parse_condition(inner, parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + bid = _register_block("operator_not", parent_key, False, pick_key_func, all_generated_blocks, # Pass parent_key + inputs={"OPERAND": inner_obj}) + if inner_obj.get("kind") == "block": + all_generated_blocks[inner_obj["block"]]["parent"] = bid + return {"kind": "block", "block": bid} + + # 2) Boolean AND / OR + #m_andor = re.fullmatch(r"<\s*(.+?)\s+(and|or)\s+(.+?)\s*>", s, re.IGNORECASE) + m_andor = re.fullmatch(r"\s*(.+?)\s+(and|or)\s+(.+?)\s*", s, re.IGNORECASE) + if m_andor: + cond1_obj = parse_condition(m_andor.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + cond2_obj = parse_condition(m_andor.group(3).strip(), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_block = 'operator_and' if m_andor.group(2).lower() == 'and' else 'operator_or' + print(f"The cond1: {cond1_obj} and the cond2: {cond2_obj} [for testing]") + inputs = {"OPERAND1": cond1_obj, "OPERAND2": cond2_obj} + block_id = _register_block(op_block, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + if cond1_obj.get("kind") == "block": all_generated_blocks[cond1_obj["block"]]["parent"] = block_id + if cond2_obj.get("kind") == "block": all_generated_blocks[cond2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # 1a) Comparisons with explicit angle wrappers: < (...) op (...) > + m = re.fullmatch( + r"\s*<\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*>\s*", + s, + re.VERBOSE + ) + if m: + left_txt, right_txt = m.group(1), m.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + # Set parents for nested inputs + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 1b) Simple comparisons without angle wrappers: A op B + m_simple = re.fullmatch(r"\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*", s) + if m_simple: + left_txt, right_txt = m_simple.group(1), m_simple.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m_simple.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 4) Contains: <[list v] contains [item]?> + #m = re.search(r"\[([^\]]+)\s*v\] contains \[(.+?)\]\?", s) + m = re.fullmatch(r"\s*\[(.+?)\]\s+contains\s+\[(.+?)\]\?\s*", s) + if m: + list_name = m.group(1).strip() + item_val = {"kind": "value", "value": m.group(2).strip()} # Item can be a value or a block + + # Create the data_list reporter block + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) # Pass parent_key + + inputs = {"LIST": {"kind": "block", "block": list_block_id}, "ITEM": item_val} + block_id = _register_block("data_listcontainsitem", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 5) Touching object: + m_touch = re.fullmatch(r""" + \s* # leading space + (?:<\s*)? # optional '<' + touching # literal + \s*\[\s* + (?P[^\]]+?) # capture the sprite name + \s*v\]\? # close the [sprite v]? + (?:\s*>)? # optional '>' + """, s, re.IGNORECASE | re.VERBOSE) + if m_touch: + sprite = m_touch.group('sprite').strip() + val = {'mouse-pointer':'_mouse_', 'edge':'_edge_'}.get(sprite, sprite) + + mid = _register_block( + "sensing_touchingobjectmenu", parent_key, True, pick_key_func, all_generated_blocks, # Pass parent_key + fields={"TOUCHINGOBJECTMENU":[val, None]} + ) + bid = _register_block( + "sensing_touchingobject", parent_key, False, pick_key_func, all_generated_blocks, # Pass parent_key + inputs={"TOUCHINGOBJECTMENU":[1, mid]} + ) + all_generated_blocks[mid]["parent"] = bid + return {"kind":"block","block":bid} + + # 6) Touching color: + m = re.search(r"touching color \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR": [1, [9, m.group(1)]]} # Color input is special, often a list [type, value] + block_id = _register_block("sensing_touchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + return {"kind": "block", "block": block_id} + + # 7) Color is touching color: + m = re.search(r"color \[(#[0-9A-Fa-f]{6})\] is touching \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR1": [1, [9, m.group(1)]], "COLOR2": [1, [9, m.group(2)]]} + block_id = _register_block("sensing_coloristouchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + return {"kind": "block", "block": block_id} + + # 8) Key pressed: + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", s) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", parent_key, True, pick_key_func, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) # Pass parent_key + + inputs = {"KEY_OPTION": [1, menu_block_id]} + block_id = _register_block("sensing_keypressed", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + all_generated_blocks[menu_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 9) Mouse down?: mouse down? + if s == "mouse down?": + block_id = _register_block("sensing_mousedown", parent_key, False, pick_key_func, all_generated_blocks) # Pass parent_key + return {"kind": "block", "block": block_id} + + val_obj = parse_reporter_or_value(unparen(stmt), parent_key, pick_key_func, all_generated_blocks) + if val_obj: + return val_obj + + raise ValueError(f"Can't parse condition: {stmt}") + +def classify(line): + """ + Classifies a pseudo-code line into its corresponding Scratch opcode and block type. + Order of checks matters: more specific patterns should come before more general ones. + """ + l = line.lower().strip() + + # Ignore comments + if l.startswith("//"): return None, None + + # Hat Blocks (most specific first) + if re.match(r"when green flag click(ed)?", l): return "event_whenflagclicked", "hat" + if re.match(r"when (.+?) key press(ed)?", l): return "event_whenkeypressed", "hat" + if re.match(r"when this sprite click(ed)?", l): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if re.match(r"when i start as a clo(ne)?", l): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + if l.startswith("procedure "): return "procedures_definition", "hat" # For "procedure moveBall" + + # Motion Blocks + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + # IMPORTANT: More specific glide block before less specific one + if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if re.match(r"set x to\s*\(.+\)", l): return "motion_setx", "stack" # Specific for set x + if l.startswith("change y by"): return "motion_changeyby", "stack" + if re.match(r"set y to\s*\(.+\)", l): return "motion_sety", "stack" # Specific for set y + #if re.match(r"if on edge, bounce( off edge)?", l): return "motion_ifonedgebounce", "stack" + if re.match(r"if on edge,\s*bounc(e)?(\s+off\s+edge)?", l.strip(), re.IGNORECASE): return "motion_ifonedgebounce", "stack" + if re.match(r"bounce off edg(e)?", l): return "motion_ifonedgebounce", "stack" # Alias + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + + + # Looks Blocks + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if re.match(r"next costum(e)?", l): return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + # Updated regex for change/set effect by/to + if re.match(r"change\s*(\[.+?v\]|\(.+?\))?\s*effect by", l): return "looks_changeeffectby", "stack" + if re.match(r"set\s*(\[.+?v\]|\(.+?\))?\s*effect to", l): return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + # Sound Blocks + if re.match(r"play sound (.+?) until do(ne)?", l): return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + # Event Blocks (broadcasts) + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + # Control Blocks + if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + # Updated regex for stop block to handle different options + if re.match(r"stop \[(all|this script|other scripts in sprite)\s*v\]", l): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + # Data Blocks + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + # Updated regex for delete of list + if re.match(r"delete \((.+?)\) of \[([^\]]+)\s*v\]", l): return "data_deleteoflist", "stack" + if l.startswith("delete all of [" ): return "data_deletealloflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + # Sensing Blocks + if re.match(r"ask (.+?) and wai(t)?", l): return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom Blocks (procedures_call) - specific rule for "call" + if l.startswith("call "): + return "procedures_call", "stack" + + # Custom Blocks (procedures_call) - LAST RESORT (generic match) + # This should be the very last check for stack-type blocks to avoid conflicts. + # It tries to match anything that looks like a function call with or without arguments. + custom_block_match = re.match(r"([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", l) + if custom_block_match: + # Before returning, ensure it's not a known simple reporter or variable name + # that might have been missed or is being used standalone. + # This is a heuristic; a full parser would be more robust. + potential_name = custom_block_match.group(1).strip() + if potential_name not in ["x position", "y position", "direction", "mouse x", "mouse y", "loudness", "timer", "days since 2000", "username", "answer", "size", "volume"] and \ + not re.fullmatch(r"\[[^\]]+\]", potential_name) and \ + not re.fullmatch(r"\[[^\]]+\]\s*v", potential_name): + return "procedures_call", "stack" + + + raise ValueError(f"Unknown statement: {line!r}") + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key -> block_data (pre-generated block definitions) + • opcode_keys: dict of opcode -> list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... list of block dictionaries ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + all_generated_blocks = {} # Initialize as empty, blocks will be added as they are parsed + + # Stack stores (indent, owner_block_id, last_block_in_current_linear_chain_id) + # owner_block_id: The ID of the C-block or Hat block that owns the current substack. + # last_block_in_current_linear_chain_id: The ID of the last block added to the *current linear sequence* within this substack. + stack = [(-1, None, None)] # Sentinel: (indent, owner_block_id, last_block_in_current_linear_chain_id) + + top_level_script_keys = [] + + lines = pseudo_code.splitlines() + i = 0 + while i < len(lines): + raw_line = lines[i] + stripped_line = raw_line.strip() + + # Skip empty lines and comments + if not stripped_line or stripped_line.startswith("//"): + i += 1 + continue + + current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2 + + # Handle 'else' and 'end' first, as they control scope + if stripped_line.lower() == "else": + # Pop the 'then' substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # The 'if-else' block (popped_owner_key) is the owner. + # Push a new scope for the 'else' substack, with the same owner. + stack.append((current_indent, popped_owner_key, None)) # New scope for 'else' part, no last block yet + i += 1 + continue + + if stripped_line.lower() == "end": + # Pop the current substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # If the substack was empty, ensure the input is [2, None]. + if popped_owner_key: + owner_block = all_generated_blocks[popped_owner_key] + if owner_block["block_shape"] == "C-Block" or owner_block["op_code"] == "procedures_definition": + if owner_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in owner_block["inputs"] and \ + owner_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in owner_block["inputs"]: + # If SUBSTACK is already set, this 'end' closes the SUBSTACK2 (else part) + if not owner_block["inputs"].get("SUBSTACK2"): # Only set if not already set by a block + owner_block["inputs"]["SUBSTACK2"] = [2, None] + elif not owner_block["inputs"].get("SUBSTACK"): # Only set if not already set by a block + owner_block["inputs"]["SUBSTACK"] = [2, None] + + i += 1 + continue + + # Adjust stack based on indentation for regular blocks + # Pop scopes whose indentation is greater than or equal to the current line's indentation + while len(stack) > 1 and stack[-1][0] >= current_indent: + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None # Terminate the chain + + # Get the current active scope from the stack + current_scope_indent, current_owner_block_id, last_block_in_current_chain = stack[-1] + + # Classify the statement and create the block + stmt_for_parse = stripped_line.rstrip("then").strip() + opcode, ntype = classify(stmt_for_parse) + + if opcode is None: # Should not happen if classify is robust + i += 1 + continue + + # Create the new block (and register it in all_generated_blocks) + # _register_block now only sets parent for shadow/input blocks; main block parent/next/topLevel set here. + key = _register_block(opcode, None, False, pick_key, all_generated_blocks) + info = all_generated_blocks[key] + + # Set parent, next, and topLevel for the main script blocks + if ntype == "hat": + info["parent"] = None + info["topLevel"] = True + top_level_script_keys.append(key) + # Hat block's 'next' will be its first child, set when the first child is processed + info["next"] = None + # Push a new scope for the children of this hat block. + stack.append((current_indent, key, None)) # New scope: owner is this hat, no last block yet + else: # Stack block or C-block (that is part of a linear sequence) + if last_block_in_current_chain: + # This block's parent is the previous block in the chain + info["parent"] = last_block_in_current_chain + all_generated_blocks[last_block_in_current_chain]["next"] = key + else: + # This is the first block in a new linear chain (e.g., first block inside a forever loop) + # Its parent is the owner of the current scope (the C-block or Hat block) + info["parent"] = current_owner_block_id + + # If the owner is a C-block or procedure definition, link its SUBSTACK input + if current_owner_block_id and (all_generated_blocks[current_owner_block_id]["block_shape"] == "C-Block" or all_generated_blocks[current_owner_block_id]["op_code"] == "procedures_definition"): + owner_block = all_generated_blocks[current_owner_block_id] + if owner_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in owner_block["inputs"] and \ + owner_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in owner_block["inputs"]: + owner_block["inputs"]["SUBSTACK2"] = [2, key] + else: + owner_block["inputs"]["SUBSTACK"] = [2, key] + elif current_owner_block_id and all_generated_blocks[current_owner_block_id]["block_shape"] == "Hat Block": + # If the owner is a Hat block, this is its first child + all_generated_blocks[current_owner_block_id]["next"] = key + + info["topLevel"] = False + info["next"] = None # Default, will be overwritten if there's a next block + + # If it's a C-block or define block, it also starts a new inner scope + if ntype == "c_block" or opcode == "procedures_definition": + # Update the current scope's last_block_in_current_chain to this C-block + stack[-1] = (current_scope_indent, current_owner_block_id, key) + # Push a new scope for the C-block's substack + stack.append((current_indent, key, None)) # New scope: owner is this C-block, no last block yet + else: + # For regular stack blocks, just update the last_block_in_current_chain for the current scope + stack[-1] = (current_scope_indent, current_owner_block_id, key) + + # Parse inputs and fields (this part remains largely the same, but ensure parse_reporter_or_value/parse_condition + # are passed the *newly created block's ID* as the parent_key for nested inputs) + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if opcode == "motion_movesteps": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["STEPS"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_turnright" or opcode == "motion_turnleft": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DEGREES"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_gotoxy": + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_glidesecstoxy": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt_for_parse, re.IGNORECASE) + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_secs: info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_pointindirection": + m = re.search(r"direction\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DIRECTION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_changexby", "motion_changeyby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DX" if opcode == "motion_changexby" else "DY"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_setx", "motion_sety"]: + m = re.search(r"(?:set x to|set y to)\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["X" if opcode == "motion_setx" else "Y"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_changesizeby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_setsizeto": + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SIZE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_changeeffectby", "sound_changeeffectby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE" if opcode == "looks_changeeffectby" else "VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE" if opcode == "looks_seteffectto" else "VOLUME"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["NUM"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "control_wait": + m = re.search(r"wait\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DURATION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "control_repeat": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["TIMES"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "data_changevariableby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "data_deleteoflist": + m = re.search(r"delete\s*\((.+?)\)\s*of", stmt_for_parse, re.IGNORECASE) + if m: + val_str = m.group(1).strip() + if val_str.isdigit(): + info["inputs"]["INDEX"] = {"kind": "value", "value": int(val_str)} + else: # "all", "last", "random" + info["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + elif opcode == "data_insertatlist": + m_item = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt_for_parse, re.IGNORECASE) + m_index = re.search(r"at\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + elif opcode == "data_replaceitemoflist": + m_index = re.search(r"replace item\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + m_item = re.search(r"with\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + elif opcode == "event_whengreaterthan": + m = re.search(r">\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + + # String inputs + elif opcode == "looks_sayforsecs": + m = re.search(r"say\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_say": + m = re.search(r"say\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = parse_reporter_or_value(m.group(1).strip(), key, pick_key, all_generated_blocks) + elif opcode == "looks_thinkforsecs": + m = re.search(r"think\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_think": + m = re.search(r"think\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "sensing_askandwait": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["QUESTION"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["ITEM"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_setvariableto": + m_var = re.search(r"set\s*\[([^\]]+)\s*v\]\s*to\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m_var: + var_name = m_var.group(1).strip() + value_str = m_var.group(2).strip() + info["fields"]["VARIABLE"] = [var_name, None] + info["inputs"]["VALUE"] = parse_reporter_or_value(value_str, key, pick_key, all_generated_blocks) + + # Dropdown/Menu inputs + elif opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_glideto": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m_secs: + info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + option = m_secs.group(2).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TOWARDS"] = {"kind": "menu", "option": option} + elif opcode == "sensing_keypressed": # For boolean block + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["KEY_OPTION"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "sensing_touchingobject": # For boolean block + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + elif option == "edge": option = "_edge_" + info["inputs"]["TOUCHINGOBJECTMENU"] = {"kind": "menu", "option": option} + elif opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "myself": option = "_myself_" + info["inputs"]["CLONE_OPTION"] = {"kind": "menu", "option": option} + elif opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SOUND_MENU"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["COSTUME"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait"]: + m = re.search(r"switch backdrop to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BACKDROP"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BROADCAST_INPUT"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "event_whenbroadcastreceived": + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info.setdefault("fields", {})["BROADCAST_OPTION"] = [m.group(1).strip(), None] + + # Conditional inputs (Boolean blocks) + elif opcode in ["control_if", "control_if_else", "control_wait_until", "control_repeat_until"]: + #cond_match = re.search(r"<(.*?)>", stmt_for_parse) + cond_match = extract_condition_balanced(stmt_for_parse) + print(f"[THE CONDA MATCH]------------->{cond_match}") + if cond_match: + # Pass current block's key as parent for nested condition + info["inputs"]["CONDITION"] = parse_condition(cond_match.strip(), key, pick_key, all_generated_blocks) + elif opcode in ["operator_and", "operator_or", "operator_not", "operator_contains", + "sensing_touchingcolor", "sensing_coloristouchingcolor", "sensing_mousedown"]: + # These are handled by parse_condition directly, which returns the nested structure + # No need to re-parse inputs here, as they are part of the condition structure + pass # Inputs are set when parse_condition is called for the parent block + + + # Fields parsing + if "VARIABLE" in info["fields"]: + m = re.search(r"\[([^\]]+)\s*v\]", stmt_for_parse) + if m: + var_name = m.group(1).strip() + info["fields"]["VARIABLE"] = [var_name, None] + if "LIST" in info["fields"]: + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["LIST"] = [m.group(1), None] + if "STOP_OPTION" in info["fields"]: + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STOP_OPTION"] = [m.group(1), None] + if "STYLE" in info["fields"]: + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STYLE"] = [m.group(1), None] + if "DRAG_MODE" in info["fields"]: + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["DRAG_MODE"] = [m.group(1), None] + if "EFFECT" in info["fields"] and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["EFFECT"] = [m.group(1).upper(), None] + if "NUMBER_NAME" in info["fields"] and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["NUMBER_NAME"] = [m.group(1), None] + if "FRONT_BACK" in info["fields"] and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FRONT_BACK"] = [m.group(1), None] + if "FORWARD_BACKWARD" in info["fields"] and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + if "OPERATOR" in info["fields"] and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["OPERATOR"] = [m.group(1).upper(), None] + if "CURRENTMENU" in info["fields"] and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + if "PROPERTY" in info["fields"] and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt_for_parse, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + info["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + if "WHENGREATERTHANMENU" in info["fields"] and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + if "KEY_OPTION" in info["fields"] and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["KEY_OPTION"] = [m.group(1), None] + if "BACKDROP" in info["fields"] and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BACKDROP"] = [m.group(1), None] + if "BROADCAST_OPTION" in info["fields"] and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + # Custom block specific parsing + if opcode == "procedures_definition": + proc_def_match = re.match(r"(?:define|procedure)\s+([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))?", stmt_for_parse, re.IGNORECASE) + if proc_def_match: + proc_name = proc_def_match.group(1).strip() + args_str = proc_def_match.group(2) + info["procedure_name"] = proc_name + info["is_custom_definition"] = True + + # Create the special mutation block for the definition + mutation_block = { + "tagName": "mutation", + "children": [], + "proccode": proc_name, + "argumentids": [], + "argumentnames": [], + "argumentdefaults": [], + "warp": False # Assuming non-warp by default + } + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for arg in args: + arg_id = f"%s" # Scratch uses %s for string args, %n for number args + # For simplicity, we'll just use a generic ID for now, or match Scratch's pattern + # For the plan, we just need the names and order. + mutation_block["argumentids"].append(arg_id) + mutation_block["argumentnames"].append(arg) + mutation_block["argumentdefaults"].append("") + + info["mutation"] = mutation_block + + elif opcode == "procedures_call": + call_match = re.match(r"(?:call\s+)?([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", stmt_for_parse, re.IGNORECASE) + if call_match: + custom_block_name = call_match.group(1).strip() + args_str = call_match.group(2) + info["custom_block_name"] = custom_block_name + + info["mutation"] = { + "tagName": "mutation", + "children": [], + "proccode": custom_block_name, + "argumentids": [], + "argumentnames": [], + "warp": False + } + + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for idx, arg_val_str in enumerate(args): + arg_input_name = f"argument_name_{idx+1}" + info["mutation"]["argumentids"].append(arg_input_name) # Use the input name as argument ID + info["mutation"]["argumentnames"].append(f"arg{idx+1}") # Placeholder name for mutation + + info["inputs"][arg_input_name] = parse_reporter_or_value(arg_val_str, key, pick_key, all_generated_blocks) # Pass current block's key + + i += 1 # Move to the next line + + # Final pass to ensure last blocks have next: None (already handled by stack pops) + # The build_script_flow function will correctly traverse the linked list. + while len(stack) > 1: # Keep the initial sentinel + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + print(f"[ALL OPCODE BLCOKS KEY 2]: {all_generated_blocks}") + with open("all_generated_blocks.json", "w") as f: + json.dump(all_generated_blocks, f, indent=2) + # Construct the final flow output based on the collected top-level keys + # Recursively build the block structure for each top-level script + def build_script_flow(current_block_key, visited=None): + if visited is None: + visited = set() + + script_flow = [] + current_iter_key = current_block_key + + while current_iter_key: + # Detect cyclic reference + if current_iter_key in visited: + script_flow.append({ + "block_key": current_iter_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected, stopping recursion" + }) + break + + visited.add(current_iter_key) + block = all_generated_blocks.get(current_iter_key) + if not block: + break # Should not happen if keys are correct + + output_block = { + "block_key": block["id"], + "opcode": block["op_code"], + "type": block["block_shape"].replace(" Block", "").lower().replace("c-", "c_"), + "inputs": {}, + "fields": {} + } + + # Handle all input types + for inp_name, inp_val in block.get("inputs", {}).items(): + if inp_name in ["SUBSTACK", "SUBSTACK2"]: + if inp_val and len(inp_val) > 1 and inp_val[1] in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(inp_val[1], visited.copy()) + else: + output_block["inputs"][inp_name] = [] + elif inp_name == "PROCCONTAINER" and block.get("is_custom_definition"): + output_block["inputs"][inp_name] = inp_val + elif isinstance(inp_val, dict) and inp_val.get("kind") == "block": + # Recursively build nested reporter/boolean blocks + nested_block_key = inp_val["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val # Keep original if not found (shouldn't happen) + elif isinstance(inp_val, dict) and inp_val.get("kind") == "nested_reporter": + nested_block_key = inp_val["reporter"]["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val + else: + output_block["inputs"][inp_name] = inp_val + + for field_name, field_val in block.get("fields", {}).items(): + output_block["fields"][field_name] = field_val + + if block.get("custom_block_name"): + output_block["custom_block_name"] = block["custom_block_name"] + + if block.get("procedure_name"): + output_block["procedure_name"] = block["procedure_name"] + output_block["is_custom_definition"] = True + if "mutation" in block: # Include mutation for custom definitions + output_block["mutation"] = block["mutation"] + + + script_flow.append(output_block) + + # Proceed to the next block in sequence + next_key = block.get("next") + if next_key in visited: + script_flow.append({ + "block_key": next_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected in 'next' pointer" + }) + break + + current_iter_key = next_key + + return script_flow + + final_flow_output = [] + for key in top_level_script_keys: + final_flow_output.extend(build_script_flow(key)) + return {"flow": final_flow_output} + +# Example input with opcodes for the initial generation +# Example input with opcodes for the initial generation +# initial_opcode_counts = [ +# {"opcode":"event_whenflagclicked","count":1}, +# {"opcode":"motion_gotoxy","count":1}, +# {"opcode":"motion_glidesecstoxy","count":1}, +# {"opcode":"motion_xposition","count":3}, # Used multiple times in conditions +# {"opcode":"control_forever","count":1}, +# {"opcode":"control_if","count":2}, # Two if blocks +# {"opcode":"control_stop","count":2}, # stop all v, stop this script v +# {"opcode":"operator_lt","count":1}, # Used in condition +# {"opcode":"sensing_touchingobject","count":2}, # Used in condition, and for if on edge, bounce +# {"opcode":"sensing_touchingobjectmenu","count":2}, # Menu for touchingobject +# {"opcode":"event_broadcast","count":2}, # broadcast [Game Over v], broadcast [jump v] +# {"opcode":"data_setvariableto","count":3}, # set [score v] to (1), set [speed v] to (1), set [other sprite X v] to ( (x position) of [Sprite2 v] ) +# {"opcode":"data_showvariable","count":2}, # show variable [score v], show variable [speed v] +# {"opcode":"operator_add","count":2}, # For set [var] to ((var) + (val)), (number 1) + (number 2) +# {"opcode":"data_variable","count":5}, # For variable reporters like (score) or [score v] +# {"opcode":"looks_sayforsecs","count":2}, # For "say [Hello!] for (2) seconds", say [You win!] for (2) seconds +# {"opcode":"looks_say","count":2}, # For "say [Hello! v]", say [Welcome to my game! v] +# {"opcode":"motion_movesteps","count":3}, # For "move (10) steps" +# {"opcode":"control_wait","count":3}, # For "wait (0.1) seconds", wait (0.5) seconds, wait (1) seconds +# {"opcode":"motion_changeyby","count":2}, # For "change y by (10)" +# {"opcode":"motion_pointindirection","count":1}, # For "point in direction (90)" +# {"opcode":"event_whenkeypressed","count":3}, # For "when [space v] key pressed", when [up arrow v] key pressed, when [right arrow v] key pressed, when [left arrow v] key pressed +# {"opcode":"control_repeat","count":2}, # For "repeat (10)" +# {"opcode":"event_whenthisspriteclicked","count":2}, # For "when this sprite clicked" +# {"opcode":"looks_costumenumbername","count":1}, # For "(costume [name v])" +# {"opcode":"operator_join","count":3}, # For "join [Hello ] (answer)", join (length of [shopping list v]) [ items in the list.], join [Hello, ] (username) +# {"opcode":"sensing_answer","count":1}, # For "(answer)" +# {"opcode":"looks_hide","count":2}, # For "hide" +# {"opcode":"control_create_clone_of","count":2}, # For "create clone of [myself v]" +# {"opcode":"control_start_as_clone","count":2}, # For "when I start as a clone" +# {"opcode":"operator_random","count":1}, # For "pick random -240 to 240" +# {"opcode":"motion_ifonedgebounce","count":2}, # For "if on edge, bounce" +# {"opcode":"operator_gt","count":2}, # For "if <(score) > (10)> then", if <(item # of [Dog] in [myList v])> (0)> then +# {"opcode":"control_if_else","count":1}, # For "if <(score) > (10)> then else" +# {"opcode":"sound_play","count":2}, # Changed from sound_start to sound_play +# {"opcode":"sensing_loudness","count":2}, # For "(loudness)" +# {"opcode":"event_whengreaterthan","count":1}, # For "when [loudness v] > (70)" +# {"opcode":"control_repeat_until","count":1}, # For "repeat until " +# {"opcode":"looks_cleargraphiceffects","count":1}, # For "clear graphic effects" +# {"opcode":"looks_changeeffectby","count":2}, # For "change [color v] effect by (50)", change [fisheye v] effect by (5) +# {"opcode":"looks_seteffectto","count":1}, # For "set [ghost v] effect to (75)" +# {"opcode":"looks_setsizeto","count":1}, # For "set size to (50) %" +# {"opcode":"looks_changesizeby","count":1}, # For "change size by (5)" +# {"opcode":"looks_nextcostume","count":2}, # For "next costume" +# {"opcode":"looks_switchbackdroptowait","count":1}, # For "switch backdrop to [game over v] and wait" +# {"opcode":"looks_nextbackdrop","count":1}, # For "next backdrop" +# {"opcode":"sound_playuntildone","count":2}, # For "play sound [fanfare v] until done" +# {"opcode":"sound_stopallsounds","count":2}, # For "stop all sounds" +# {"opcode":"sound_changevolumeby","count":1}, # For "change volume by (-5)" +# {"opcode":"sound_setvolumeto","count":1}, # For "set volume to (50) %" +# {"opcode":"sensing_resettimer","count":2}, # For "reset timer" +# {"opcode":"sensing_setdragmode","count":2}, # For "set drag mode [not draggable v]" +# {"opcode":"data_addtolist","count":1}, # For "add [apple] to [shopping list v]" +# {"opcode":"data_deleteoflist","count":1}, # For "delete (all) of [my list v]" +# {"opcode":"data_insertatlist","count":1}, # For "insert [orange] at (2) of [fruits v]" +# {"opcode":"data_replaceitemoflist","count":1}, # For "replace item (1) of [colors v] with [blue]" +# {"opcode":"data_listcontainsitem","count":1}, # For "<[inventory v] contains [key]?>" +# {"opcode":"data_itemoflist","count":1}, # For "(item (2) of [myList v])" +# {"opcode":"data_lengthoflist","count":2}, # For "(length of [shopping list v])" +# {"opcode":"data_itemnumoflist","count":1}, # For "(item # of [Dog] in [myList v])" +# {"opcode":"sensing_touchingcolor","count":1}, # For "" +# {"opcode":"sensing_coloristouchingcolor","count":1}, # For "" +# {"opcode":"operator_and","count":1}, # For "< and >" +# {"opcode":"operator_or","count":1}, # For "< or >" +# {"opcode":"operator_not","count":1}, # For ">" +# {"opcode":"operator_contains","count":1}, # For "<[answer] contains [yes]?>" +# {"opcode":"procedures_call","count":1}, # For "jump (50)" +# {"opcode":"procedures_definition","count":1}, # For "define jump (height)" +# {"opcode":"sensing_of","count":1}, # For "(x position) of [Sprite2 v]" +# {"opcode":"sensing_current","count":1}, # For "(current [hour v])" +# {"opcode":"sensing_mousex","count":1}, # For "(mouse x)" +# {"opcode":"sensing_mousey","count":1}, # For "(mouse y)" +# {"opcode":"operator_subtract","count":1}, # For "((10) - (4))" +# {"opcode":"operator_multiply","count":1}, # For "(6) * (7)" +# {"opcode":"operator_divide","count":1}, # For "((20) / (5))" +# {"opcode":"data_list","count":1}, # For "[my list v]" +# {"opcode":"looks_gotofrontback","count":1}, # For "go to [front v] layer" +# {"opcode":"looks_goforwardbackwardlayers","count":1}, # For "go [forward v] (1) layers" +# {"opcode":"sound_sounds_menu","count":2}, # For sound menus +# {"opcode":"motion_goto_menu","count":1}, # For motion_goto menu +# {"opcode":"motion_glideto_menu","count":1}, # For motion_glideto menu +# {"opcode":"motion_pointtowards_menu","count":1}, # For motion_pointtowards menu +# {"opcode":"sensing_keyoptions","count":1}, # For sensing_keypressed menu +# {"opcode":"sensing_of_object_menu","count":1}, # For sensing_of menu +# {"opcode":"control_create_clone_of_menu","count":1}, # For control_create_clone_of menu +# {"opcode":"looks_costume","count":1}, # For looks_switchcostumeto menu +# {"opcode":"looks_backdrops","count":1}, # For looks_switchbackdropto menu +# ] +# # +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_xposition","count":1}, + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":2}, + {"opcode":"control_stop","count":1}, + {"opcode":"operator_lt","count":1}, + {"opcode":"sensing_touchingobject","count":1}, + {"opcode":"sensing_touchingobjectmenu","count":1}, + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":2}, + {"opcode":"data_showvariable","count":2}, +] + +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) +with open("generated_output_json.json", "w") as f: + json.dump(generated_output_json, f, indent=2) + +pseudo_code_examples = [""" +when green flag clicked + go to x: (240) y: (-135) + set [score v] to (1) + set [speed v] to (1) + show variable [score v] + show variable [speed v] + forever + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end +end +""",] +txt="" +trace="" +# Process each example and print the plan +for i, pseudo_code_input in enumerate(pseudo_code_examples): + print(f"\n--- Processing Example {i+1} ---") + #print(pseudo_code_input.strip()) + try: + # Regenerate blocks and opcode_occurrences for each run to ensure fresh keys + # This is important because pick_key uses a defaultdict that persists state. + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) + #print(json.dumps(plan, indent=2)) + txt += str(plan) + " \n" + except Exception as e: + #print(f"Error processing example: {e}") + import traceback + #traceback.print_exc() + trace += str(e) +" "+str(pseudo_code_input)+" \n" + +with open("all_analysis.txt", "w", encoding="utf-8") as f: + f.write(txt) + +with open("all_analysis_trace.txt", "w", encoding="utf-8") as f: + f.write(trace) diff --git a/utils/plan_generator_8.py b/utils/plan_generator_8.py new file mode 100644 index 0000000000000000000000000000000000000000..8203aacda2335e7e0f4ee620ccdc9392d699df67 --- /dev/null +++ b/utils/plan_generator_8.py @@ -0,0 +1,2166 @@ +import json +import copy +import re +from collections import defaultdict + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False + + # Ensure inputs and fields are dictionaries, even if they were None or list in definition + if "inputs" not in main_block_data or not isinstance(main_block_data["inputs"], dict): + main_block_data["inputs"] = {} + if "fields" not in main_block_data or not isinstance(main_block_data["fields"], dict): + main_block_data["fields"] = {} + + generated_blocks[main_key] = main_block_data + + # Handle menus + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_key + + # Ensure inputs and fields are dictionaries for menu blocks too + if "inputs" not in menu_block_data or not isinstance(menu_block_data["inputs"], dict): + menu_block_data["inputs"] = {} + if "fields" not in menu_block_data or not isinstance(menu_block_data["fields"], dict): + menu_block_data["fields"] = {} + + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 1 and \ + main_block_data["inputs"][input_name][0] == 1: + + main_block_data["inputs"][input_name][1] = menu_key + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +# Consolidated block definitions from all JSON files +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_distanceto": { # Added sensing_distanceto + "block_name": "(distance to ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto", + "functionality": "Reports the distance from the sprite to the mouse-pointer or another specified sprite.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": False, "topLevel": True + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True + } +} + +def unparen(s): + s = s.strip() + # keep peeling off *all* matching outer parens + while True: + m = re.fullmatch(r"\((.*)\)", s) + if not m: + break + s = m.group(1).strip() + return s + +def _register_block(opcode, parent_key_for_shadow_or_input, is_shadow, pick_key_func, all_generated_blocks, inputs=None, fields=None): + """ + Helper to create and register a block in all_generated_blocks. + Returns the key of the newly created block. + """ + key = pick_key_func(opcode) # Use the passed pick_key_func + block_data = copy.deepcopy(all_block_definitions[opcode]) + block_data["id"] = key + + # Initialize inputs and fields if not provided to prevent KeyError later + if "inputs" not in block_data or not isinstance(block_data["inputs"], dict): + block_data["inputs"] = {} + if "fields" not in block_data or not isinstance(block_data["fields"], dict): + block_data["fields"] = {} + + if inputs: + block_data["inputs"].update(inputs) # Use update to merge, not overwrite + if fields: + block_data["fields"].update(fields) # Use update to merge, not overwrite + + block_data["shadow"] = is_shadow + + if is_shadow: + block_data["parent"] = parent_key_for_shadow_or_input + block_data["next"] = None # Shadow blocks don't have a 'next' in the main chain + block_data["topLevel"] = False # Shadow blocks are never top-level + else: + # For non-shadow blocks: + # If it's a Reporter or Boolean block, its parent is the block it's an input to. + # Otherwise (Stack, C-Block, Cap, Hat), its parent/next will be set by generate_plan's sequential logic. + if all_block_definitions[opcode]["block_shape"] in ["Reporter Block", "Boolean Block"]: + block_data["parent"] = parent_key_for_shadow_or_input # This is the key of the block it's an input to + block_data["next"] = None # Reporters/Booleans never have a 'next' in the main chain + block_data["topLevel"] = False # Reporters/Booleans are never top-level + else: + # For Stack, C-Block, Cap, Hat blocks, parent/next/topLevel are set by generate_plan + block_data["parent"] = None + block_data["next"] = None + block_data["topLevel"] = False # Default, will be set to True for Hat blocks by generate_plan + + all_generated_blocks[key] = block_data + return key + +def _auto_balance(text): + # if there are more "(" than ")", append the missing ")" + diff = text.count("(") - text.count(")") + if diff > 0: + text = text + ")"*diff + # same for square brackets + diff = text.count("[") - text.count("]") + if diff > 0: + text = text + "]"*diff + return text + +def strip_outer_angle_brackets(text): + """ + Strip exactly one balanced pair of outer <...> brackets, only if they wrap the whole string. + """ + text = text.strip() + if text.startswith("<") and text.endswith(">"): + depth = 0 + for i, char in enumerate(text): + if char == '<': + depth += 1 + elif char == '>': + depth -= 1 + if depth == 0 and i == len(text) - 1: + return text[1:-1].strip() + # If we exit the loop and depth is 0, it means the outer brackets were balanced and wrapped the whole string + if depth == 0: + return text[1:-1].strip() + return text + +def extract_condition_balanced(stmt): + # 1. Remove "if" and "then" + stmt = stmt.strip() + if stmt.lower().startswith("if "): + stmt = stmt[3:].strip() + if stmt.lower().startswith("repeat until"): + stmt = stmt[12:].strip() + if stmt.lower().startswith("wait until "): + stmt = stmt[11:].strip() + if stmt.lower().endswith(" then"): + stmt = stmt[:-5].strip() + + # Helper to detect and strip single outer balanced angle brackets + def unwrap_balanced(s): + if s.startswith("<") and s.endswith(">"): + depth = 0 + for i in range(len(s)): + if s[i] == "<": + depth += 1 + elif s[i] == ">": + depth -= 1 + if depth == 0 and i < len(s) - 1: + return s # Early balance → not a single outer wrapper + if depth == 0: + return s[1:-1].strip() + return s + + # Recursively simplify things like > to not + def simplify(s): + s = unwrap_balanced(s) + s = s.strip() + + # Match > pattern + m = re.fullmatch(r"not\s*<(.+)>", s, re.IGNORECASE) + if m: + inner = m.group(1).strip() + inner = simplify(inner) + return f"not <{inner}>" + + # Match comparison operators like <(x position) < (100)> + # This part might be redundant if the main parser handles it, but good for internal consistency + m_comp = re.fullmatch(r"<\s*\(([^<>]+?)\)\s*([<>=])\s*\(([^<>]+?)\)\s*>", stmt) + if m_comp: + return f"({m_comp.group(1).strip()}) {m_comp.group(2)} ({m_comp.group(3).strip()})" + + return s + + return simplify(stmt) + +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, parent_key, pick_key_func, all_generated_blocks): + text = _auto_balance(text.strip()) + text = unparen(text.strip()) + # Check for numeric literal (including parenthesized numbers like "(0)" or "(10)") + m_num = re.fullmatch(r"\(?\s*(-?\d+(\.\d+)?)\s*\)?", text) + if m_num: + val_str = m_num.group(1) + return {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # # Check for string literal (e.g., "[Hello!]") + # if text.startswith('[') and text.endswith(']'): + # return {"kind": "value", "value": text[1:-1]} + # Variable reporter: [score v], [health v], etc. + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, + fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + + # Now catch other bracketed values as literal strings + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + # Check for simple reporters, potentially with outer parentheses + m_simple_reporter = re.fullmatch(r"\((.+?)\)", text) + if m_simple_reporter: + inner_text = m_simple_reporter.group(1).strip() + if inner_text in simple_reporters: + block_id = _register_block(simple_reporters[inner_text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + # Also check for simple reporters without parentheses (e.g., if passed directly) + if text in simple_reporters: + block_id = _register_block(simple_reporters[text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + + # Variable reporter: [score v] or (score) or just "score" + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + # Ensure it's not a simple reporter already handled, or a number + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [potential_var_name, None]}) + return {"kind": "block", "block": block_id} + # Handle plain variable names like "score", "number 1", "total score" + if re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_ ]*", text): # Allow spaces for "number 1" etc. + # Exclude known simple reporters that don't have 'v' or parentheses + if text not in simple_reporters: + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [text, None]}) + return {"kind": "block", "block": block_id} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + return {"kind": "block", "block": block_id} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + max_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + inputs = {"FROM": min_val_obj, "TO": max_val_obj} + block_id = _register_block("operator_random", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if min_val_obj.get("kind") == "block": all_generated_blocks[min_val_obj["block"]]["parent"] = block_id + if max_val_obj.get("kind") == "block": all_generated_blocks[max_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (join ()()) (operator_join) - handle both [] and () for inputs + + # m = re.search(r"join \((.+?)\) \((.+?)\)", text) # Try (val) (val) + # if not m: + # m = re.search(r"join \[(.+?)\] \[(.+?)\]", text) # Try [val] [val] + # if m: + # str1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + # str2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + # inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + # block_id = _register_block("operator_join", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # if str1_obj.get("kind") == "block": all_generated_blocks[str1_obj["block"]]["parent"] = block_id + # if str2_obj.get("kind") == "block": all_generated_blocks[str2_obj["block"]]["parent"] = block_id + # return {"kind": "block", "block": block_id} + + #m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s+(\[.+?\]|\(.+?\))", text) # Try (val) (val) + m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s*(\[.+?\]|\(.+?\))", text) + if m: + part1_txt = m.group(1).strip() + part2_txt = m.group(2).strip() + str1_obj = parse_reporter_or_value(part1_txt, None, pick_key_func, all_generated_blocks) + str2_obj = parse_reporter_or_value(part2_txt, None, pick_key_func, all_generated_blocks) + inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + block_id = _register_block("operator_join", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs) + # set parents if nested blocks + for obj in (str1_obj, str2_obj): + if obj.get("kind") == "block": + all_generated_blocks[obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # letter () of () (operator_letterof) - handle both [] and () for inputs + m = re.search(r"letter \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + string_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"LETTER": index_obj, "STRING": string_val_obj} + block_id = _register_block("operator_letterof", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + if string_val_obj.get("kind") == "block": all_generated_blocks[string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (length of ()) (operator_length) - handle both [] and () for inputs + #m = re.search(r"length of \((.+?)\)", text) + m = re.search(r"length of\s*(?:\((.+?)\)|\[(.+?)\])", text) + if not m: + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + arg_txt = (m.group(1) or m.group(2)).strip() + list_or_string_val_obj = parse_reporter_or_value(arg_txt, None, pick_key_func, all_generated_blocks) + #list_or_string_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"STRING": list_or_string_val_obj} + block_id = _register_block("operator_length", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if list_or_string_val_obj.get("kind") == "block": all_generated_blocks[list_or_string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (() mod ()) (operator_mod) + # m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + m = re.search(r"\[([^\]]+)\s*v\]\s*mod\s*\(?\s*(.+?)\s*\)?", text) + if m: + num1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + num2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM1": num1_obj, "NUM2": num2_obj} + block_id = _register_block("operator_mod", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num1_obj.get("kind") == "block": all_generated_blocks[num1_obj["block"]]["parent"] = block_id + if num2_obj.get("kind") == "block": all_generated_blocks[num2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": num_obj} + block_id = _register_block("operator_round", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num_obj.get("kind") == "block": all_generated_blocks[num_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (() of ()) (operator_mathop) - handle variable for function type + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) # e.g. [sqrt v] of ((x pos) * (x pos)) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + # Also handle direct string for function type (e.g., "abs of (x)") + m = re.search(r"([a-zA-Z]+)\s*of\s*\((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + # This regex is designed to handle nested parentheses correctly. + # It looks for an opening parenthesis, then non-parenthesis characters or balanced parentheses, + # followed by an operator, and then the second operand. + # This is a simplified approach; a full-fledged parser would use a stack. + # arithmetic_match = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + arithmetic_match = re.search(r"\(?\s*(.+?)\s*\)?\s*([\+\-\*/])\s*\(?\s*(.+?)\s*\)?", text) + if not arithmetic_match: + # Try to match without outer parentheses for the operands, but still with an operator + arithmetic_match = re.search(r"(.+?)\s*([+\-*/])\s*(.+)", text) + + if arithmetic_match: + op1_str = arithmetic_match.group(1).strip() + operator_symbol = arithmetic_match.group(2).strip() + op2_str = arithmetic_match.group(3).strip() + + op1_obj = parse_reporter_or_value(op1_str, None, pick_key_func, all_generated_blocks) + op2_obj = parse_reporter_or_value(op2_str, None, pick_key_func, all_generated_blocks) + + opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + inputs = {"NUM1": op1_obj, "NUM2": op2_obj} + block_id = _register_block(opcode_map[operator_symbol], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if op1_obj.get("kind") == "block": all_generated_blocks[op1_obj["block"]]["parent"] = block_id + if op2_obj.get("kind") == "block": all_generated_blocks[op2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (costume ()) (looks_costumenumbername) - handle with or without 'v' + m = re.search(r"costume \((.+?)\)", text) + if not m: + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_costumenumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (backdrop ()) (looks_backdropnumbername) - handle with or without 'v' + m = re.search(r"backdrop \((.+?)\)", text) + if not m: + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_backdropnumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (distance to ()) (sensing_distanceto) - handle with or without 'v' + m = re.search(r"distance to \((.+?)\)", text) + if not m: + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + inputs = {"TARGET": [target_val, None]} # This is a direct value, not a block + block_id = _register_block("sensing_distanceto", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # (current ()) (sensing_current) - handle with or without 'v' + m = re.search(r"current \((.+?)\)", text) + if not m: + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + fields = {"CURRENTMENU": [unit.upper(), None]} + block_id = _register_block("sensing_current", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (() of ()) (sensing_of) - handle both variable and non-variable properties, and objects + # Updated regex to correctly capture the property and object, including nested reporters for property + m = re.search(r"\((.+?)\)\s*of\s*\((.+?)\)", text) # (prop) of (obj) + if not m: + m = re.search(r"\((.+?)\)\s*of\s*\[([^\]]+)\s*v\]", text) # (prop) of [obj v] + if m: + prop_str = m.group(1).strip() + obj = m.group(2).strip() + + # Map common property names to their internal Scratch representation + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + property_value = prop_map.get(prop_str, prop_str) # Use mapped value or original string + + # The object can be a sprite name or "_stage_" + obj_kind = "menu" + if obj.lower() == "stage": obj_val = "_stage_" + elif obj.lower() == "myself": obj_val = "_myself_" + else: obj_val = obj # Assume it's a sprite name + + # Create the menu block for OBJECT input + object_menu_id = _register_block("sensing_of_object_menu", parent_key, True, pick_key_func, all_generated_blocks, fields={"OBJECT": [obj_val, None]}) + + # inputs = {"OBJECT": [1, object_menu_id]} # Link to the menu block + # block_id = _register_block("sensing_of", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + + # # Update parent for the menu block + # all_generated_blocks[object_menu_id]["parent"] = block_id + + # return {"kind": "block", "block": block_id} + of_fields = {"OBJECT": [obj_val, None]} + inputs = {"OBJECT": [1, object_menu_id]} + block_id = _register_block("sensing_of", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs, + fields=of_fields) + all_generated_blocks[object_menu_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item (index) of [list v]) (data_itemoflist) - handle with or without 'v' and parentheses for index + m = re.search(r"item \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + inputs = {"INDEX": index_obj} + fields = {"LIST": [list_name, None]} + block_id = _register_block("data_itemoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item # of [item] in [list v]) (data_itemnumoflist) - handle with or without 'v' and parentheses for item + m = re.search(r"item # of \((.+?)\) in \((.+?)\)", text) + if not m: + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + inputs = {"ITEM": item_obj} + fields = {"LIST": [list_name, None]} + block_id = _register_block("data_itemnumoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if item_obj.get("kind") == "block": all_generated_blocks[item_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # inside parse_reporter_or_value + m = re.search(r"\(\s*(.+?)\s*\)\s*([\+\-\*/])\s*\(\s*(.+?)\s*\)", text) + if m: + left = parse_reporter_or_value(f"({m.group(1).strip()})", parent_key, pick_key_func, all_generated_blocks) + right = parse_reporter_or_value(f"({m.group(3).strip()})", parent_key, pick_key_func, all_generated_blocks) + op_map = {"+": "operator_add", "-": "operator_subtract", "*": "operator_multiply", "/": "operator_divide"} + opcode = op_map[m.group(2).strip()] + inputs = {"NUM1": left, "NUM2": right} + block_id = _register_block(opcode, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + for side in (left, right): + if side.get("kind") == "block": + all_generated_blocks[side["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + raise ValueError(f"Can't parse reporter or value: {text}") + +def parse_condition(stmt, parent_key, pick_key_func, all_generated_blocks): + """ + Parse Scratch-style boolean conditions, handling comparisons (<, =, >), + boolean operators (and, or, not), and other sensing conditions. + """ + s = stmt.strip() + s = extract_condition_balanced(s) + s = s.lower() + + print(f"the stmt was this {stmt} and parsed was this {s}") + # 1) Boolean NOT: `not <...>` + #m_not = re.fullmatch(r'not\s*<\s*(.+?)\s*>', s, re.IGNORECASE) + #m_not = re.fullmatch(r"\s*<\s*not\s+(.+?)\s*>\s*", s, re.IGNORECASE) + m_not = re.fullmatch(r"\s*(?:<\s*)?not\s+(.+?)(?:\s*>)?\s*",s, re.IGNORECASE) + if m_not: + inner = m_not.group(1).strip() + print(f"[2]the stmt was this {stmt} and parsed was this {s}") + inner_obj = parse_condition(inner, parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + bid = _register_block("operator_not", parent_key, False, pick_key_func, all_generated_blocks, # Pass parent_key + inputs={"OPERAND": inner_obj}) + if inner_obj.get("kind") == "block": + all_generated_blocks[inner_obj["block"]]["parent"] = bid + return {"kind": "block", "block": bid} + + # 2) Boolean AND / OR + #m_andor = re.fullmatch(r"<\s*(.+?)\s+(and|or)\s+(.+?)\s*>", s, re.IGNORECASE) + m_andor = re.fullmatch(r"\s*(.+?)\s+(and|or)\s+(.+?)\s*", s, re.IGNORECASE) + if m_andor: + cond1_obj = parse_condition(m_andor.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + cond2_obj = parse_condition(m_andor.group(3).strip(), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_block = 'operator_and' if m_andor.group(2).lower() == 'and' else 'operator_or' + print(f"The cond1: {cond1_obj} and the cond2: {cond2_obj} [for testing]") + inputs = {"OPERAND1": cond1_obj, "OPERAND2": cond2_obj} + block_id = _register_block(op_block, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + if cond1_obj.get("kind") == "block": all_generated_blocks[cond1_obj["block"]]["parent"] = block_id + if cond2_obj.get("kind") == "block": all_generated_blocks[cond2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # 1a) Comparisons with explicit angle wrappers: < (...) op (...) > + m = re.fullmatch( + r"\s*<\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*>\s*", + s, + re.VERBOSE + ) + if m: + left_txt, right_txt = m.group(1), m.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + # Set parents for nested inputs + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 1b) Simple comparisons without angle wrappers: A op B + m_simple = re.fullmatch(r"\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*", s) + if m_simple: + left_txt, right_txt = m_simple.group(1), m_simple.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m_simple.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 4) Contains: <[list v] contains [item]?> + #m = re.search(r"\[([^\]]+)\s*v\] contains \[(.+?)\]\?", s) + m = re.fullmatch(r"\s*\[(.+?)\]\s+contains\s+\[(.+?)\]\?\s*", s) + if m: + list_name = m.group(1).strip() + item_val = {"kind": "value", "value": m.group(2).strip()} # Item can be a value or a block + + # Create the data_list reporter block + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) # Pass parent_key + + inputs = {"LIST": {"kind": "block", "block": list_block_id}, "ITEM": item_val} + block_id = _register_block("data_listcontainsitem", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 5) Touching object: + m_touch = re.fullmatch(r""" + \s* # leading space + (?:<\s*)? # optional '<' + touching # literal + \s*\[\s* + (?P[^\]]+?) # capture the sprite name + \s*v\]\? # close the [sprite v]? + (?:\s*>)? # optional '>' + """, s, re.IGNORECASE | re.VERBOSE) + if m_touch: + sprite = m_touch.group('sprite').strip() + val = {'mouse-pointer':'_mouse_', 'edge':'_edge_'}.get(sprite, sprite) + + mid = _register_block( + "sensing_touchingobjectmenu", parent_key, True, pick_key_func, all_generated_blocks, # Pass parent_key + fields={"TOUCHINGOBJECTMENU":[val, None]} + ) + bid = _register_block( + "sensing_touchingobject", parent_key, False, pick_key_func, all_generated_blocks, # Pass parent_key + inputs={"TOUCHINGOBJECTMENU":[1, mid]} + ) + all_generated_blocks[mid]["parent"] = bid + return {"kind":"block","block":bid} + + # 6) Touching color: + m = re.search(r"touching color \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR": [1, [9, m.group(1)]]} # Color input is special, often a list [type, value] + block_id = _register_block("sensing_touchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + return {"kind": "block", "block": block_id} + + # 7) Color is touching color: + m = re.search(r"color \[(#[0-9A-Fa-f]{6})\] is touching \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR1": [1, [9, m.group(1)]], "COLOR2": [1, [9, m.group(2)]]} + block_id = _register_block("sensing_coloristouchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + return {"kind": "block", "block": block_id} + + # 8) Key pressed: + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", s) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", parent_key, True, pick_key_func, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) # Pass parent_key + + inputs = {"KEY_OPTION": [1, menu_block_id]} + block_id = _register_block("sensing_keypressed", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + all_generated_blocks[menu_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 9) Mouse down?: mouse down? + if s == "mouse down?": + block_id = _register_block("sensing_mousedown", parent_key, False, pick_key_func, all_generated_blocks) # Pass parent_key + return {"kind": "block", "block": block_id} + + val_obj = parse_reporter_or_value(unparen(stmt), parent_key, pick_key_func, all_generated_blocks) + if val_obj: + return val_obj + + raise ValueError(f"Can't parse condition: {stmt}") + +def classify(line): + """ + Classifies a pseudo-code line into its corresponding Scratch opcode and block type. + Order of checks matters: more specific patterns should come before more general ones. + """ + l = line.lower().strip() + + # Ignore comments + if l.startswith("//"): return None, None + + # Hat Blocks (most specific first) + if re.match(r"when green flag click(ed)?", l): return "event_whenflagclicked", "hat" + if re.match(r"when (.+?) key press(ed)?", l): return "event_whenkeypressed", "hat" + if re.match(r"when this sprite click(ed)?", l): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if re.match(r"when i start as a clo(ne)?", l): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + if l.startswith("procedure "): return "procedures_definition", "hat" # For "procedure moveBall" + + # Motion Blocks + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + # IMPORTANT: More specific glide block before less specific one + if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if re.match(r"set x to\s*\(.+\)", l): return "motion_setx", "stack" # Specific for set x + if l.startswith("change y by"): return "motion_changeyby", "stack" + if re.match(r"set y to\s*\(.+\)", l): return "motion_sety", "stack" # Specific for set y + #if re.match(r"if on edge, bounce( off edge)?", l): return "motion_ifonedgebounce", "stack" + if re.match(r"if on edge,\s*bounc(e)?(\s+off\s+edge)?", l.strip(), re.IGNORECASE): return "motion_ifonedgebounce", "stack" + if re.match(r"bounce off edg(e)?", l): return "motion_ifonedgebounce", "stack" # Alias + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + + + # Looks Blocks + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if re.match(r"next costum(e)?", l): return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + # Updated regex for change/set effect by/to + if re.match(r"change\s*(\[.+?v\]|\(.+?\))?\s*effect by", l): return "looks_changeeffectby", "stack" + if re.match(r"set\s*(\[.+?v\]|\(.+?\))?\s*effect to", l): return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + # Sound Blocks + if re.match(r"play sound (.+?) until do(ne)?", l): return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + # Event Blocks (broadcasts) + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + # Control Blocks + if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + # Updated regex for stop block to handle different options + if re.match(r"stop \[(all|this script|other scripts in sprite)\s*v\]", l): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + # Data Blocks + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + # Updated regex for delete of list + if re.match(r"delete \((.+?)\) of \[([^\]]+)\s*v\]", l): return "data_deleteoflist", "stack" + if l.startswith("delete all of [" ): return "data_deletealloflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + # Sensing Blocks + if re.match(r"ask (.+?) and wai(t)?", l): return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom Blocks (procedures_call) - specific rule for "call" + if l.startswith("call "): + return "procedures_call", "stack" + + # Custom Blocks (procedures_call) - LAST RESORT (generic match) + # This should be the very last check for stack-type blocks to avoid conflicts. + # It tries to match anything that looks like a function call with or without arguments. + custom_block_match = re.match(r"([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", l) + if custom_block_match: + # Before returning, ensure it's not a known simple reporter or variable name + # that might have been missed or is being used standalone. + # This is a heuristic; a full parser would be more robust. + potential_name = custom_block_match.group(1).strip() + if potential_name not in ["x position", "y position", "direction", "mouse x", "mouse y", "loudness", "timer", "days since 2000", "username", "answer", "size", "volume"] and \ + not re.fullmatch(r"\[[^\]]+\]", potential_name) and \ + not re.fullmatch(r"\[[^\]]+\]\s*v", potential_name): + return "procedures_call", "stack" + + + raise ValueError(f"Unknown statement: {line!r}") + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key -> block_data (pre-generated block definitions) + • opcode_keys: dict of opcode -> list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... list of block dictionaries ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + all_generated_blocks = {} # Initialize as empty, blocks will be added as they are parsed + + # Stack stores (indent, owner_block_id, last_block_in_current_linear_chain_id) + # owner_block_id: The ID of the C-block or Hat block that owns the current substack. + # last_block_in_current_linear_chain_id: The ID of the last block added to the *current linear sequence* within this substack. + stack = [(-1, None, None)] # Sentinel: (indent, owner_block_id, last_block_in_current_linear_chain_id) + + top_level_script_keys = [] + + lines = pseudo_code.splitlines() + i = 0 + while i < len(lines): + raw_line = lines[i] + stripped_line = raw_line.strip() + + # Skip empty lines and comments + if not stripped_line or stripped_line.startswith("//"): + i += 1 + continue + + current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2 + + # Handle 'else' and 'end' first, as they control scope + if stripped_line.lower() == "else": + # Pop the 'then' substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # The 'if-else' block (popped_owner_key) is the owner. + # Push a new scope for the 'else' substack, with the same owner. + stack.append((current_indent, popped_owner_key, None)) # New scope for 'else' part, no last block yet + i += 1 + continue + + if stripped_line.lower() == "end": + # Pop the current substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # If the substack was empty, ensure the input is [2, None]. + if popped_owner_key: + owner_block = all_generated_blocks[popped_owner_key] + if owner_block["block_shape"] == "C-Block" or owner_block["op_code"] == "procedures_definition": + if owner_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in owner_block["inputs"] and \ + owner_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in owner_block["inputs"]: + # If SUBSTACK is already set, this 'end' closes the SUBSTACK2 (else part) + if not owner_block["inputs"].get("SUBSTACK2"): # Only set if not already set by a block + owner_block["inputs"]["SUBSTACK2"] = [2, None] + elif not owner_block["inputs"].get("SUBSTACK"): # Only set if not already set by a block + owner_block["inputs"]["SUBSTACK"] = [2, None] + + i += 1 + continue + + # Adjust stack based on indentation for regular blocks + # Pop scopes whose indentation is greater than or equal to the current line's indentation + while len(stack) > 1 and stack[-1][0] >= current_indent: + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None # Terminate the chain + + # Get the current active scope from the stack + current_scope_indent, current_owner_block_id, last_block_in_current_chain = stack[-1] + + # Classify the statement and create the block + stmt_for_parse = stripped_line.rstrip("then").strip() + opcode, ntype = classify(stmt_for_parse) + + if opcode is None: # Should not happen if classify is robust + i += 1 + continue + + # Create the new block (and register it in all_generated_blocks) + # _register_block now only sets parent for shadow/input blocks; main block parent/next/topLevel set here. + key = _register_block(opcode, None, False, pick_key, all_generated_blocks) + info = all_generated_blocks[key] + + # Set parent, next, and topLevel for the main script blocks + if ntype == "hat": + info["parent"] = None + info["topLevel"] = True + top_level_script_keys.append(key) + # Hat block's 'next' will be its first child, set when the first child is processed + info["next"] = None + # Push a new scope for the children of this hat block. + stack.append((current_indent, key, None)) # New scope: owner is this hat, no last block yet + else: # Stack block or C-block (that is part of a linear sequence) + if last_block_in_current_chain: + # This block's parent is the previous block in the chain + info["parent"] = last_block_in_current_chain + all_generated_blocks[last_block_in_current_chain]["next"] = key + else: + # This is the first block in a new linear chain (e.g., first block inside a forever loop) + # Its parent is the owner of the current scope (the C-block or Hat block) + info["parent"] = current_owner_block_id + + # If the owner is a C-block or procedure definition, link its SUBSTACK input + if current_owner_block_id and (all_generated_blocks[current_owner_block_id]["block_shape"] == "C-Block" or all_generated_blocks[current_owner_block_id]["op_code"] == "procedures_definition"): + owner_block = all_generated_blocks[current_owner_block_id] + if owner_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in owner_block["inputs"] and \ + owner_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in owner_block["inputs"]: + owner_block["inputs"]["SUBSTACK2"] = [2, key] + else: + owner_block["inputs"]["SUBSTACK"] = [2, key] + elif current_owner_block_id and all_generated_blocks[current_owner_block_id]["block_shape"] == "Hat Block": + # If the owner is a Hat block, this is its first child + all_generated_blocks[current_owner_block_id]["next"] = key + + info["topLevel"] = False + info["next"] = None # Default, will be overwritten if there's a next block + + # If it's a C-block or define block, it also starts a new inner scope + if ntype == "c_block" or opcode == "procedures_definition": + # Update the current scope's last_block_in_current_chain to this C-block + stack[-1] = (current_scope_indent, current_owner_block_id, key) + # Push a new scope for the C-block's substack + stack.append((current_indent, key, None)) # New scope: owner is this C-block, no last block yet + else: + # For regular stack blocks, just update the last_block_in_current_chain for the current scope + stack[-1] = (current_scope_indent, current_owner_block_id, key) + + # Parse inputs and fields (this part remains largely the same, but ensure parse_reporter_or_value/parse_condition + # are passed the *newly created block's ID* as the parent_key for nested inputs) + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if opcode == "motion_movesteps": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["STEPS"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_turnright" or opcode == "motion_turnleft": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DEGREES"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_gotoxy": + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_glidesecstoxy": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt_for_parse, re.IGNORECASE) + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_secs: info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_pointindirection": + m = re.search(r"direction\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DIRECTION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_changexby", "motion_changeyby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DX" if opcode == "motion_changexby" else "DY"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_setx", "motion_sety"]: + m = re.search(r"(?:set x to|set y to)\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["X" if opcode == "motion_setx" else "Y"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_changesizeby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_setsizeto": + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SIZE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_changeeffectby", "sound_changeeffectby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE" if opcode == "looks_changeeffectby" else "VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE" if opcode == "looks_seteffectto" else "VOLUME"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["NUM"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "control_wait": + m = re.search(r"wait\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DURATION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "control_repeat": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["TIMES"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "data_changevariableby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "data_deleteoflist": + m = re.search(r"delete\s*\((.+?)\)\s*of", stmt_for_parse, re.IGNORECASE) + if m: + val_str = m.group(1).strip() + if val_str.isdigit(): + info["inputs"]["INDEX"] = {"kind": "value", "value": int(val_str)} + else: # "all", "last", "random" + info["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + elif opcode == "data_insertatlist": + m_item = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt_for_parse, re.IGNORECASE) + m_index = re.search(r"at\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + elif opcode == "data_replaceitemoflist": + m_index = re.search(r"replace item\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + m_item = re.search(r"with\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + elif opcode == "event_whengreaterthan": + m = re.search(r">\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + + # String inputs + elif opcode == "looks_sayforsecs": + m = re.search(r"say\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_say": + m = re.search(r"say\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = parse_reporter_or_value(m.group(1).strip(), key, pick_key, all_generated_blocks) + elif opcode == "looks_thinkforsecs": + m = re.search(r"think\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_think": + m = re.search(r"think\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "sensing_askandwait": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["QUESTION"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["ITEM"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_setvariableto": + m_var = re.search(r"set\s*\[([^\]]+)\s*v\]\s*to\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m_var: + var_name = m_var.group(1).strip() + value_str = m_var.group(2).strip() + info["fields"]["VARIABLE"] = [var_name, None] + info["inputs"]["VALUE"] = parse_reporter_or_value(value_str, key, pick_key, all_generated_blocks) + + # Dropdown/Menu inputs + elif opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_glideto": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m_secs: + info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + option = m_secs.group(2).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TOWARDS"] = {"kind": "menu", "option": option} + elif opcode == "sensing_keypressed": # For boolean block + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["KEY_OPTION"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "sensing_touchingobject": # For boolean block + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + elif option == "edge": option = "_edge_" + info["inputs"]["TOUCHINGOBJECTMENU"] = {"kind": "menu", "option": option} + elif opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "myself": option = "_myself_" + info["inputs"]["CLONE_OPTION"] = {"kind": "menu", "option": option} + elif opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SOUND_MENU"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["COSTUME"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait"]: + m = re.search(r"switch backdrop to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BACKDROP"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BROADCAST_INPUT"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "event_whenbroadcastreceived": + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info.setdefault("fields", {})["BROADCAST_OPTION"] = [m.group(1).strip(), None] + + # Conditional inputs (Boolean blocks) + elif opcode in ["control_if", "control_if_else", "control_wait_until", "control_repeat_until"]: + #cond_match = re.search(r"<(.*?)>", stmt_for_parse) + cond_match = extract_condition_balanced(stmt_for_parse) + print(f"[THE CONDA MATCH]------------->{cond_match}") + if cond_match: + # Pass current block's key as parent for nested condition + info["inputs"]["CONDITION"] = parse_condition(cond_match.strip(), key, pick_key, all_generated_blocks) + elif opcode in ["operator_and", "operator_or", "operator_not", "operator_contains", + "sensing_touchingcolor", "sensing_coloristouchingcolor", "sensing_mousedown"]: + # These are handled by parse_condition directly, which returns the nested structure + # No need to re-parse inputs here, as they are part of the condition structure + pass # Inputs are set when parse_condition is called for the parent block + + + # Fields parsing + if "VARIABLE" in info["fields"]: + m = re.search(r"\[([^\]]+)\s*v\]", stmt_for_parse) + if m: + var_name = m.group(1).strip() + info["fields"]["VARIABLE"] = [var_name, None] + if "LIST" in info["fields"]: + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["LIST"] = [m.group(1), None] + if "STOP_OPTION" in info["fields"]: + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STOP_OPTION"] = [m.group(1), None] + if "STYLE" in info["fields"]: + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STYLE"] = [m.group(1), None] + if "DRAG_MODE" in info["fields"]: + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["DRAG_MODE"] = [m.group(1), None] + if "EFFECT" in info["fields"] and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["EFFECT"] = [m.group(1).upper(), None] + if "NUMBER_NAME" in info["fields"] and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["NUMBER_NAME"] = [m.group(1), None] + if "FRONT_BACK" in info["fields"] and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FRONT_BACK"] = [m.group(1), None] + if "FORWARD_BACKWARD" in info["fields"] and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + if "OPERATOR" in info["fields"] and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["OPERATOR"] = [m.group(1).upper(), None] + if "CURRENTMENU" in info["fields"] and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + if "PROPERTY" in info["fields"] and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt_for_parse, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + info["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + if "WHENGREATERTHANMENU" in info["fields"] and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + if "KEY_OPTION" in info["fields"] and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["KEY_OPTION"] = [m.group(1), None] + if "BACKDROP" in info["fields"] and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BACKDROP"] = [m.group(1), None] + if "BROADCAST_OPTION" in info["fields"] and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + # Custom block specific parsing + if opcode == "procedures_definition": + proc_def_match = re.match(r"(?:define|procedure)\s+([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))?", stmt_for_parse, re.IGNORECASE) + if proc_def_match: + proc_name = proc_def_match.group(1).strip() + args_str = proc_def_match.group(2) + info["procedure_name"] = proc_name + info["is_custom_definition"] = True + + # Create the special mutation block for the definition + mutation_block = { + "tagName": "mutation", + "children": [], + "proccode": proc_name, + "argumentids": [], + "argumentnames": [], + "argumentdefaults": [], + "warp": False # Assuming non-warp by default + } + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for arg in args: + arg_id = f"%s" # Scratch uses %s for string args, %n for number args + # For simplicity, we'll just use a generic ID for now, or match Scratch's pattern + # For the plan, we just need the names and order. + mutation_block["argumentids"].append(arg_id) + mutation_block["argumentnames"].append(arg) + mutation_block["argumentdefaults"].append("") + + info["mutation"] = mutation_block + + elif opcode == "procedures_call": + call_match = re.match(r"(?:call\s+)?([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", stmt_for_parse, re.IGNORECASE) + if call_match: + custom_block_name = call_match.group(1).strip() + args_str = call_match.group(2) + info["custom_block_name"] = custom_block_name + + info["mutation"] = { + "tagName": "mutation", + "children": [], + "proccode": custom_block_name, + "argumentids": [], + "argumentnames": [], + "warp": False + } + + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for idx, arg_val_str in enumerate(args): + arg_input_name = f"argument_name_{idx+1}" + info["mutation"]["argumentids"].append(arg_input_name) # Use the input name as argument ID + info["mutation"]["argumentnames"].append(f"arg{idx+1}") # Placeholder name for mutation + + info["inputs"][arg_input_name] = parse_reporter_or_value(arg_val_str, key, pick_key, all_generated_blocks) # Pass current block's key + + i += 1 # Move to the next line + + # Final pass to ensure last blocks have next: None (already handled by stack pops) + # The build_script_flow function will correctly traverse the linked list. + while len(stack) > 1: # Keep the initial sentinel + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + print(f"[ALL OPCODE BLCOKS KEY 2]: {all_generated_blocks}") + with open("all_generated_blocks.json", "w") as f: + json.dump(all_generated_blocks, f, indent=2) + # Construct the final flow output based on the collected top-level keys + # Recursively build the block structure for each top-level script + def build_script_flow(current_block_key, visited=None): + if visited is None: + visited = set() + + script_flow = [] + current_iter_key = current_block_key + + while current_iter_key: + # Detect cyclic reference + if current_iter_key in visited: + script_flow.append({ + "block_key": current_iter_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected, stopping recursion" + }) + break + + visited.add(current_iter_key) + block = all_generated_blocks.get(current_iter_key) + if not block: + break # Should not happen if keys are correct + + output_block = { + "block_key": block["id"], + "opcode": block["op_code"], + "type": block["block_shape"].replace(" Block", "").lower().replace("c-", "c_"), + "inputs": {}, + "fields": {} + } + + # Handle all input types + for inp_name, inp_val in block.get("inputs", {}).items(): + if inp_name in ["SUBSTACK", "SUBSTACK2"]: + if inp_val and len(inp_val) > 1 and inp_val[1] in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(inp_val[1], visited.copy()) + else: + output_block["inputs"][inp_name] = [] + elif inp_name == "PROCCONTAINER" and block.get("is_custom_definition"): + output_block["inputs"][inp_name] = inp_val + elif isinstance(inp_val, dict) and inp_val.get("kind") == "block": + # Recursively build nested reporter/boolean blocks + nested_block_key = inp_val["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val # Keep original if not found (shouldn't happen) + elif isinstance(inp_val, dict) and inp_val.get("kind") == "nested_reporter": + nested_block_key = inp_val["reporter"]["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val + else: + output_block["inputs"][inp_name] = inp_val + + for field_name, field_val in block.get("fields", {}).items(): + output_block["fields"][field_name] = field_val + + if block.get("custom_block_name"): + output_block["custom_block_name"] = block["custom_block_name"] + + if block.get("procedure_name"): + output_block["procedure_name"] = block["procedure_name"] + output_block["is_custom_definition"] = True + if "mutation" in block: # Include mutation for custom definitions + output_block["mutation"] = block["mutation"] + + + script_flow.append(output_block) + + # Proceed to the next block in sequence + next_key = block.get("next") + if next_key in visited: + script_flow.append({ + "block_key": next_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected in 'next' pointer" + }) + break + + current_iter_key = next_key + + return script_flow + + final_flow_output = [] + for key in top_level_script_keys: + final_flow_output.extend(build_script_flow(key)) + return {"flow": final_flow_output} + +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_xposition","count":1}, + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":2}, + {"opcode":"control_stop","count":1}, + {"opcode":"operator_lt","count":1}, + {"opcode":"sensing_touchingobject","count":1}, + {"opcode":"sensing_touchingobjectmenu","count":1}, + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":2}, + {"opcode":"data_showvariable","count":2}, +] +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) +with open("generated_output_json.json", "w") as f: + json.dump(generated_output_json, f, indent=2) + +pseudo_code_examples = [""" +when green flag clicked + go to x: (240) y: (-135) + set [score v] to (1) + set [speed v] to (1) + show variable [score v] + show variable [speed v] + forever + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end +end +""",] +txt="" +trace="" +# Process each example and print the plan +for i, pseudo_code_input in enumerate(pseudo_code_examples): + print(f"\n--- Processing Example {i+1} ---") + #print(pseudo_code_input.strip()) + try: + # Regenerate blocks and opcode_occurrences for each run to ensure fresh keys + # This is important because pick_key uses a defaultdict that persists state. + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) + print(json.dumps(plan, indent=2)) + txt += str(plan) + " \n" + except Exception as e: + #print(f"Error processing example: {e}") + import traceback + #traceback.print_exc() + trace += str(e) +" "+str(pseudo_code_input)+" \n" + +with open("all_analysis.txt", "w", encoding="utf-8") as f: + f.write(txt) + +with open("all_analysis_trace.txt", "w", encoding="utf-8") as f: + f.write(trace) diff --git a/utils/plan_generator_9.py b/utils/plan_generator_9.py new file mode 100644 index 0000000000000000000000000000000000000000..6fd5842aa18d4f7a28f7aefd6284ccfc846d0187 --- /dev/null +++ b/utils/plan_generator_9.py @@ -0,0 +1,2199 @@ +import json +import copy +import re +from collections import defaultdict + + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition, + and groups all generated block keys by their corresponding opcode. + + Returns: + tuple: (generated_blocks, opcode_to_keys) + - generated_blocks: dict of block_key -> block_data + - opcode_to_keys: dict of opcode -> list of block_keys + """ + generated_blocks = {} + opcode_counts_map = {} # For counting unique suffix per opcode + opcode_to_keys = {} # For grouping block keys by opcode + + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + if opcode == "sensing_istouching": # Handle potential old opcode name + opcode = "sensing_touchingobject" + + if not opcode or opcode not in all_block_definitions: + print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).") + continue + + for _ in range(count): + # Count occurrences per opcode for unique key generation + opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1 + instance_num = opcode_counts_map[opcode] + main_key = f"{opcode}_{instance_num}" + + # Track the generated key + opcode_to_keys.setdefault(opcode, []).append(main_key) + + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False + + # Ensure inputs and fields are dictionaries, even if they were None or list in definition + if "inputs" not in main_block_data or not isinstance(main_block_data["inputs"], dict): + main_block_data["inputs"] = {} + if "fields" not in main_block_data or not isinstance(main_block_data["fields"], dict): + main_block_data["fields"] = {} + + generated_blocks[main_key] = main_block_data + + # Handle menus + if opcode in explicit_menu_links: + for input_name, menu_opcode in explicit_menu_links[opcode]: + if menu_opcode not in all_block_definitions: + continue + + opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1 + menu_instance_num = opcode_counts_map[menu_opcode] + menu_key = f"{menu_opcode}_{menu_instance_num}" + + opcode_to_keys.setdefault(menu_opcode, []).append(menu_key) + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode]) + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_key + + # Ensure inputs and fields are dictionaries for menu blocks too + if "inputs" not in menu_block_data or not isinstance(menu_block_data["inputs"], dict): + menu_block_data["inputs"] = {} + if "fields" not in menu_block_data or not isinstance(menu_block_data["fields"], dict): + menu_block_data["fields"] = {} + + if input_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_name], list) and \ + len(main_block_data["inputs"][input_name]) > 1 and \ + main_block_data["inputs"][input_name][0] == 1: + + main_block_data["inputs"][input_name][1] = menu_key + + generated_blocks[menu_key] = menu_block_data + + return generated_blocks, opcode_to_keys + +# Consolidated block definitions from all JSON files +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_distanceto": { # Added sensing_distanceto + "block_name": "(distance to ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_distanceto", + "functionality": "Reports the distance from the sprite to the mouse-pointer or another specified sprite.", + "inputs": {}, "fields": {"TARGET": ["_mouse_", None]}, "shadow": False, "topLevel": True + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + "data_list": { # Added this block definition + "block_name": "[list v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_list", + "functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.", + "inputs": {}, "fields": {"LIST": ["my list", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "procedures_definition": { + "block_name": "define [my custom block]", + "block_type": "My Blocks", + "op_code": "procedures_definition", + "block_shape": "Hat Block", + "functionality": "This Hat block serves as the definition header for a custom block's script.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True + }, + "procedures_call": { + "block_name": "[my custom block]", + "block_type": "My Blocks", + "block_shape": "Stack Block", + "op_code": "procedures_call", + "functionality": "Executes the script defined by a corresponding 'define' Hat block.", + "inputs": {}, # Changed to empty dict + "fields": {}, + "shadow": False, + "topLevel": True + } +} + +def unparen(s): + s = s.strip() + # keep peeling off *all* matching outer parens + while True: + m = re.fullmatch(r"\((.*)\)", s) + if not m: + break + s = m.group(1).strip() + return s + +def _register_block(opcode, parent_key_for_shadow_or_input, is_shadow, pick_key_func, all_generated_blocks, inputs=None, fields=None): + """ + Helper to create and register a block in all_generated_blocks. + Returns the key of the newly created block. + """ + key = pick_key_func(opcode) # Use the passed pick_key_func + block_data = copy.deepcopy(all_block_definitions[opcode]) + block_data["id"] = key + + # Initialize inputs and fields if not provided to prevent KeyError later + if "inputs" not in block_data or not isinstance(block_data["inputs"], dict): + block_data["inputs"] = {} + if "fields" not in block_data or not isinstance(block_data["fields"], dict): + block_data["fields"] = {} + + if inputs: + block_data["inputs"].update(inputs) # Use update to merge, not overwrite + if fields: + block_data["fields"].update(fields) # Use update to merge, not overwrite + + block_data["shadow"] = is_shadow + + if is_shadow: + block_data["parent"] = parent_key_for_shadow_or_input + block_data["next"] = None # Shadow blocks don't have a 'next' in the main chain + block_data["topLevel"] = False # Shadow blocks are never top-level + else: + # For non-shadow blocks: + # If it's a Reporter or Boolean block, its parent is the block it's an input to. + # Otherwise (Stack, C-Block, Cap, Hat), its parent/next will be set by generate_plan's sequential logic. + if all_block_definitions[opcode]["block_shape"] in ["Reporter Block", "Boolean Block"]: + block_data["parent"] = parent_key_for_shadow_or_input # This is the key of the block it's an input to + block_data["next"] = None # Reporters/Booleans never have a 'next' in the main chain + block_data["topLevel"] = False # Reporters/Booleans are never top-level + else: + # For Stack, C-Block, Cap, Hat blocks, parent/next/topLevel are set by generate_plan + block_data["parent"] = None + block_data["next"] = None + block_data["topLevel"] = False # Default, will be set to True for Hat blocks by generate_plan + + all_generated_blocks[key] = block_data + return key + +def _auto_balance(text): + # if there are more "(" than ")", append the missing ")" + diff = text.count("(") - text.count(")") + if diff > 0: + text = text + ")"*diff + # same for square brackets + diff = text.count("[") - text.count("]") + if diff > 0: + text = text + "]"*diff + return text + +def strip_outer_angle_brackets(text): + """ + Strip exactly one balanced pair of outer <...> brackets, only if they wrap the whole string. + """ + text = text.strip() + if text.startswith("<") and text.endswith(">"): + depth = 0 + for i, char in enumerate(text): + if char == '<': + depth += 1 + elif char == '>': + depth -= 1 + if depth == 0 and i == len(text) - 1: + return text[1:-1].strip() + # If we exit the loop and depth is 0, it means the outer brackets were balanced and wrapped the whole string + if depth == 0: + return text[1:-1].strip() + return text + +def extract_condition_balanced(stmt): + # 1. Remove "if" and "then" + stmt = stmt.strip() + if stmt.lower().startswith("if "): + stmt = stmt[3:].strip() + if stmt.lower().startswith("repeat until"): + stmt = stmt[12:].strip() + if stmt.lower().startswith("wait until "): + stmt = stmt[11:].strip() + if stmt.lower().endswith(" then"): + stmt = stmt[:-5].strip() + + # Helper to detect and strip single outer balanced angle brackets + def unwrap_balanced(s): + if s.startswith("<") and s.endswith(">"): + depth = 0 + for i in range(len(s)): + if s[i] == "<": + depth += 1 + elif s[i] == ">": + depth -= 1 + if depth == 0 and i < len(s) - 1: + return s # Early balance → not a single outer wrapper + if depth == 0: + return s[1:-1].strip() + return s + + # Recursively simplify things like > to not + def simplify(s): + s = unwrap_balanced(s) + s = s.strip() + + # Match > pattern + m = re.fullmatch(r"not\s*<(.+)>", s, re.IGNORECASE) + if m: + inner = m.group(1).strip() + inner = simplify(inner) + return f"not <{inner}>" + + # Match comparison operators like <(x position) < (100)> + # This part might be redundant if the main parser handles it, but good for internal consistency + m_comp = re.fullmatch(r"<\s*\(([^<>]+?)\)\s*([<>=])\s*\(([^<>]+?)\)\s*>", stmt) + if m_comp: + return f"({m_comp.group(1).strip()}) {m_comp.group(2)} ({m_comp.group(3).strip()})" + + return s + + return simplify(stmt) + +# Nested helper for parsing reporters or values +def parse_reporter_or_value(text, parent_key, pick_key_func, all_generated_blocks): + text = _auto_balance(text.strip()) + text = unparen(text.strip()) + # Check for numeric literal (including parenthesized numbers like "(0)" or "(10)") + m_num = re.fullmatch(r"\(?\s*(-?\d+(\.\d+)?)\s*\)?", text) + if m_num: + val_str = m_num.group(1) + return {"kind": "value", "value": float(val_str) if '.' in val_str else int(val_str)} + + # # Check for string literal (e.g., "[Hello!]") + # if text.startswith('[') and text.endswith(']'): + # return {"kind": "value", "value": text[1:-1]} + # Variable reporter: [score v], [health v], etc. + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m_var.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, + fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + + # Now catch other bracketed values as literal strings + if text.startswith('[') and text.endswith(']'): + return {"kind": "value", "value": text[1:-1]} + + # --- Reporter Blocks --- + + # (x position), (y position), (direction), (mouse x), (mouse y), (loudness), (timer), (days since 2000), (username), (answer), (size), (volume) + simple_reporters = { + "x position": "motion_xposition", + "y position": "motion_yposition", + "direction": "motion_direction", + "mouse x": "sensing_mousex", + "mouse y": "sensing_mousey", + "loudness": "sensing_loudness", + "timer": "sensing_timer", + "days since 2000": "sensing_dayssince2000", + "username": "sensing_username", + "answer": "sensing_answer", + "size": "looks_size", + "volume": "sound_volume" + } + # Check for simple reporters, potentially with outer parentheses + m_simple_reporter = re.fullmatch(r"\((.+?)\)", text) + if m_simple_reporter: + inner_text = m_simple_reporter.group(1).strip() + if inner_text in simple_reporters: + block_id = _register_block(simple_reporters[inner_text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + # Also check for simple reporters without parentheses (e.g., if passed directly) + if text in simple_reporters: + block_id = _register_block(simple_reporters[text], parent_key, False, pick_key_func, all_generated_blocks) + return {"kind": "block", "block": block_id} + + + # Variable reporter: [score v] or (score) or just "score" + m_var = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_var: + var_name = m.group(1).strip() + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [var_name, None]}) + return {"kind": "block", "block": block_id} + m_paren_var = re.fullmatch(r"\(([^)]+)\)", text) + if m_paren_var: + potential_var_name = m_paren_var.group(1).strip() + # Ensure it's not a simple reporter already handled, or a number + if potential_var_name not in simple_reporters and not re.fullmatch(r"-?\d+(\.\d+)?", potential_var_name): + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [potential_var_name, None]}) + return {"kind": "block", "block": block_id} + # Handle plain variable names like "score", "number 1", "total score" + if re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_ ]*", text): # Allow spaces for "number 1" etc. + # Exclude known simple reporters that don't have 'v' or parentheses + if text not in simple_reporters: + block_id = _register_block("data_variable", parent_key, True, pick_key_func, all_generated_blocks, fields={"VARIABLE": [text, None]}) + return {"kind": "block", "block": block_id} + + + # List reporter: [my list v] + m_list_reporter = re.fullmatch(r"\[([^\]]+)\s*v\]", text) + if m_list_reporter: + list_name = m_list_reporter.group(1).strip() + block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) + return {"kind": "block", "block": block_id} + + + # (pick random () to ()) (operator_random) + m = re.search(r"pick random \((.+?)\) to \((.+?)\)", text) + if m: + min_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + max_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) # Parent will be set later + inputs = {"FROM": min_val_obj, "TO": max_val_obj} + block_id = _register_block("operator_random", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # Set parents for nested inputs + if min_val_obj.get("kind") == "block": all_generated_blocks[min_val_obj["block"]]["parent"] = block_id + if max_val_obj.get("kind") == "block": all_generated_blocks[max_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (join ()()) (operator_join) - handle both [] and () for inputs + + # m = re.search(r"join \((.+?)\) \((.+?)\)", text) # Try (val) (val) + # if not m: + # m = re.search(r"join \[(.+?)\] \[(.+?)\]", text) # Try [val] [val] + # if m: + # str1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + # str2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + # inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + # block_id = _register_block("operator_join", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + # if str1_obj.get("kind") == "block": all_generated_blocks[str1_obj["block"]]["parent"] = block_id + # if str2_obj.get("kind") == "block": all_generated_blocks[str2_obj["block"]]["parent"] = block_id + # return {"kind": "block", "block": block_id} + + #m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s+(\[.+?\]|\(.+?\))", text) # Try (val) (val) + m = re.search(r"join\s+(\[.+?\]|\(.+?\))\s*(\[.+?\]|\(.+?\))", text) + if m: + part1_txt = m.group(1).strip() + part2_txt = m.group(2).strip() + str1_obj = parse_reporter_or_value(part1_txt, None, pick_key_func, all_generated_blocks) + str2_obj = parse_reporter_or_value(part2_txt, None, pick_key_func, all_generated_blocks) + inputs = {"STRING1": str1_obj, "STRING2": str2_obj} + block_id = _register_block("operator_join", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs) + # set parents if nested blocks + for obj in (str1_obj, str2_obj): + if obj.get("kind") == "block": + all_generated_blocks[obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # letter () of () (operator_letterof) - handle both [] and () for inputs + m = re.search(r"letter \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"letter \((.+?)\) of \[(.+?)\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + string_val_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"LETTER": index_obj, "STRING": string_val_obj} + block_id = _register_block("operator_letterof", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + if string_val_obj.get("kind") == "block": all_generated_blocks[string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (length of ()) (operator_length) - handle both [] and () for inputs + #m = re.search(r"length of \((.+?)\)", text) + m = re.search(r"length of\s*(?:\((.+?)\)|\[(.+?)\])", text) + if not m: + m = re.search(r"length of \[([^\]]+)\s*v\]", text) + if m: + arg_txt = (m.group(1) or m.group(2)).strip() + list_or_string_val_obj = parse_reporter_or_value(arg_txt, None, pick_key_func, all_generated_blocks) + #list_or_string_val_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"STRING": list_or_string_val_obj} + block_id = _register_block("operator_length", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if list_or_string_val_obj.get("kind") == "block": all_generated_blocks[list_or_string_val_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (() mod ()) (operator_mod) + # m = re.search(r"\((.+?)\)\s*mod\s*\((.+?)\)", text) + m = re.search(r"\[([^\]]+)\s*v\]\s*mod\s*\(?\s*(.+?)\s*\)?", text) + if m: + num1_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + num2_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM1": num1_obj, "NUM2": num2_obj} + block_id = _register_block("operator_mod", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num1_obj.get("kind") == "block": all_generated_blocks[num1_obj["block"]]["parent"] = block_id + if num2_obj.get("kind") == "block": all_generated_blocks[num2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (round ()) (operator_round) + m = re.search(r"round \((.+?)\)", text) + if m: + num_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": num_obj} + block_id = _register_block("operator_round", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if num_obj.get("kind") == "block": all_generated_blocks[num_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (() of ()) (operator_mathop) - handle variable for function type + m = re.search(r"\[([^\]]+)\s*v\] of \((.+?)\)", text) # e.g. [sqrt v] of ((x pos) * (x pos)) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + # Also handle direct string for function type (e.g., "abs of (x)") + m = re.search(r"([a-zA-Z]+)\s*of\s*\((.+?)\)", text) + if m: + func_type = m.group(1).strip() + value_obj = parse_reporter_or_value(m.group(2).strip(), None, pick_key_func, all_generated_blocks) + inputs = {"NUM": value_obj} + fields = {"OPERATOR": [func_type.upper(), None]} + block_id = _register_block("operator_mathop", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if value_obj.get("kind") == "block": all_generated_blocks[value_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # Arithmetic operations: (() + ()), (() - ()), (() * ()), (() / ()) + # This regex is designed to handle nested parentheses correctly. + # It looks for an opening parenthesis, then non-parenthesis characters or balanced parentheses, + # followed by an operator, and then the second operand. + # This is a simplified approach; a full-fledged parser would use a stack. + # arithmetic_match = re.search(r"\((.+?)\)\s*([+\-*/])\s*\((.+?)\)", text) + arithmetic_match = re.search(r"\(?\s*(.+?)\s*\)?\s*([\+\-\*/])\s*\(?\s*(.+?)\s*\)?", text) + if not arithmetic_match: + # Try to match without outer parentheses for the operands, but still with an operator + arithmetic_match = re.search(r"(.+?)\s*([+\-*/])\s*(.+)", text) + + if arithmetic_match: + op1_str = arithmetic_match.group(1).strip() + operator_symbol = arithmetic_match.group(2).strip() + op2_str = arithmetic_match.group(3).strip() + + op1_obj = parse_reporter_or_value(op1_str, None, pick_key_func, all_generated_blocks) + op2_obj = parse_reporter_or_value(op2_str, None, pick_key_func, all_generated_blocks) + + opcode_map = {'+': 'operator_add', '-': 'operator_subtract', '*': 'operator_multiply', '/': 'operator_divide'} + inputs = {"NUM1": op1_obj, "NUM2": op2_obj} + block_id = _register_block(opcode_map[operator_symbol], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + if op1_obj.get("kind") == "block": all_generated_blocks[op1_obj["block"]]["parent"] = block_id + if op2_obj.get("kind") == "block": all_generated_blocks[op2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # (costume ()) (looks_costumenumbername) - handle with or without 'v' + m = re.search(r"costume \((.+?)\)", text) + if not m: + m = re.search(r"costume \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_costumenumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (backdrop ()) (looks_backdropnumbername) - handle with or without 'v' + m = re.search(r"backdrop \((.+?)\)", text) + if not m: + m = re.search(r"backdrop \[([^\]]+)\s*v\]", text) + if m: + option = m.group(1).strip() + fields = {"NUMBER_NAME": [option, None]} + block_id = _register_block("looks_backdropnumbername", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (distance to ()) (sensing_distanceto) - handle with or without 'v' + m = re.search(r"distance to \((.+?)\)", text) + if not m: + m = re.search(r"distance to \[([^\]]+)\s*v\]", text) + if m: + target = m.group(1).strip() + if target == "mouse-pointer": target_val = "_mouse_" + elif target == "edge": target_val = "_edge_" + else: target_val = target + inputs = {"TARGET": [target_val, None]} # This is a direct value, not a block + block_id = _register_block("sensing_distanceto", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + return {"kind": "block", "block": block_id} + + # (current ()) (sensing_current) - handle with or without 'v' + m = re.search(r"current \((.+?)\)", text) + if not m: + m = re.search(r"current \[([^\]]+)\s*v\]", text) + if m: + unit = m.group(1).strip() + fields = {"CURRENTMENU": [unit.upper(), None]} + block_id = _register_block("sensing_current", parent_key, False, pick_key_func, all_generated_blocks, fields=fields) + return {"kind": "block", "block": block_id} + + # (() of ()) (sensing_of) - handle both variable and non-variable properties, and objects + # Updated regex to correctly capture the property and object, including nested reporters for property + m = re.search(r"\((.+?)\)\s*of\s*\((.+?)\)", text) # (prop) of (obj) + if not m: + m = re.search(r"\((.+?)\)\s*of\s*\[([^\]]+)\s*v\]", text) # (prop) of [obj v] + if m: + prop_str = m.group(1).strip() + obj = m.group(2).strip() + + # Map common property names to their internal Scratch representation + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + property_value = prop_map.get(prop_str, prop_str) # Use mapped value or original string + + # The object can be a sprite name or "_stage_" + obj_kind = "menu" + if obj.lower() == "stage": obj_val = "_stage_" + elif obj.lower() == "myself": obj_val = "_myself_" + else: obj_val = obj # Assume it's a sprite name + + # Create the menu block for OBJECT input + object_menu_id = _register_block("sensing_of_object_menu", parent_key, True, pick_key_func, all_generated_blocks, fields={"OBJECT": [obj_val, None]}) + + # inputs = {"OBJECT": [1, object_menu_id]} # Link to the menu block + # block_id = _register_block("sensing_of", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + + # # Update parent for the menu block + # all_generated_blocks[object_menu_id]["parent"] = block_id + + # return {"kind": "block", "block": block_id} + of_fields = {"OBJECT": [obj_val, None]} + inputs = {"OBJECT": [1, object_menu_id]} + block_id = _register_block("sensing_of", parent_key, False, + pick_key_func, all_generated_blocks, + inputs=inputs, + fields=of_fields) + all_generated_blocks[object_menu_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item (index) of [list v]) (data_itemoflist) - handle with or without 'v' and parentheses for index + m = re.search(r"item \((.+?)\) of \((.+?)\)", text) + if not m: + m = re.search(r"item \((.+?)\) of \[([^\]]+)\s*v\]", text) + if m: + index_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + inputs = {"INDEX": index_obj} + fields = {"LIST": [list_name, None]} + block_id = _register_block("data_itemoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if index_obj.get("kind") == "block": all_generated_blocks[index_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # (item # of [item] in [list v]) (data_itemnumoflist) - handle with or without 'v' and parentheses for item + m = re.search(r"item # of \((.+?)\) in \((.+?)\)", text) + if not m: + m = re.search(r"item # of \[([^\]]+)\] in \[([^\]]+)\s*v\]", text) + if m: + item_obj = parse_reporter_or_value(m.group(1).strip(), None, pick_key_func, all_generated_blocks) + list_name = m.group(2).strip() + + inputs = {"ITEM": item_obj} + fields = {"LIST": [list_name, None]} + block_id = _register_block("data_itemnumoflist", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs, fields=fields) + if item_obj.get("kind") == "block": all_generated_blocks[item_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # inside parse_reporter_or_value + m = re.search(r"\(\s*(.+?)\s*\)\s*([\+\-\*/])\s*\(\s*(.+?)\s*\)", text) + if m: + left = parse_reporter_or_value(f"({m.group(1).strip()})", parent_key, pick_key_func, all_generated_blocks) + right = parse_reporter_or_value(f"({m.group(3).strip()})", parent_key, pick_key_func, all_generated_blocks) + op_map = {"+": "operator_add", "-": "operator_subtract", "*": "operator_multiply", "/": "operator_divide"} + opcode = op_map[m.group(2).strip()] + inputs = {"NUM1": left, "NUM2": right} + block_id = _register_block(opcode, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) + for side in (left, right): + if side.get("kind") == "block": + all_generated_blocks[side["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + raise ValueError(f"Can't parse reporter or value: {text}") + +def parse_condition(stmt, parent_key, pick_key_func, all_generated_blocks): + """ + Parse Scratch-style boolean conditions, handling comparisons (<, =, >), + boolean operators (and, or, not), and other sensing conditions. + """ + s = stmt.strip() + s = extract_condition_balanced(s) + s = s.lower() + + print(f"the stmt was this {stmt} and parsed was this {s}") + # 1) Boolean NOT: `not <...>` + #m_not = re.fullmatch(r'not\s*<\s*(.+?)\s*>', s, re.IGNORECASE) + #m_not = re.fullmatch(r"\s*<\s*not\s+(.+?)\s*>\s*", s, re.IGNORECASE) + m_not = re.fullmatch(r"\s*(?:<\s*)?not\s+(.+?)(?:\s*>)?\s*",s, re.IGNORECASE) + if m_not: + inner = m_not.group(1).strip() + print(f"[2]the stmt was this {stmt} and parsed was this {s}") + inner_obj = parse_condition(inner, parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + bid = _register_block("operator_not", parent_key, False, pick_key_func, all_generated_blocks, # Pass parent_key + inputs={"OPERAND": inner_obj}) + if inner_obj.get("kind") == "block": + all_generated_blocks[inner_obj["block"]]["parent"] = bid + return {"kind": "block", "block": bid} + + # 2) Boolean AND / OR + #m_andor = re.fullmatch(r"<\s*(.+?)\s+(and|or)\s+(.+?)\s*>", s, re.IGNORECASE) + m_andor = re.fullmatch(r"\s*(.+?)\s+(and|or)\s+(.+?)\s*", s, re.IGNORECASE) + if m_andor: + cond1_obj = parse_condition(m_andor.group(1).strip(), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + cond2_obj = parse_condition(m_andor.group(3).strip(), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_block = 'operator_and' if m_andor.group(2).lower() == 'and' else 'operator_or' + print(f"The cond1: {cond1_obj} and the cond2: {cond2_obj} [for testing]") + inputs = {"OPERAND1": cond1_obj, "OPERAND2": cond2_obj} + block_id = _register_block(op_block, parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + if cond1_obj.get("kind") == "block": all_generated_blocks[cond1_obj["block"]]["parent"] = block_id + if cond2_obj.get("kind") == "block": all_generated_blocks[cond2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + + # 1a) Comparisons with explicit angle wrappers: < (...) op (...) > + m = re.fullmatch( + r"\s*<\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*>\s*", + s, + re.VERBOSE + ) + if m: + left_txt, right_txt = m.group(1), m.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + # Set parents for nested inputs + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 1b) Simple comparisons without angle wrappers: A op B + m_simple = re.fullmatch(r"\s*(.+?)\s*(?P<|=|>)\s*(.+?)\s*", s) + if m_simple: + left_txt, right_txt = m_simple.group(1), m_simple.group(3) + operand1_obj = parse_reporter_or_value(unparen(left_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + operand2_obj = parse_reporter_or_value(unparen(right_txt), parent_key, pick_key_func, all_generated_blocks) # Pass parent_key + op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'} + + inputs = {"OPERAND1": operand1_obj, "OPERAND2": operand2_obj} + block_id = _register_block(op_map[m_simple.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + if operand1_obj.get("kind") == "block": all_generated_blocks[operand1_obj["block"]]["parent"] = block_id + if operand2_obj.get("kind") == "block": all_generated_blocks[operand2_obj["block"]]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 4) Contains: <[list v] contains [item]?> + #m = re.search(r"\[([^\]]+)\s*v\] contains \[(.+?)\]\?", s) + m = re.fullmatch(r"\s*\[(.+?)\]\s+contains\s+\[(.+?)\]\?\s*", s) + if m: + list_name = m.group(1).strip() + item_val = {"kind": "value", "value": m.group(2).strip()} # Item can be a value or a block + + # Create the data_list reporter block + list_block_id = _register_block("data_list", parent_key, True, pick_key_func, all_generated_blocks, fields={"LIST": [list_name, None]}) # Pass parent_key + + inputs = {"LIST": {"kind": "block", "block": list_block_id}, "ITEM": item_val} + block_id = _register_block("data_listcontainsitem", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + all_generated_blocks[list_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 5) Touching object: + m_touch = re.fullmatch(r""" + \s* # leading space + (?:<\s*)? # optional '<' + touching # literal + \s*\[\s* + (?P[^\]]+?) # capture the sprite name + \s*v\]\? # close the [sprite v]? + (?:\s*>)? # optional '>' + """, s, re.IGNORECASE | re.VERBOSE) + if m_touch: + sprite = m_touch.group('sprite').strip() + val = {'mouse-pointer':'_mouse_', 'edge':'_edge_'}.get(sprite, sprite) + + mid = _register_block( + "sensing_touchingobjectmenu", parent_key, True, pick_key_func, all_generated_blocks, # Pass parent_key + fields={"TOUCHINGOBJECTMENU":[val, None]} + ) + bid = _register_block( + "sensing_touchingobject", parent_key, False, pick_key_func, all_generated_blocks, # Pass parent_key + inputs={"TOUCHINGOBJECTMENU":[1, mid]} + ) + all_generated_blocks[mid]["parent"] = bid + return {"kind":"block","block":bid} + + # 6) Touching color: + m = re.search(r"touching color \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR": [1, [9, m.group(1)]]} # Color input is special, often a list [type, value] + block_id = _register_block("sensing_touchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + return {"kind": "block", "block": block_id} + + # 7) Color is touching color: + m = re.search(r"color \[(#[0-9A-Fa-f]{6})\] is touching \[(#[0-9A-Fa-f]{6})\]\?", s) + if m: + inputs = {"COLOR1": [1, [9, m.group(1)]], "COLOR2": [1, [9, m.group(2)]]} + block_id = _register_block("sensing_coloristouchingcolor", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + return {"kind": "block", "block": block_id} + + # 8) Key pressed: + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", s) + if m: + option = m.group(1).strip() + menu_block_id = _register_block("sensing_keyoptions", parent_key, True, pick_key_func, all_generated_blocks, fields={"KEY_OPTION": [option, None]}) # Pass parent_key + + inputs = {"KEY_OPTION": [1, menu_block_id]} + block_id = _register_block("sensing_keypressed", parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs) # Pass parent_key + all_generated_blocks[menu_block_id]["parent"] = block_id + return {"kind": "block", "block": block_id} + + # 9) Mouse down?: mouse down? + if s == "mouse down?": + block_id = _register_block("sensing_mousedown", parent_key, False, pick_key_func, all_generated_blocks) # Pass parent_key + return {"kind": "block", "block": block_id} + + val_obj = parse_reporter_or_value(unparen(stmt), parent_key, pick_key_func, all_generated_blocks) + if val_obj: + return val_obj + + raise ValueError(f"Can't parse condition: {stmt}") + +def classify(line): + """ + Classifies a pseudo-code line into its corresponding Scratch opcode and block type. + Order of checks matters: more specific patterns should come before more general ones. + """ + l = line.lower().strip() + + # Ignore comments + if l.startswith("//"): return None, None + + # Hat Blocks (most specific first) + if re.match(r"when green flag click(ed)?", l): return "event_whenflagclicked", "hat" + if re.match(r"when (.+?) key press(ed)?", l): return "event_whenkeypressed", "hat" + if re.match(r"when this sprite click(ed)?", l): return "event_whenthisspriteclicked", "hat" + if l.startswith("when backdrop switches to"): return "event_whenbackdropswitchesto", "hat" + if l.startswith("when ") and " > (" in l: return "event_whengreaterthan", "hat" + if l.startswith("when i receive"): return "event_whenbroadcastreceived", "hat" + if re.match(r"when i start as a clo(ne)?", l): return "control_start_as_clone", "hat" + if l.startswith("define "): return "procedures_definition", "hat" + if l.startswith("procedure "): return "procedures_definition", "hat" # For "procedure moveBall" + + # Motion Blocks + if l.startswith("go to x:"): return "motion_gotoxy", "stack" + # IMPORTANT: More specific glide block before less specific one + if l.startswith("glide ") and " secs to x:" in l: return "motion_glidesecstoxy", "stack" + if l.startswith("glide ") and " secs to " in l: return "motion_glideto", "stack" + if l.startswith("move "): return "motion_movesteps", "stack" + if l.startswith("turn right "): return "motion_turnright", "stack" + if l.startswith("turn left "): return "motion_turnleft", "stack" + if l.startswith("go to "): return "motion_goto", "stack" + if l.startswith("point in direction"): return "motion_pointindirection", "stack" + if l.startswith("point towards"): return "motion_pointtowards", "stack" + if l.startswith("change x by"): return "motion_changexby", "stack" + if re.match(r"set x to\s*\(.+\)", l): return "motion_setx", "stack" # Specific for set x + if l.startswith("change y by"): return "motion_changeyby", "stack" + if re.match(r"set y to\s*\(.+\)", l): return "motion_sety", "stack" # Specific for set y + #if re.match(r"if on edge, bounce( off edge)?", l): return "motion_ifonedgebounce", "stack" + if re.match(r"if on edge,\s*bounc(e)?(\s+off\s+edge)?", l.strip(), re.IGNORECASE): return "motion_ifonedgebounce", "stack" + if re.match(r"bounce off edg(e)?", l): return "motion_ifonedgebounce", "stack" # Alias + if l.startswith("set rotation style"): return "motion_setrotationstyle", "stack" + + + # Looks Blocks + if l.startswith("say ") and " for " in l: return "looks_sayforsecs", "stack" + if l.startswith("say "): return "looks_say", "stack" + if l.startswith("think ") and " for " in l: return "looks_thinkforsecs", "stack" + if l.startswith("think "): return "looks_think", "stack" + if l.startswith("switch costume to"): return "looks_switchcostumeto", "stack" + if re.match(r"next costum(e)?", l): return "looks_nextcostume", "stack" + if l.startswith("switch backdrop to ") and " and wait" in l: return "looks_switchbackdroptowait", "stack" + if l.startswith("switch backdrop to"): return "looks_switchbackdropto", "stack" + if l == "next backdrop": return "looks_nextbackdrop", "stack" + if l.startswith("change size by"): return "looks_changesizeby", "stack" + if l.startswith("set size to"): return "looks_setsizeto", "stack" + # Updated regex for change/set effect by/to + if re.match(r"change\s*(\[.+?v\]|\(.+?\))?\s*effect by", l): return "looks_changeeffectby", "stack" + if re.match(r"set\s*(\[.+?v\]|\(.+?\))?\s*effect to", l): return "looks_seteffectto", "stack" + if l == "clear graphic effects": return "looks_cleargraphiceffects", "stack" + if l == "show": return "looks_show", "stack" + if l == "hide": return "looks_hide", "stack" + if l.startswith("go to ") and " layer" in l: return "looks_gotofrontback", "stack" + if l.startswith("go ") and " layers" in l: return "looks_goforwardbackwardlayers", "stack" + + # Sound Blocks + if re.match(r"play sound (.+?) until do(ne)?", l): return "sound_playuntildone", "stack" + if l.startswith("start sound "): return "sound_play", "stack" + if l == "stop all sounds": return "sound_stopallsounds", "stack" + if l.startswith("change volume by"): return "sound_changevolumeby", "stack" + if l.startswith("set volume to"): return "sound_setvolumeto", "stack" + + # Event Blocks (broadcasts) + if l.startswith("broadcast ") and " and wait" in l: return "event_broadcastandwait", "stack" + if l.startswith("broadcast "): return "event_broadcast", "stack" + + # Control Blocks + if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack" + if l.startswith("wait until <"): return "control_wait_until", "stack" + if l.startswith("repeat ("): return "control_repeat", "c_block" + if l == "forever": return "control_forever", "c_block" + if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block" + if l.startswith("if <"): return "control_if", "c_block" + if l.startswith("repeat until <"): return "control_repeat_until", "c_block" + # Updated regex for stop block to handle different options + if re.match(r"stop \[(all|this script|other scripts in sprite)\s*v\]", l): return "control_stop", "cap" + if l.startswith("create clone of"): return "control_create_clone_of", "stack" + if l == "delete this clone": return "control_delete_this_clone", "cap" + + # Data Blocks + if l.startswith("set [") and " to " in l: return "data_setvariableto", "stack" + if l.startswith("change [") and " by " in l: return "data_changevariableby", "stack" + if l.startswith("show variable"): return "data_showvariable", "stack" + if l.startswith("hide variable"): return "data_hidevariable", "stack" + if l.startswith("add ") and " to [" in l: return "data_addtolist", "stack" + # Updated regex for delete of list + if re.match(r"delete \((.+?)\) of \[([^\]]+)\s*v\]", l): return "data_deleteoflist", "stack" + if l.startswith("delete all of [" ): return "data_deletealloflist", "stack" + if l.startswith("insert ") and " at " in l: return "data_insertatlist", "stack" + if l.startswith("replace item ") and " of [" in l: return "data_replaceitemoflist", "stack" + if l.startswith("show list"): return "data_showlist", "stack" + if l.startswith("hide list"): return "data_hidelist", "stack" + + # Sensing Blocks + if re.match(r"ask (.+?) and wai(t)?", l): return "sensing_askandwait", "stack" + if l == "reset timer": return "sensing_resettimer", "stack" + if l.startswith("set drag mode"): return "sensing_setdragmode", "stack" + + # Custom Blocks (procedures_call) - specific rule for "call" + if l.startswith("call "): + return "procedures_call", "stack" + + # Custom Blocks (procedures_call) - LAST RESORT (generic match) + # This should be the very last check for stack-type blocks to avoid conflicts. + # It tries to match anything that looks like a function call with or without arguments. + custom_block_match = re.match(r"([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", l) + if custom_block_match: + # Before returning, ensure it's not a known simple reporter or variable name + # that might have been missed or is being used standalone. + # This is a heuristic; a full parser would be more robust. + potential_name = custom_block_match.group(1).strip() + if potential_name not in ["x position", "y position", "direction", "mouse x", "mouse y", "loudness", "timer", "days since 2000", "username", "answer", "size", "volume"] and \ + not re.fullmatch(r"\[[^\]]+\]", potential_name) and \ + not re.fullmatch(r"\[[^\]]+\]\s*v", potential_name): + return "procedures_call", "stack" + + + raise ValueError(f"Unknown statement: {line!r}") + +def generate_plan(generated_input, opcode_keys, pseudo_code): + """ + Build a nested “plan” tree from: + • generated_input: dict of block_key -> block_data (pre-generated block definitions) + • opcode_keys: dict of opcode -> list of block_keys (in order) + • pseudo_code: a multiline string, indented with two‑space levels + + Returns: + { "flow": [ ... list of block dictionaries ... ] } + """ + # helper: pick next unused block_key for an opcode + ptrs = defaultdict(int) + def pick_key(opcode): + lst = opcode_keys.get(opcode, []) + idx = ptrs[opcode] + if idx >= len(lst): + # Fallback: if no more pre-generated keys, create a new one. + ptrs[opcode] += 1 + return f"{opcode}_{idx + 1}" + ptrs[opcode] += 1 + return lst[idx] + + all_generated_blocks = {} # Initialize as empty, blocks will be added as they are parsed + + # Stack stores (indent, owner_block_id, last_block_in_current_linear_chain_id) + # owner_block_id: The ID of the C-block or Hat block that owns the current substack. + # last_block_in_current_linear_chain_id: The ID of the last block added to the *current linear sequence* within this substack. + stack = [(-1, None, None)] # Sentinel: (indent, owner_block_id, last_block_in_current_linear_chain_id) + + top_level_script_keys = [] + + lines = pseudo_code.splitlines() + i = 0 + while i < len(lines): + raw_line = lines[i] + stripped_line = raw_line.strip() + + # Skip empty lines and comments + if not stripped_line or stripped_line.startswith("//"): + i += 1 + continue + + current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2 + + # Handle 'else' and 'end' first, as they control scope + if stripped_line.lower() == "else": + # Pop the 'then' substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # The 'if-else' block (popped_owner_key) is the owner. + # Push a new scope for the 'else' substack, with the same owner. + stack.append((current_indent, popped_owner_key, None)) # New scope for 'else' part, no last block yet + i += 1 + continue + + if stripped_line.lower() == "end": + # Pop the current substack's scope + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + # If the substack was empty, ensure the input is [2, None]. + if popped_owner_key: + owner_block = all_generated_blocks[popped_owner_key] + if owner_block["block_shape"] == "C-Block" or owner_block["op_code"] == "procedures_definition": + if owner_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in owner_block["inputs"] and \ + owner_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in owner_block["inputs"]: + # If SUBSTACK is already set, this 'end' closes the SUBSTACK2 (else part) + if not owner_block["inputs"].get("SUBSTACK2"): # Only set if not already set by a block + owner_block["inputs"]["SUBSTACK2"] = [2, None] + elif not owner_block["inputs"].get("SUBSTACK"): # Only set if not already set by a block + owner_block["inputs"]["SUBSTACK"] = [2, None] + + i += 1 + continue + + # Adjust stack based on indentation for regular blocks + # Pop scopes whose indentation is greater than or equal to the current line's indentation + while len(stack) > 1 and stack[-1][0] >= current_indent: + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None # Terminate the chain + + # Get the current active scope from the stack + current_scope_indent, current_owner_block_id, last_block_in_current_chain = stack[-1] + + # Classify the statement and create the block + stmt_for_parse = stripped_line.rstrip("then").strip() + opcode, ntype = classify(stmt_for_parse) + + if opcode is None: # Should not happen if classify is robust + i += 1 + continue + + # Create the new block (and register it in all_generated_blocks) + # _register_block now only sets parent for shadow/input blocks; main block parent/next/topLevel set here. + key = _register_block(opcode, None, False, pick_key, all_generated_blocks) + info = all_generated_blocks[key] + + # Set parent, next, and topLevel for the main script blocks + if ntype == "hat": + info["parent"] = None + info["topLevel"] = True + top_level_script_keys.append(key) + # info["next"] = None # REMOVED: This was causing the hat block's 'next' to be None + # Push a new scope for the children of this hat block. + stack.append((current_indent, key, None)) # New scope: owner is this hat, no last block yet + else: # Stack block or C-block (that is part of a linear sequence) + if last_block_in_current_chain: + # This block's parent is the previous block in the chain + info["parent"] = last_block_in_current_chain + all_generated_blocks[last_block_in_current_chain]["next"] = key + else: + # This is the first block in a new linear chain (e.g., first block inside a forever loop) + # Its parent is the owner of the current scope (the C-block or Hat block) + info["parent"] = current_owner_block_id + + # If the owner is a C-block or procedure definition, link its SUBSTACK input + if current_owner_block_id and (all_generated_blocks[current_owner_block_id]["block_shape"] == "C-Block" or all_generated_blocks[current_owner_block_id]["op_code"] == "procedures_definition"): + owner_block = all_generated_blocks[current_owner_block_id] + if owner_block["op_code"] == "control_if_else" and \ + "SUBSTACK" in owner_block["inputs"] and \ + owner_block["inputs"]["SUBSTACK"][1] is not None and \ + "SUBSTACK2" not in owner_block["inputs"]: + owner_block["inputs"]["SUBSTACK2"] = [2, key] + else: + owner_block["inputs"]["SUBSTACK"] = [2, key] + elif current_owner_block_id and all_generated_blocks[current_owner_block_id]["block_shape"] == "Hat Block": + # If the owner is a Hat block, this is its first child + all_generated_blocks[current_owner_block_id]["next"] = key + + info["topLevel"] = False + info["next"] = None # Default, will be overwritten if there's a next block + + # If it's a C-block or define block, it also starts a new inner scope + if ntype == "c_block" or opcode == "procedures_definition": + # Update the current scope's last_block_in_current_chain to this C-block + stack[-1] = (current_scope_indent, current_owner_block_id, key) + # Push a new scope for the C-block's substack + stack.append((current_indent, key, None)) # New scope: owner is this C-block, no last block yet + else: + # For regular stack blocks, just update the last_block_in_current_chain for the current scope + stack[-1] = (current_scope_indent, current_owner_block_id, key) + + # Parse inputs and fields (this part remains largely the same, but ensure parse_reporter_or_value/parse_condition + # are passed the *newly created block's ID* as the parent_key for nested inputs) + # Numeric inputs (e.g., move (10) steps, wait (1) seconds) + if opcode == "motion_movesteps": + m = re.search(r"move\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*steps", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["STEPS"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_turnright" or opcode == "motion_turnleft": + m = re.search(r"turn\s*(?:right|left)?\s*\(.*?\)\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*degrees", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DEGREES"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "motion_gotoxy": + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_glidesecstoxy": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs", stmt_for_parse, re.IGNORECASE) + m_x = re.search(r"x:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + m_y = re.search(r"y:\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m_secs: info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + if m_x: info["inputs"]["X"] = {"kind": "value", "value": float(m_x.group(1)) if '.' in m_x.group(1) else int(m_x.group(1))} + if m_y: info["inputs"]["Y"] = {"kind": "value", "value": float(m_y.group(1)) if '.' in m_y.group(1) else int(m_y.group(1))} + elif opcode == "motion_pointindirection": + m = re.search(r"direction\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DIRECTION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_changexby", "motion_changeyby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DX" if opcode == "motion_changexby" else "DY"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["motion_setx", "motion_sety"]: + m = re.search(r"(?:set x to|set y to)\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["X" if opcode == "motion_setx" else "Y"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_changesizeby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_setsizeto": + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*%", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SIZE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_changeeffectby", "sound_changeeffectby"]: + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["CHANGE" if opcode == "looks_changeeffectby" else "VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode in ["looks_seteffectto", "sound_setvolumeto"]: + m = re.search(r"to\s*\(\s*(-?\d+(\.\d+)?)\s*\)(?:\s*%)?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE" if opcode == "looks_seteffectto" else "VOLUME"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*(?:forward|backward)\s*\(\s*(-?\d+)\s*\)\s*layers", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["NUM"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "control_wait": + m = re.search(r"wait\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["DURATION"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "control_repeat": + m = re.search(r"repeat\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["TIMES"] = {"kind": "value", "value": int(m.group(1))} + elif opcode == "data_changevariableby": + m = re.search(r"by\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + elif opcode == "data_deleteoflist": + m = re.search(r"delete\s*\((.+?)\)\s*of", stmt_for_parse, re.IGNORECASE) + if m: + val_str = m.group(1).strip() + if val_str.isdigit(): + info["inputs"]["INDEX"] = {"kind": "value", "value": int(val_str)} + else: # "all", "last", "random" + info["inputs"]["INDEX"] = {"kind": "menu", "option": val_str} + elif opcode == "data_insertatlist": + m_item = re.search(r"insert\s*\[([^\]]+)\]\s*at", stmt_for_parse, re.IGNORECASE) + m_index = re.search(r"at\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + elif opcode == "data_replaceitemoflist": + m_index = re.search(r"replace item\s*\(\s*(-?\d+)\s*\)\s*of", stmt_for_parse, re.IGNORECASE) + m_item = re.search(r"with\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m_index: info["inputs"]["INDEX"] = {"kind": "value", "value": int(m_index.group(1))} + if m_item: info["inputs"]["ITEM"] = {"kind": "value", "value": m_item.group(1).strip()} + elif opcode == "event_whengreaterthan": + m = re.search(r">\s*\(\s*(-?\d+(\.\d+)?)\s*\)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["VALUE"] = {"kind": "value", "value": float(m.group(1)) if '.' in m.group(1) else int(m.group(1))} + + # String inputs + elif opcode == "looks_sayforsecs": + m = re.search(r"say\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_say": + m = re.search(r"say\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = parse_reporter_or_value(m.group(1).strip(), key, pick_key, all_generated_blocks) + elif opcode == "looks_thinkforsecs": + m = re.search(r"think\s*\[([^\]]+)\]\s*for\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*seconds", stmt_for_parse, re.IGNORECASE) + if m: + info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + info["inputs"]["SECS"] = {"kind": "value", "value": float(m.group(2)) if '.' in m.group(2) else int(m.group(2))} + elif opcode == "looks_think": + m = re.search(r"think\s*\[([^\]]+)\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["MESSAGE"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "sensing_askandwait": + m = re.search(r"ask\s*\[([^\]]+)\]\s*and wait", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["QUESTION"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_addtolist": + m = re.search(r"add\s*\[([^\]]+)\]\s*to", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["ITEM"] = {"kind": "value", "value": m.group(1).strip()} + elif opcode == "data_setvariableto": + m_var = re.search(r"set\s*\[([^\]]+)\s*v\]\s*to\s*(.+)", stmt_for_parse, re.IGNORECASE) + if m_var: + var_name = m_var.group(1).strip() + value_str = m_var.group(2).strip() + info["fields"]["VARIABLE"] = [var_name, None] + info["inputs"]["VALUE"] = parse_reporter_or_value(value_str, key, pick_key, all_generated_blocks) + + # Dropdown/Menu inputs + elif opcode == "motion_goto": + m = re.search(r"go to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_glideto": + m_secs = re.search(r"glide\s*\(\s*(-?\d+(\.\d+)?)\s*\)\s*secs to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m_secs: + info["inputs"]["SECS"] = {"kind": "value", "value": float(m_secs.group(1)) if '.' in m_secs.group(1) else int(m_secs.group(1))} + option = m_secs.group(2).strip() + if option == "random position": option = "_random_" + elif option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TO"] = {"kind": "menu", "option": option} + elif opcode == "motion_pointtowards": + m = re.search(r"point towards\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + info["inputs"]["TOWARDS"] = {"kind": "menu", "option": option} + elif opcode == "sensing_keypressed": # For boolean block + m = re.search(r"key \[([^\]]+)\s*v\] pressed\?", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["KEY_OPTION"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "sensing_touchingobject": # For boolean block + m = re.search(r"touching \[([^\]]+)\s*v\]\?", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "mouse-pointer": option = "_mouse_" + elif option == "edge": option = "_edge_" + info["inputs"]["TOUCHINGOBJECTMENU"] = {"kind": "menu", "option": option} + elif opcode == "control_create_clone_of": + m = re.search(r"create clone of\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: + option = m.group(1).strip() + if option == "myself": option = "_myself_" + info["inputs"]["CLONE_OPTION"] = {"kind": "menu", "option": option} + elif opcode in ["sound_playuntildone", "sound_play"]: + m = re.search(r"(?:play sound|start sound)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["SOUND_MENU"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "looks_switchcostumeto": + m = re.search(r"switch costume to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["COSTUME"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["looks_switchbackdropto", "looks_switchbackdroptowait"]: + m = re.search(r"switch backdrop to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BACKDROP"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode in ["event_broadcast", "event_broadcastandwait"]: + m = re.search(r"broadcast\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["inputs"]["BROADCAST_INPUT"] = {"kind": "menu", "option": m.group(1).strip()} + elif opcode == "event_whenbroadcastreceived": + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info.setdefault("fields", {})["BROADCAST_OPTION"] = [m.group(1).strip(), None] + + # Conditional inputs (Boolean blocks) + elif opcode in ["control_if", "control_if_else", "control_wait_until", "control_repeat_until"]: + #cond_match = re.search(r"<(.*?)>", stmt_for_parse) + cond_match = extract_condition_balanced(stmt_for_parse) + print(f"[THE CONDA MATCH]------------->{cond_match}") + if cond_match: + # Pass current block's key as parent for nested condition + info["inputs"]["CONDITION"] = parse_condition(cond_match.strip(), key, pick_key, all_generated_blocks) + elif opcode in ["operator_and", "operator_or", "operator_not", "operator_contains", + "sensing_touchingcolor", "sensing_coloristouchingcolor", "sensing_mousedown"]: + # These are handled by parse_condition directly, which returns the nested structure + # No need to re-parse inputs here, as they are part of the condition structure + pass # Inputs are set when parse_condition is called for the parent block + + + # Fields parsing + if "VARIABLE" in info["fields"]: + m = re.search(r"\[([^\]]+)\s*v\]", stmt_for_parse) + if m: + var_name = m.group(1).strip() + info["fields"]["VARIABLE"] = [var_name, None] + if "LIST" in info["fields"]: + m = re.search(r"(?:to|of|in)\s*\[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["LIST"] = [m.group(1), None] + if "STOP_OPTION" in info["fields"]: + m = re.search(r"stop \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STOP_OPTION"] = [m.group(1), None] + if "STYLE" in info["fields"]: + m = re.search(r"set rotation style \[([^\]]+)\s*v\]", stmt_for_parse) + if m: info["fields"]["STYLE"] = [m.group(1), None] + if "DRAG_MODE" in info["fields"]: + m = re.search(r"set drag mode \[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["DRAG_MODE"] = [m.group(1), None] + if "EFFECT" in info["fields"] and opcode in ["looks_changeeffectby", "looks_seteffectto", "sound_changeeffectby", "sound_seteffectto"]: + m = re.search(r"(?:change|set)\s*\[([^\]]+)\s*v\] effect", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["EFFECT"] = [m.group(1).upper(), None] + if "NUMBER_NAME" in info["fields"] and opcode in ["looks_costumenumbername", "looks_backdropnumbername"]: + m = re.search(r"(?:costume|backdrop)\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["NUMBER_NAME"] = [m.group(1), None] + if "FRONT_BACK" in info["fields"] and opcode == "looks_gotofrontback": + m = re.search(r"go to\s*\[([^\]]+)\s*v\] layer", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FRONT_BACK"] = [m.group(1), None] + if "FORWARD_BACKWARD" in info["fields"] and opcode == "looks_goforwardbackwardlayers": + m = re.search(r"go\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["FORWARD_BACKWARD"] = [m.group(1), None] + if "OPERATOR" in info["fields"] and opcode == "operator_mathop": + m = re.search(r"\[([^\]]+)\s*v\] of", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["OPERATOR"] = [m.group(1).upper(), None] + if "CURRENTMENU" in info["fields"] and opcode == "sensing_current": + m = re.search(r"current\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["CURRENTMENU"] = [m.group(1).upper(), None] + if "PROPERTY" in info["fields"] and opcode == "sensing_of": + m = re.search(r"\((.+?)\) of", stmt_for_parse, re.IGNORECASE) + if m: + prop = m.group(1).strip() + prop_map = { + "x position": "x position", "y position": "y position", "direction": "direction", + "costume #": "costume number", "costume name": "costume name", "size": "size", + "volume": "volume", "backdrop #": "backdrop number", "backdrop name": "backdrop name" + } + info["fields"]["PROPERTY"] = [prop_map.get(prop, prop), None] + if "WHENGREATERTHANMENU" in info["fields"] and opcode == "event_whengreaterthan": + m = re.search(r"when\s*\[([^\]]+)\s*v\] >", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["WHENGREATERTHANMENU"] = [m.group(1).upper(), None] + if "KEY_OPTION" in info["fields"] and opcode == "event_whenkeypressed": # For event_whenkeypressed hat block's field + m = re.search(r"when\s*\[([^\]]+)\s*v\] key pressed", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["KEY_OPTION"] = [m.group(1), None] + if "BACKDROP" in info["fields"] and opcode == "event_whenbackdropswitchesto": # For event_whenbackdropswitchesto hat block's field + m = re.search(r"when backdrop switches to\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BACKDROP"] = [m.group(1), None] + if "BROADCAST_OPTION" in info["fields"] and opcode == "event_whenbroadcastreceived": # For event_whenbroadcastreceived hat block's field + m = re.search(r"when i receive\s*\[([^\]]+)\s*v\]", stmt_for_parse, re.IGNORECASE) + if m: info["fields"]["BROADCAST_OPTION"] = [m.group(1), None] + + # Custom block specific parsing + if opcode == "procedures_definition": + proc_def_match = re.match(r"(?:define|procedure)\s+([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))?", stmt_for_parse, re.IGNORECASE) + if proc_def_match: + proc_name = proc_def_match.group(1).strip() + args_str = proc_def_match.group(2) + info["procedure_name"] = proc_name + info["is_custom_definition"] = True + + # Create the special mutation block for the definition + mutation_block = { + "tagName": "mutation", + "children": [], + "proccode": proc_name, + "argumentids": [], + "argumentnames": [], + "argumentdefaults": [], + "warp": False # Assuming non-warp by default + } + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for arg in args: + arg_id = f"%s" # Scratch uses %s for string args, %n for number args + # For simplicity, we'll just use a generic ID for now, or match Scratch's pattern + # For the plan, we just need the names and order. + mutation_block["argumentids"].append(arg_id) + mutation_block["argumentnames"].append(arg) + mutation_block["argumentdefaults"].append("") + + info["mutation"] = mutation_block + + elif opcode == "procedures_call": + call_match = re.match(r"(?:call\s+)?([a-zA-Z_][a-zA-Z0-9_ ]*)(?:\s*\((.+?)\))*\s*$", stmt_for_parse, re.IGNORECASE) + if call_match: + custom_block_name = call_match.group(1).strip() + args_str = call_match.group(2) + info["custom_block_name"] = custom_block_name + + info["mutation"] = { + "tagName": "mutation", + "children": [], + "proccode": custom_block_name, + "argumentids": [], + "argumentnames": [], + "warp": False + } + + if args_str: + args = [arg.strip() for arg in args_str.split(',')] + for idx, arg_val_str in enumerate(args): + arg_input_name = f"argument_name_{idx+1}" + info["mutation"]["argumentids"].append(arg_input_name) # Use the input name as argument ID + info["mutation"]["argumentnames"].append(f"arg{idx+1}") # Placeholder name for mutation + + info["inputs"][arg_input_name] = parse_reporter_or_value(arg_val_str, key, pick_key, all_generated_blocks) # Pass current block's key + + i += 1 # Move to the next line + + # Final pass to ensure last blocks have next: None (already handled by stack pops) + # The build_script_flow function will correctly traverse the linked list. + while len(stack) > 1: # Keep the initial sentinel + popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop() + if popped_last_block_in_chain: + all_generated_blocks[popped_last_block_in_chain]["next"] = None + + print(f"[ALL OPCODE BLCOKS KEY 2]: {all_generated_blocks}") + with open("all_generated_blocks.json", "w") as f: + json.dump(all_generated_blocks, f, indent=2) + # Construct the final flow output based on the collected top-level keys + # Recursively build the block structure for each top-level script + def build_script_flow(current_block_key, visited=None): + if visited is None: + visited = set() + + script_flow = [] + current_iter_key = current_block_key + + while current_iter_key: + # Detect cyclic reference + if current_iter_key in visited: + script_flow.append({ + "block_key": current_iter_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected, stopping recursion" + }) + break + + visited.add(current_iter_key) + block = all_generated_blocks.get(current_iter_key) + if not block: + break # Should not happen if keys are correct + + output_block = { + "block_key": block["id"], + "opcode": block["op_code"], + "type": block["block_shape"].replace(" Block", "").lower().replace("c-", "c_"), + "inputs": {}, + "fields": {}, + "shadow": block.get("shadow"), + "topLevel": block.get("topLevel"), + "parent": block.get("parent"), + "next": block.get("next") + } + + # Handle all input types + for inp_name, inp_val in block.get("inputs", {}).items(): + if inp_name in ["SUBSTACK", "SUBSTACK2"]: + if inp_val and len(inp_val) > 1 and inp_val[1] in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(inp_val[1], visited.copy()) + else: + output_block["inputs"][inp_name] = [] + elif inp_name == "PROCCONTAINER" and block.get("is_custom_definition"): + output_block["inputs"][inp_name] = inp_val + elif isinstance(inp_val, dict) and inp_val.get("kind") == "block": + # Recursively build nested reporter/boolean blocks + nested_block_key = inp_val["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val # Keep original if not found (shouldn't happen) + elif isinstance(inp_val, dict) and inp_val.get("kind") == "nested_reporter": + nested_block_key = inp_val["reporter"]["block"] + if nested_block_key in all_generated_blocks: + output_block["inputs"][inp_name] = build_script_flow(nested_block_key, visited.copy()) + else: + output_block["inputs"][inp_name] = inp_val + else: + output_block["inputs"][inp_name] = inp_val + + for field_name, field_val in block.get("fields", {}).items(): + output_block["fields"][field_name] = field_val + + if block.get("custom_block_name"): + output_block["custom_block_name"] = block["custom_block_name"] + + if block.get("procedure_name"): + output_block["procedure_name"] = block["procedure_name"] + output_block["is_custom_definition"] = True + if "mutation" in block: # Include mutation for custom definitions + output_block["mutation"] = block["mutation"] + + + script_flow.append(output_block) + + # Proceed to the next block in sequence + next_key = block.get("next") + if next_key in visited: + script_flow.append({ + "block_key": next_key, + "opcode": "control_stop", + "type": "statement", + "inputs": {}, + "fields": {}, + "comment": "Cycle detected in 'next' pointer" + }) + break + + current_iter_key = next_key + + return script_flow + + final_flow_output = [] + for key in top_level_script_keys: + final_flow_output.extend(build_script_flow(key)) + return all_generated_blocks + +# Example input with opcodes for the initial generation + +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + #{"opcode":"motion_goto","count":1}, + #{"opcode":"motion_glideto","count":1}, + #{"opcode":"motion_pointtowards","count":1}, + # {"opcode":"sensing_keypressed","count":1}, + # {"opcode":"control_if","count":2}, + # {"opcode":"control_create_clone_of","count":1}, + {"opcode":"sensing_of","count":1}, + {"opcode":"data_setvariableto","count":1} + # {"opcode":"looks_switchcostumeto","count":1}, + # {"opcode":"event_broadcast","count":1}, +] +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_xposition","count":1}, + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":2}, + {"opcode":"control_stop","count":1}, + {"opcode":"operator_lt","count":1}, + {"opcode":"sensing_touchingobject","count":1}, + #{"opcode":"sensing_touchingobjectmenu","count":1}, + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":2}, + {"opcode":"data_showvariable","count":2}, +] +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) +with open("generated_output_json.json", "w") as f: + json.dump(generated_output_json, f, indent=2) + +pseudo_code_examples = [""" +when green flag clicked +set [othersprite v] to (([game over v]) of [bat v]) +""",] +#create clone of [myself v] +# broadcast [shoot v] +#glide (1) secs to ([random position v]) +#point towards [mouse-pointer v] +# go to [random position v] +# end + +#switch costume to [costume1 v] +# create clone of [myself v] + +pseudo_code_examples = [""" +when green flag clicked + go to x: (240) y: (-135) + set [score v] to (1) + set [speed v] to (1) + show variable [score v] + show variable [speed v] + forever + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end +end +""",] +txt="" +trace="" +# Process each example and print the plan +for i, pseudo_code_input in enumerate(pseudo_code_examples): + print(f"\n--- Processing Example {i+1} ---") + #print(pseudo_code_input.strip()) + try: + # Regenerate blocks and opcode_occurrences for each run to ensure fresh keys + # This is important because pick_key uses a defaultdict that persists state. + generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input) + print("------------\n\n") + print(json.dumps(plan, indent=2)) + txt += str(plan) + " \n" + except Exception as e: + #print(f"Error processing example: {e}") + import traceback + #traceback.print_exc() + trace += str(e) +" "+str(pseudo_code_input)+" \n" + +with open("all_analysis.txt", "w", encoding="utf-8") as f: + f.write(txt) + +with open("all_analysis_trace.txt", "w", encoding="utf-8") as f: + f.write(trace) diff --git a/utils/pseudo_exampls.txt b/utils/pseudo_exampls.txt new file mode 100644 index 0000000000000000000000000000000000000000..e9cf2db13c5f736ffb4c49e2799109ed0a75d0d3 --- /dev/null +++ b/utils/pseudo_exampls.txt @@ -0,0 +1,618 @@ +pseudo_code_examples = [ + # From motion_block.json + """ +when green flag clicked + go to x: (0) y: (0) + point in direction (90) + move (50) steps +end + """, + """ +when [right arrow v] key pressed + turn right (15) degrees +end + """, + """ +when this sprite clicked + go to [mouse-pointer v] + """, + """ +when green flag clicked + glide (2) secs to x: (150) y: (-100) + glide (2) secs to x: (-150) y: (100) +end + """, + """ +when green flag clicked + forever + point towards [mouse-pointer v] + move (5) steps + end +end + """, + """ +when [right arrow v] key pressed + change x by (10) +end + """, + """ +when green flag clicked + set x to (0) + set y to (0) +end + """, + """ +when [up arrow v] key pressed + change y by (10) +end + """, + """ +when green flag clicked + forever + move (10) steps + if on edge, bounce + end +end + """, + """ +when green flag clicked + set rotation style [left-right v] + forever + move (10) steps + if on edge, bounce + end +end + """, + # From looks_block.json + """ +when green flag clicked + say [Grr] for (3) seconds + say [Have you seen my honey? v] for (3) seconds +end + """, + """ +when green flag clicked + say [Welcome to my game! v] + wait (2) seconds + say [] +end + """, + """ +when this sprite clicked + think [What should I do? v] for (2) seconds +end + """, + """ +when I receive [correct answer v] + think [That's right! v] + wait (1) seconds + think [good v] +end + """, + """ +when I receive [explosion v] + repeat (5) + next costume + end + hide +end + """, + """ +when green flag clicked + forever + next costume + wait (0.2) seconds + end +end + """, + """ +when green flag clicked + switch backdrop to [start screen v] +end + """, + """ +broadcast [game over v] + switch backdrop to [game over v] and wait + stop [all v] +end + """, + """ +when [space v] key pressed + next backdrop +end + """, + """ +when green flag clicked + repeat (10) + change size by (5) + wait (0.1) seconds + end +end + """, + """ +when green flag clicked + set size to (50) % + wait (1) seconds + set size to (100) % +end + """, + """ +when green flag clicked + forever + change [color v] effect by (5) + wait (0.1) seconds + end +end + """, + """ +when green flag clicked + set [ghost v] effect to (75) +end + """, + """ +when green flag clicked + change [color v] effect by (50) + wait (2) seconds + clear graphic effects +end + """, + """ +when green flag clicked + hide +when I receive [start game v] + show +end + """, + """ +when green flag clicked + hide +end + """, + """ +when green flag clicked + go to [front v] layer +end + """, + """ +when this sprite clicked + go [forward v] (1) layers +end + """, + """ +say join [I am costume ] (costume [name v]) + """, + """ +say join [Current backdrop: ] (backdrop [name v]) for (2) seconds + """, + """ +set size to ( (size) + (10) ) + """, + # From sound_block.json + """ +when backdrop switches to [winning screen v] + play sound [fanfare v] until done + say [You won!] for (2) seconds +end + """, + """ +forever + play sound [Music v] until done +end + """, + """ +when this sprite clicked + start sound [Pop v] + change [score v] by (1) +end + """, + """ +when I receive [game over v] + stop all sounds +end + """, + """ +when [down arrow v] key pressed + change volume by (-5) +end + """, + """ +when green flag clicked + set volume to (50) % +end + """, + """ +say join [Current volume: ] (volume) + """, + # From event_block.json + """ +when green flag clicked + go to x: (0) y: (0) + say [Hello!] for (2) seconds +end + """, + """ +when [space v] key pressed + repeat (10) + change y by (10) + wait (0.1) seconds + change y by (-10) + end +end + """, + """ +when [right arrow v] key pressed + point in direction (90) + move (10) steps +end + """, + """ +when this sprite clicked + say [Ouch!] for (1) seconds + change [score v] by (-1) +end + """, + """ +when backdrop switches to [game over v] + stop [all v] +end + """, + """ +when [loudness v] > (70) + start sound [scream v] +end + """, + """ +when I receive [start game v] + show + go to x: (0) y: (0) +end + """, + """ +when I receive [game over v] + set score to 0 + stop [all v] +end + """, + """ +if then + broadcast [jump v] +end + """, + """ +broadcast [initialize sprites v] and wait + say [Game Started!] for (2) seconds + """, + # From control_block.json + """ +say [Hello!] for (1) seconds + wait (0.5) seconds + say [Goodbye!] for (1) seconds + """, + """ +when green flag clicked + repeat (10) + move (10) steps + wait (0.1) seconds + end +end + """, + """ +when green flag clicked + forever + move (5) steps + if on edge, bounce + end +end + """, + """ +forever + if then + stop [this script v] + end +end + """, + """ +if <(score) > (10)> then + say [You win!] for (2) seconds +else + say [Keep trying!] for (2) seconds +end + """, + """ +repeat until + move (5) steps +end + """, + """ +if <(health) = (0)> then + stop [all v] +end + """, + """ +when I start as a clone + wait until + delete this clone +end + """, + """ +when I start as a clone + go to x: (pick random -240 to 240) y: (pick random -180 to 180) + show + forever + move (10) steps + if on edge, bounce + end +end + """, + """ +when I start as a clone + wait (5) seconds + delete this clone +end + """, + """ +when green flag clicked + hide + forever + create clone of [myself v] + wait (1) seconds + end + """, + # From data_block.json + """ +when green flag clicked + set [score v] to (0) + set [player name v] to [Guest] +end + """, + """ +when this sprite clicked + change [score v] by (1) +end + """, + """ +when green flag clicked + add [apple] to [shopping list v] + add [banana] to [shopping list v] +end + """, + """ +when green flag clicked + delete (all) of [my list v] +end + """, + """ +insert [orange] at (2) of [fruits v] + """, + """ +replace item (1) of [colors v] with [blue] + """, + """ +when green flag clicked + show variable [score v] +end + """, + """ +when I receive [game over v] + hide variable [score v] +end + """, + """ +when green flag clicked + show list [shopping list v] +end + """, + """ +when I receive [game over v] + hide list [shopping list v] +end + """, + """ +say ([score v]) for (2) seconds + """, + """ +say ([my list v]) + """, + """ +say (item (2) of [myList v]) for 2 seconds + """, + """ +say join (length of [shopping list v]) [ items in the list.] + """, + """ +if <(item # of [Dog] in [myList v])> (0)> then + say join [Dog found at position ] (item # of [Dog] in [my list v]) +end + """, + # From reporter_blocks.json (some already covered by other categories) + """ +when green flag clicked + say (x position) for (2) seconds +end + """, + """ +set [worms v] to (y position) + """, + """ +when green flag clicked + say (direction) for (2) seconds +end + """, + """ +say join [I am costume ] (costume [name v]) + """, + """ +set size to ( (size) + (10) ) + """, + """ +say join [Current backdrop: ] (backdrop [name v]) for (2) seconds + """, + """ +say join [Current volume: ] (volume) + """, + """ +if <(distance to [Sprite2 v]) < (50)> then + say [Too close!] +end + """, + """ +ask [What is your name?] and wait + say join [Hello ] (answer) + """, + """ +go to x: (mouse x) y: (mouse y) + """, + """ +if <(mouse y) < (0)> then + say [Below center] +end + """, + """ +when green flag clicked + forever + if <(loudness) > (30)> then + start sound [pop v] +end + """, + """ +when green flag clicked + reset timer + wait (5) seconds + say join [Time elapsed: ] (timer) +end + """, + """ +set [other sprite X v] to ( (x position) of [Sprite2 v] ) + """, + """ +say join [The current hour is ] (current [hour v]) + """, + """ +say join [Days passed: ] (days since 2000) + """, + """ +say join [Hello, ] (username) + """, + """ +set [total v] to ( (number 1) + (number 2) ) + """, + """ +set [difference v] to ( (number 1) - (number 2) ) + """, + """ +set [area v] to ( (length) * (width) ) + """, + """ +set [average v] to ( (total score) / (number of students) ) + """, + """ +go to x: (pick random -240 to 240) y: (pick random -180 to 180) + """, + """ +say (join [Hello ][World!]) + """, + """ +say (letter (1) of [apple]) + """, + """ +say (length of [banana]) + """, + """ +if <([number v] mod (2) = (0))> then + say [Even number] +end + """, + """ +set [rounded score v] to (round (score)) + """, + """ +set [distance v] to ([sqrt v] of ( ( (x position) * (x position) ) + ( (y position) * (y position) ) )) + """, + # From boolean_blocks.json (conditions already covered by parse_condition) + """ +if <(score) < (10)> then + say [Keep trying!] +end + """, + """ +if <(answer) = (5)> then + say [Correct!] +end + """, + """ +if <([health v]) > (0)> then + move (10) steps +else + stop [all v] +end + """, + """ +if < and > then + say [You're clicking me!] +end + """, + """ +if < or > then + change x by (-10) +end + """, + """ +if > then + say [I'm safe!] +end + """, + """ +if <[answer] contains [yes]?> then + say [Great!] +end + """, + """ +if then + broadcast [Game Over v] +end + """, + """ +if then + bounce off edge +end + """, + """ +if then + change [health v] by (-1) +end + """, + """ +if then + say [Collision!] +end + """, + """ +forever + if then + broadcast [shoot v] + end +end + """, + """ +if then + go to mouse-pointer +end + """, + """ +if <[inventory v] contains [key]?> then + say [You have the key!] +end + """, + # Custom block example + """ +define jump (height) + change y by (height) + wait (0.5) seconds + change y by (0 - (height)) +end + +when green flag clicked + jump (50) +end + """ +] \ No newline at end of file diff --git a/utils/script_plan.py b/utils/script_plan.py new file mode 100644 index 0000000000000000000000000000000000000000..4abbf344f9ebaa514feccd3bba662f1c8b110b59 --- /dev/null +++ b/utils/script_plan.py @@ -0,0 +1,1449 @@ +import json +import copy +import re + +def generate_blocks_from_opcodes(opcode_counts, all_block_definitions): + """ + Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition. + It now correctly links parent and menu blocks using their generated unique keys, and handles various block categories. + It ensures that menu blocks are only generated as children of their respective parent blocks. + + Args: + opcode_counts (list): An array of objects, each with an 'opcode' and 'count' property. + Example: [{"opcode": "motion_gotoxy", "count": 1}] + all_block_definitions (dict): A comprehensive dictionary containing definitions for all block types. + + Returns: + tuple: A tuple containing: + - dict: A JSON object where keys are generated block IDs and values are the block definitions. + - dict: The opcode_occurrences dictionary for consistent unique key generation across functions. + """ + generated_blocks = {} + opcode_occurrences = {} # To keep track of how many times each opcode (main or menu) has been used for unique keys + + # Define explicit parent-menu relationships for linking purposes + # This maps main_opcode -> list of (input_field_name, menu_opcode) + explicit_menu_links = { + "motion_goto": [("TO", "motion_goto_menu")], + "motion_glideto": [("TO", "motion_glideto_menu")], + "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")], + "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")], + "sensing_of": [("OBJECT", "sensing_of_object_menu")], + "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")], + "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")], + "sound_play": [("SOUND_MENU", "sound_sounds_menu")], + "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")], + "looks_switchcostumeto": [("COSTUME", "looks_costume")], + "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")], + } + + # --- Step 1: Process explicitly requested opcodes and generate their instances and associated menus --- + for item in opcode_counts: + opcode = item.get("opcode") + count = item.get("count", 1) + + # Special handling for 'sensing_istouching' which maps to 'sensing_touchingobject' + if opcode == "sensing_istouching": + opcode = "sensing_touchingobject" + + if not opcode: + print("Warning: Skipping item with missing 'opcode'.") + continue + + if opcode not in all_block_definitions: + print(f"Warning: Opcode '{opcode}' not found in all_block_definitions. Skipping.") + continue + + for _ in range(count): + # Increment occurrence count for the current main opcode + opcode_occurrences[opcode] = opcode_occurrences.get(opcode, 0) + 1 + main_block_instance_num = opcode_occurrences[opcode] + + main_block_unique_key = f"{opcode}_{main_block_instance_num}" + + # Create a deep copy of the main block definition + main_block_data = copy.deepcopy(all_block_definitions[opcode]) + + # Set properties for a top-level main block + main_block_data["parent"] = None + main_block_data["next"] = None + main_block_data["topLevel"] = True + main_block_data["shadow"] = False # Main blocks are typically not shadows + + generated_blocks[main_block_unique_key] = main_block_data + + # If this main block has associated menus, generate and link them now + if opcode in explicit_menu_links: + for input_field_name, menu_opcode_type in explicit_menu_links[opcode]: + if menu_opcode_type in all_block_definitions: + # Increment the occurrence for the menu block type + opcode_occurrences[menu_opcode_type] = opcode_occurrences.get(menu_opcode_type, 0) + 1 + menu_block_instance_num = opcode_occurrences[menu_opcode_type] + + menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode_type]) + + # Generate a unique key for this specific menu instance + menu_unique_key = f"{menu_opcode_type}_{menu_block_instance_num}" + + # Set properties for a shadow menu block + menu_block_data["shadow"] = True + menu_block_data["topLevel"] = False + menu_block_data["next"] = None + menu_block_data["parent"] = main_block_unique_key # Link menu to its parent instance + + # Update the main block's input to point to this unique menu instance + if input_field_name in main_block_data.get("inputs", {}) and \ + isinstance(main_block_data["inputs"][input_field_name], list) and \ + len(main_block_data["inputs"][input_field_name]) > 1 and \ + main_block_data["inputs"][input_field_name][0] == 1: + + main_block_data["inputs"][input_field_name][1] = menu_unique_key + + generated_blocks[menu_unique_key] = menu_block_data + + return generated_blocks, opcode_occurrences + +def interpret_pseudo_code_and_update_blocks(generated_blocks_json, pseudo_code, all_block_definitions, opcode_occurrences): + """ + Interprets pseudo-code to update the generated Scratch blocks, replacing static values + with dynamic values and establishing stacking/nesting logic. + + Args: + generated_blocks_json (dict): The JSON object of pre-generated blocks. + pseudo_code (str): The pseudo-code string to interpret. + all_block_definitions (dict): A comprehensive dictionary containing definitions for all block types. + opcode_occurrences (dict): A dictionary to keep track of opcode occurrences for unique key generation. + + Returns: + dict: The updated JSON object of Scratch blocks. + """ + updated_blocks = copy.deepcopy(generated_blocks_json) + + # Helper to create a new block instance (used for shadows/nested blocks) + def create_block_instance_for_parsing(opcode, parent_key=None, is_shadow=False, is_top_level=False): + opcode_occurrences[opcode] = opcode_occurrences.get(opcode, 0) + 1 + unique_key = f"{opcode}_{opcode_occurrences[opcode]}" + + new_block = copy.deepcopy(all_block_definitions.get(opcode, {})) + if not new_block: + print(f"Error: Definition for opcode '{opcode}' not found when creating instance for parsing.") + return None, None + + new_block["parent"] = parent_key + new_block["next"] = None + new_block["topLevel"] = is_top_level + new_block["shadow"] = is_shadow + + # Initialize inputs/fields to default empty values, but preserve structure + if "inputs" in new_block: + for input_name, input_data in new_block["inputs"].items(): + if isinstance(input_data, list) and len(input_data) > 1: + if input_data[0] == 1: # Block reference + new_block["inputs"][input_name][1] = None # Placeholder for linked block ID + elif input_data[0] in [4, 5, 6, 7, 8, 9, 10]: # Literal types + new_block["inputs"][input_name][1] = "" # Default empty literal + if "fields" in new_block: + for field_name, field_data in new_block["fields"].items(): + if isinstance(field_data, list) and len(field_data) > 0: + new_block["fields"][field_name][0] = "" # Default empty field value + + updated_blocks[unique_key] = new_block + return unique_key, new_block + + # Helper to parse input values from pseudo-code and create nested blocks if necessary + def parse_and_link_input(parent_block_key, input_type, pseudo_value, input_name_in_parent=None, field_name_in_parent=None): + pseudo_value = pseudo_value.strip() + + # 1. Handle literal numbers (e.g., "2", "+1", "-135") + if re.fullmatch(r"[-+]?\d+(\.\d+)?", pseudo_value): + return [4, pseudo_value] # Type 4 for number literal + + # 2. Handle literal strings (e.g., "Game Over") - often in quotes or simply text + # If it's a broadcast message, it's typically a string literal. + # If it's a 'say' message, it's a string literal. + # If it's a variable name, it's handled by specific patterns later. + if pseudo_value.startswith('"') and pseudo_value.endswith('"'): + return [10, pseudo_value.strip('"')] # Type 10 for string literal + + # 3. Handle variable names (e.g., "[score v]", "[Sprite1 v]", "[Game Over v]") + # These can be fields or inputs that expect a variable reporter block. + var_match = re.match(r"\[(.+?) v\]", pseudo_value) + if var_match: + var_name = var_match.group(1).strip() + # If it's a field (like for set variable, show variable) + if field_name_in_parent: + return var_name # Return name, parent block will set its field + # If it's an input that expects a variable reporter (e.g., 'say (score)') + # Create a data_variable shadow block + var_reporter_key, var_reporter_data = create_block_instance_for_parsing( + "data_variable", parent_key=parent_block_key, is_shadow=True + ) + if var_reporter_key: + var_reporter_data["fields"]["VARIABLE"] = [var_name, f"`var_{var_name}"] # Placeholder ID + return [1, var_reporter_key] # Type 1 for block reference + else: + print(f"Warning: Could not create data_variable block for '{var_name}'") + return [10, var_name] # Fallback to string literal + + # 4. Handle nested reporter blocks (e.g., "(x position)") + reporter_match = re.match(r"\((.+?)\)", pseudo_value) + if reporter_match: + inner_content = reporter_match.group(1).strip() + # Check if it's a known reporter block (like "x position") + if inner_content in reporter_opcode_lookup: + reporter_opcode = reporter_opcode_lookup[inner_content] + reporter_block_key, reporter_block_data = create_block_instance_for_parsing( + reporter_opcode, parent_key=parent_block_key, is_shadow=True + ) + if reporter_block_key: + return [1, reporter_block_key] # Type 1 for block reference + else: + print(f"Warning: Could not create reporter block for '{inner_content}'") + return [10, inner_content] # Fallback to string literal + else: # It's a literal number or string inside parentheses + return parse_and_link_input(parent_block_key, input_type, inner_content) # Recurse for inner content + + # 5. Handle nested boolean blocks (e.g., "<(...) < (...)>") + boolean_match = re.match(r"<(.+?)>", pseudo_value) + if boolean_match: + inner_condition_str = boolean_match.group(1).strip() + # This is typically handled by the parent block's parsing (e.g., control_if) + # For now, if called directly, it implies a boolean reporter. + # We'll need specific logic for operator_lt, operator_and, etc. + # This part is complex and often handled by the parent block's specific regex. + # For this problem, the 'if' block's logic will create the boolean shadow. + print(f"Warning: Direct parsing of standalone boolean '{pseudo_value}' not fully supported here.") + return [10, pseudo_value] # Fallback to string literal + + # Default to string literal if no other pattern matches + return [10, pseudo_value] + + lines = [line.strip() for line in pseudo_code.strip().split('\n') if line.strip()] + + # Track the current script and nesting + current_script_head = None + block_stack = [] # Stores (parent_block_key, indent_level, last_child_key_in_scope) + + # Create a mapping from block name patterns to their opcodes and input/field details + # The 'input_map' keys are the internal Scratch input names, values are regex group indices. + # The 'field_map' keys are the internal Scratch field names, values are regex group indices. + # 'condition_opcode' is for 'if' blocks that take a specific boolean reporter. + pseudo_code_to_opcode_map = { + re.compile(r"when green flag clicked"): {"opcode": "event_whenflagclicked"}, + re.compile(r"go to x: \((.+?)\) y: \((.+?)\)"): {"opcode": "motion_gotoxy", "input_map": {"X": 0, "Y": 1}}, + re.compile(r"set \[(.+?) v\] to (.+)"): {"opcode": "data_setvariableto", "field_map": {"VARIABLE": 0}, "input_map": {"VALUE": 1}}, + re.compile(r"show variable \[(.+?) v\]"): {"opcode": "data_showvariable", "field_map": {"VARIABLE": 0}}, + re.compile(r"forever"): {"opcode": "control_forever"}, + re.compile(r"glide \((.+?)\) seconds to x: \((.+?)\) y: \((.+?)\)"): {"opcode": "motion_glidesecstoxy", "input_map": {"SECS": 0, "X": 1, "Y": 2}}, + re.compile(r"if <\((.+)\) < \((.+)\)> then"): {"opcode": "control_if", "condition_type": "operator_lt", "condition_input_map": {"OPERAND1": 0, "OPERAND2": 1}}, + re.compile(r"set x to \((.+?)\)"): {"opcode": "motion_setx", "input_map": {"X": 0}}, + re.compile(r"if then"): {"opcode": "control_if", "condition_type": "sensing_touchingobject", "condition_field_map": {"TOUCHINGOBJECTMENU": 0}}, + re.compile(r"broadcast \[(.+?) v\]"): {"opcode": "event_broadcast", "input_map": {"BROADCAST_INPUT": 0}}, + re.compile(r"stop \[(.+?) v\]"): {"opcode": "control_stop", "field_map": {"STOP_OPTION": 0}}, + re.compile(r"end"): {"opcode": "end_block"}, # Special marker for script end/C-block end + } + + # Create a reverse lookup for reporter block opcodes based on their pseudo-code representation + reporter_opcode_lookup = {} + for opcode, definition in all_block_definitions.items(): + if definition.get("block_shape") == "Reporter Block": + block_name = definition.get("block_name") + if block_name: + # Clean up block name for matching: remove parentheses, 'v' for variable, etc. + clean_name = block_name.replace("(", "").replace(")", "").replace("[", "").replace("]", "").replace(" v", "").strip() + reporter_opcode_lookup[clean_name] = opcode + # Add specific entries for common reporters if their pseudo-code differs from clean_name + if opcode == "motion_xposition": + reporter_opcode_lookup["x position"] = opcode + elif opcode == "motion_yposition": + reporter_opcode_lookup["y position"] = opcode + # Add more as needed based on pseudo-code patterns + + for line_idx, raw_line in enumerate(lines): + current_line_indent = len(raw_line) - len(raw_line.lstrip()) + line = raw_line.strip() + + # Adjust block_stack based on current indent level + while block_stack and current_line_indent <= block_stack[-1][1]: + block_stack.pop() + + matched_block_info = None + matched_values = None + + # Try to match the line against known block patterns + for pattern_regex, info in pseudo_code_to_opcode_map.items(): + match = pattern_regex.match(line) + if match: + matched_block_info = info + matched_values = match.groups() + break + + if not matched_block_info: + print(f"Warning: Could not interpret line: '{line}' at line {line_idx + 1}") + continue + + opcode = matched_block_info["opcode"] + + # Handle 'end' block separately as it signifies closing a C-block + if opcode == "end_block": + # This 'end' matches the most recent C-block on the stack. + # The while loop at the beginning of the iteration already handles popping. + continue + + parent_key = None + if block_stack: + parent_key = block_stack[-1][0] # The last block on the stack is the parent + + # Create the new block instance + new_block_key, new_block_data = create_block_instance_for_parsing( + opcode, + parent_key=parent_key, + is_top_level=(parent_key is None) + ) + if not new_block_key: + continue + + # Link to previous block in the same script/nesting level + if block_stack: + # Update the 'next' of the previous block in the current scope + last_child_key_in_scope = block_stack[-1][2] if len(block_stack[-1]) > 2 else None + if last_child_key_in_scope and last_child_key_in_scope in updated_blocks: + updated_blocks[last_child_key_in_scope]["next"] = new_block_key + + # Update the last child in the current scope + block_stack[-1] = (block_stack[-1][0], block_stack[-1][1], new_block_key) + + # Populate inputs and fields based on matched_block_info and matched_values + if matched_values: + # Handle fields + if "field_map" in matched_block_info: + for field_name, group_idx in matched_block_info["field_map"].items(): + pseudo_field_value = matched_values[group_idx].replace(' v', '').strip() + if field_name == "VARIABLE": + # For variable fields, the actual variable ID is often derived or generated. + # For now, we use a placeholder and the name. + new_block_data["fields"][field_name] = [pseudo_field_value, f"`var_{pseudo_field_value}"] + elif field_name == "STOP_OPTION": + new_block_data["fields"][field_name] = [pseudo_field_value, None] # No ID needed for dropdown option + else: + new_block_data["fields"][field_name][0] = pseudo_field_value + + # Handle inputs + if "input_map" in matched_block_info: + for input_name, group_idx in matched_block_info["input_map"].items(): + pseudo_input_value = matched_values[group_idx].strip() + + 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) + + if parsed_input_info: + if parsed_input_info[0] == 1: # It's a linked block (shadow reporter/boolean/menu) + new_block_data["inputs"][input_name][1] = parsed_input_info[1] # Link block ID + new_block_data["inputs"][input_name][0] = parsed_input_info[0] # Set type to 1 (block) + else: # It's a literal value + new_block_data["inputs"][input_name][1] = parsed_input_info[1] + new_block_data["inputs"][input_name][0] = parsed_input_info[0] # Set appropriate type (4 for number, 10 for string) + + # Special handling for 'if' block conditions + if opcode == "control_if": + condition_type = matched_block_info.get("condition_type") + if condition_type: + condition_block_key, condition_block_data = create_block_instance_for_parsing( + condition_type, parent_key=new_block_key, is_shadow=True + ) + if condition_block_key: + new_block_data["inputs"]["CONDITION"] = [2, condition_block_key] # Type 2 for boolean block reference + + # Populate inputs for the condition block (e.g., operator_lt) + if "condition_input_map" in matched_block_info: + for cond_input_name, cond_group_idx in matched_block_info["condition_input_map"].items(): + pseudo_cond_value = matched_values[cond_group_idx].strip() + parsed_cond_input_info = parse_and_link_input(condition_block_key, condition_block_data["inputs"][cond_input_name][0], pseudo_cond_value) + if parsed_cond_input_info: + if parsed_cond_input_info[0] == 1: + condition_block_data["inputs"][cond_input_name][1] = parsed_cond_input_info[1] + condition_block_data["inputs"][cond_input_name][0] = parsed_cond_input_info[0] + else: + condition_block_data["inputs"][cond_input_name][1] = parsed_cond_input_info[1] + condition_block_data["inputs"][cond_input_name][0] = parsed_cond_input_info[0] + + # Populate fields for the condition block (e.g., sensing_touchingobject's menu) + if "condition_field_map" in matched_block_info: + for cond_field_name, cond_group_idx in matched_block_info["condition_field_map"].items(): + pseudo_cond_field_value = matched_values[cond_group_idx].replace(' v', '').strip() + if cond_field_name == "TOUCHINGOBJECTMENU": + # Create the menu block for TOUCHINGOBJECTMENU + menu_opcode = "sensing_touchingobjectmenu" + menu_key, menu_data = create_block_instance_for_parsing( + menu_opcode, + parent_key=condition_block_key, + is_shadow=True + ) + if menu_key: + condition_block_data["inputs"]["TOUCHINGOBJECTMENU"] = [1, menu_key] # Link to menu block + menu_data["fields"]["TOUCHINGOBJECTMENU"] = [pseudo_cond_field_value, None] # Set menu value + else: + print(f"Warning: Could not create menu block for touching object: '{pseudo_cond_field_value}'") + else: + condition_block_data["fields"][cond_field_name][0] = pseudo_cond_field_value + else: + print(f"Warning: Could not create condition block '{condition_type}' for 'if' statement.") + + + # For C-blocks, push onto stack to track nesting + # 'control_if' is a C-block, but its 'next' is inside its substack. + # 'control_forever' is also a C-block. + if all_block_definitions[opcode].get("block_shape") == "C-Block" and opcode != "control_if": + # For C-blocks, the 'next' of the parent is usually null, and children start in 'SUBSTACK' + # We add the block to the stack, and its "next" will be its first child. + # The 'next' of the *last child* in its substack will point to the block after the C-block. + block_stack.append((new_block_key, current_line_indent, None)) # (parent_key, indent, last_child_key_in_substack) + + # For C-blocks, the first child is linked via the 'SUBSTACK' input + new_block_data["inputs"]["SUBSTACK"] = [2, None] # Placeholder for the first child block ID + + # For 'if' blocks, the 'next' of the parent is usually null, and children start in 'SUBSTACK' + if opcode == "control_if": + block_stack.append((new_block_key, current_line_indent, None)) + new_block_data["inputs"]["SUBSTACK"] = [2, None] # Placeholder for the first child block ID + + + # Final pass to ensure topLevel is correctly set for the very first block of a script + for key, block in updated_blocks.items(): + if block.get("parent") is None and block.get("next") is not None: + block["topLevel"] = True + elif block.get("parent") is None and block.get("next") is None and block.get("opcode") == "event_whenflagclicked": + block["topLevel"] = True # Ensure hat blocks are always topLevel + + return updated_blocks + +# --- Consolidated Block Definitions from all provided JSONs --- +# This dictionary should contain ALL block definitions from your JSON files. +# I'm using the provided definitions from the previous turn. +all_block_definitions = { + # motion_block.json + "motion_movesteps": { + "block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps", + "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.", + "inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnright": { + "block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright", + "functionality": "Turns the sprite clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_turnleft": { + "block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft", + "functionality": "Turns the sprite counter-clockwise by the specified number of degrees.", + "inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto": { + "block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto", + "functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.", + "inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_goto_menu": { + "block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu", + "functionality": "Menu for go to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_gotoxy": { + "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": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto": { + "block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto", + "functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_glideto_menu": { + "block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu", + "functionality": "Menu for glide to block.", + "inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False + }, + "motion_glidesecstoxy": { + "block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy", + "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", + "inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointindirection": { + "block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection", + "functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).", + "inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards": { + "block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards", + "functionality": "Points the sprite towards the mouse pointer or another specified sprite.", + "inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_pointtowards_menu": { + "block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu", + "functionality": "Menu for point towards block.", + "inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "motion_changexby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setx": { + "block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx", + "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", + "inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_changeyby": { + "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": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_sety": { + "block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety", + "functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.", + "inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_ifonedgebounce": { + "block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce", + "functionality": "Reverses the sprite's direction if it touches the edge of the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_setrotationstyle": { + "block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle", + "functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).", + "inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True + }, + "motion_xposition": { + "block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition", + "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "motion_yposition": { + "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": True + }, + "motion_direction": { + "block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction", + "functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # control_block.json + "control_wait": { + "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": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat": { + "block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat", + "functionality": "Repeats the blocks inside it a specified number of times.", + "inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_forever": { + "block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever", + "functionality": "Continuously runs the blocks inside it.", + "inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if": { + "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": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_if_else": { + "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else", + "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]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_wait_until": { + "block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until", + "functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_repeat_until": { + "block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until", + "functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]", + "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_stop": { + "block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop", + "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.", + "inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"} + }, + "control_start_as_clone": { + "block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone", + "functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of": { + "block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of", + "functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).", + "inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "control_create_clone_of_menu": { + "block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu", + "functionality": "Menu for create clone of block.", + "inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False + }, + "control_delete_this_clone": { + "block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone", + "functionality": "Removes the clone that is executing it from the stage.", + "inputs":None, "fields": {}, "shadow": False, "topLevel": True + }, + + # data_block.json + "data_setvariableto": { + "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": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_changevariableby": { + "block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby", + "functionality": "Increases or decreases a variable's numerical value by a specified amount.", + "inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_showvariable": { + "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": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_hidevariable": { + "block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable", + "functionality": "Hides a variable's monitor from the stage.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True + }, + "data_addtolist": { + "block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist", + "functionality": "Appends an item to the end of a list.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deleteoflist": { + "block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist", + "functionality": "Removes an item from a list by its index or by selecting 'all' items.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_deletealloflist": { + "block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist", + "functionality": "Removes all items from a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_insertatlist": { + "block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist", + "functionality": "Inserts an item at a specific position within a list.", + "inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_replaceitemoflist": { + "block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist", + "functionality": "Replaces an item at a specific position in a list with a new value.", + "inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemoflist": { + "block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist", + "functionality": "Reports the item located at a specific position in a list.", + "inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_itemnumoflist": { + "block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist", + "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.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_lengthoflist": { + "block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist", + "functionality": "Provides the total number of items contained in a list.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_listcontainsitem": { + "block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem", + "functionality": "Checks if a list includes a specific item.", + "inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_showlist": { + "block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist", + "functionality": "Makes a list's monitor visible on the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_hidelist": { + "block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist", + "functionality": "Hides a list's monitor from the stage.", + "inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True + }, + "data_variable": { # This is a reporter block for a variable's value + "block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable", + "functionality": "Provides the current value stored in a variable.", + "inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False + }, + + # event_block.json + "event_whenflagclicked": { + "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 + }, + "event_whenkeypressed": { + "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 + }, + "event_whenthisspriteclicked": { + "block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when the sprite itself is clicked.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_whenbackdropswitchesto": { + "block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block", + "functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True + }, + "event_whengreaterthan": { + "block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block", + "functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True + }, + "event_whenbroadcastreceived": { + "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": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True + }, + "event_broadcast": { + "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": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "event_broadcastandwait": { + "block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait", + "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.", + "inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + + # looks_block.json + "looks_sayforsecs": { + "block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs", + "functionality": "Displays a speech bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_say": { + "block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say", + "functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_thinkforsecs": { + "block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs", + "functionality": "Displays a thought bubble containing specified text for a set duration.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_think": { + "block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think", + "functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.", + "inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchcostumeto": { + "block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto", + "functionality": "Alters the sprite's appearance to a designated costume.", + "inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_costume": { + "block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume", + "functionality": "Menu for switch costume to block.", + "inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False + }, + "looks_nextcostume": { + "block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_switchbackdropto": { + "block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto", + "functionality": "Changes the stage's backdrop to a specified backdrop.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_backdrops": { + "block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops", + "functionality": "Menu for switch backdrop to block.", + "inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False + }, + "looks_switchbackdroptowait": { + "block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait", + "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.", + "inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_nextbackdrop": { + "block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop", + "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.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changesizeby": { + "block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby", + "functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.", + "inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_setsizeto": { + "block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto", + "functionality": "Sets the sprite's size to a specific percentage of its original size.", + "inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby", + "functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).", + "inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto", + "functionality": "Sets a visual effect on the sprite to a specific value.", + "inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True + }, + "looks_cleargraphiceffects": { + "block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects", + "functionality": "Removes all visual effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_show": { + "block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show", + "functionality": "Makes the sprite visible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_hide": { + "block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide", + "functionality": "Makes the sprite invisible on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "looks_gotofrontback": { + "block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback", + "functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.", + "inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True + }, + "looks_goforwardbackwardlayers": { + "block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers", + "functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.", + "inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True + }, + "looks_costumenumbername": { + "block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername", + "functionality": "Reports the current costume's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_backdropnumbername": { + "block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername", + "functionality": "Reports the current backdrop's number or name.", + "inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True + }, + "looks_size": { + "block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size", + "functionality": "Reports the current size of the sprite as a percentage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # operator_block.json + "operator_add": { + "block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add", + "functionality": "Adds two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_subtract": { + "block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract", + "functionality": "Subtracts the second numerical value from the first.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_multiply": { + "block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply", + "functionality": "Multiplies two numerical values.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_divide": { + "block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide", + "functionality": "Divides the first numerical value by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_random": { + "block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random", + "functionality": "Generates a random integer within a specified inclusive range.", + "inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_gt": { + "block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt", + "functionality": "Checks if the first value is greater than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_lt": { + "block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt", + "functionality": "Checks if the first value is less than the second.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_equals": { + "block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals", + "functionality": "Checks if two values are equal.", + "inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_and": { + "block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and", + "functionality": "Returns 'true' if both provided Boolean conditions are 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_or": { + "block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or", + "functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.", + "inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_not": { + "block_name": ">", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not", + "functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.", + "inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_join": { + "block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join", + "functionality": "Concatenates two strings or values into a single string.", + "inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_letterof": { + "block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof", + "functionality": "Reports the character at a specific numerical position within a string.", + "inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_length": { + "block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length", + "functionality": "Reports the total number of characters in a given string.", + "inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_contains": { + "block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains", + "functionality": "Checks if one string contains another string.", + "inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mod": { + "block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod", + "functionality": "Reports the remainder when the first number is divided by the second.", + "inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_round": { + "block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round", + "functionality": "Rounds a numerical value to the nearest integer.", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "operator_mathop": { + "block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop", + "functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).", + "inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True + }, + + # sensing_block.json + "sensing_touchingobject": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block", + "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", + "inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_touchingobjectmenu": { + "block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu", + "functionality": "Menu for touching object block.", + "inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False + }, + "sensing_touchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether its sprite is touching a specified color.", + "inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_coloristouchingcolor": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block", + "functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.", + "inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_askandwait": { + "block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait", + "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.", + "inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_answer": { + "block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer", + "functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keypressed": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block", + "functionality": "Checks if a specified keyboard key is currently being pressed.", + "inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_keyoptions": { + "block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions", + "functionality": "Menu for key pressed block.", + "inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False + }, + "sensing_mousedown": { + "block_name": "", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block", + "functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousex": { + "block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex", + "functionality": "Reports the mouse-pointer’s current X position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_mousey": { + "block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey", + "functionality": "Reports the mouse-pointer’s current Y position on the stage.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_setdragmode": { + "block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode", + "functionality": "Sets whether the sprite can be dragged by the mouse on the stage.", + "inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True + }, + "sensing_loudness": { + "block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness", + "functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_timer": { + "block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer", + "functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_resettimer": { + "block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer", + "functionality": "Sets the timer’s value back to 0.0.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_of": { + "block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of", + "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.", + "inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True + }, + "sensing_of_object_menu": { + "block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu", + "functionality": "Menu for of block.", + "inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False + }, + "sensing_current": { + "block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current", + "functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.", + "inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True + }, + "sensing_dayssince2000": { + "block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000", + "functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sensing_username": { + "block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username", + "functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + + # sound_block.json + "sound_playuntildone": { + "block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone", + "functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_sounds_menu": { + "block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu", + "functionality": "Menu for sound blocks.", + "inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False + }, + "sound_play": { + "block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play", + "functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.", + "inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_stopallsounds": { + "block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds", + "functionality": "Stops all currently playing sounds.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changeeffectby": { + "block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby", + "functionality": "Changes the project's sound effect by a specified amount.", + "inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_seteffectto": { + "block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto", + "functionality": "Sets the sound effect to a specific value.", + "inputs": {"VALUE": [1, [4, "100"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True + }, + "sound_cleareffects": { + "block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects", + "functionality": "Removes all sound effects applied to the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_changevolumeby": { + "block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby", + "functionality": "Changes the project's sound volume by a specified amount.", + "inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_setvolumeto": { + "block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto", + "functionality": "Sets the sound volume to a specific percentage (0-100).", + "inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True + }, + "sound_volume": { + "block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume", + "functionality": "Reports the current volume level of the sprite.", + "inputs": {}, "fields": {}, "shadow": False, "topLevel": True + }, +} + +# Example input with opcodes for the initial generation +initial_opcode_counts = [ + {"opcode":"event_whenflagclicked","count":1}, + {"opcode":"motion_gotoxy","count":1}, + {"opcode":"motion_glidesecstoxy","count":1}, + {"opcode":"motion_xposition","count":1}, + {"opcode":"motion_setx","count":1}, + {"opcode":"control_forever","count":1}, + {"opcode":"control_if","count":1}, + {"opcode":"control_stop","count":1}, + {"opcode":"operator_lt","count":1}, + {"opcode":"sensing_touchingobject","count":1}, # Changed from sensing_istouching + {"opcode":"sensing_touchingobjectmenu","count":1}, + {"opcode":"event_broadcast","count":1}, + {"opcode":"data_setvariableto","count":2}, + {"opcode":"data_showvariable","count":2}, +] + +# Generate the initial blocks and get the opcode_occurrences +generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions) + +# Pseudo-code to interpret +pseudo_code_input = """ +when green flag clicked + go to x: (240) y: (-135) + set [score v] to (1) + set [speed v] to (1) + show variable [score v] + show variable [speed v] + forever + glide (2) seconds to x: (-240) y: (-135) + if <((x position)) < (-235)> then + set x to (240) + end + if then + broadcast [Game Over v] + stop [all v] + end + end +end +""" + +# Interpret the pseudo-code and update the blocks, passing opcode_occurrences +final_generated_blocks = interpret_pseudo_code_and_update_blocks(generated_output_json, pseudo_code_input, all_block_definitions, initial_opcode_occurrences) + +print(initial_opcode_occurrences) + +#print(json.dumps(final_generated_blocks, indent=2)) +# ```json +# { +# "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, +# "parent": null, +# "next": "motion_gotoxy_1" +# }, +# "motion_gotoxy_1": { +# "block_name": "go to x: () y: ()", +# "block_type": "Motion", +# "op_code": "motion_gotoxy", +# "functionality": "Moves the sprite to the specified X and Y coordinates on the stage.", +# "inputs": { +# "X": [ +# 4, +# "240" +# ], +# "Y": [ +# 4, +# "-135" +# ] +# }, +# "fields": {}, +# "shadow": false, +# "topLevel": false, +# "parent": null, +# "next": "data_setvariableto_1" +# }, +# "motion_glidesecstoxy_1": { +# "block_name": "glide () secs to x: () y: ()", +# "block_type": "Motion", +# "op_code": "motion_glidesecstoxy", +# "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.", +# "inputs": { +# "SECS": [ +# 4, +# "2" +# ], +# "X": [ +# 4, +# "-240" +# ], +# "Y": [ +# 4, +# "-135" +# ] +# }, +# "fields": {}, +# "shadow": false, +# "topLevel": false, +# "parent": "control_forever_1", +# "next": "control_if_1" +# }, +# "motion_xposition_1": { +# "block_name": "(x position)", +# "block_type": "Motion", +# "op_code": "motion_xposition", +# "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]", +# "inputs": {}, +# "fields": {}, +# "shadow": true, +# "topLevel": false, +# "parent": "operator_lt_1", +# "next": null +# }, +# "motion_setx_1": { +# "block_name": "set x to ()", +# "block_type": "Motion", +# "op_code": "motion_setx", +# "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.", +# "inputs": { +# "X": [ +# 4, +# "240" +# ] +# }, +# "fields": {}, +# "shadow": false, +# "topLevel": false, +# "parent": "control_if_1", +# "next": null +# }, +# "control_forever_1": { +# "block_name": "forever", +# "block_type": "Control", +# "op_code": "control_forever", +# "functionality": "Continuously runs the blocks inside it.", +# "inputs": { +# "SUBSTACK": [ +# 2, +# "motion_glidesecstoxy_1" +# ] +# }, +# "fields": {}, +# "shadow": false, +# "topLevel": false, +# "parent": null, +# "next": null +# }, +# "control_if_1": { +# "block_name": "if <> then", +# "block_type": "Control", +# "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": [ +# 2, +# "operator_lt_1" +# ], +# "SUBSTACK": [ +# 2, +# "motion_setx_1" +# ] +# }, +# "fields": {}, +# "shadow": false, +# "topLevel": false, +# "parent": "control_forever_1", +# "next": "control_if_2" +# }, +# "control_stop_1": { +# "block_name": "stop [v]", +# "block_type": "Control", +# "op_code": "control_stop", +# "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.", +# "inputs": {}, +# "fields": { +# "STOP_OPTION": [ +# "all", +# null +# ] +# }, +# "shadow": false, +# "topLevel": false, +# "parent": "control_if_2", +# "mutation": { +# "tagName": "mutation", +# "children": [], +# "hasnext": "false" +# }, +# "next": null +# }, +# "operator_lt_1": { +# "block_name": "<() < ()>", +# "block_type": "operator", +# "op_code": "operator_lt", +# "functionality": "Checks if the first value is less than the second.", +# "inputs": { +# "OPERAND1": [ +# 1, +# "motion_xposition_1" +# ], +# "OPERAND2": [ +# 4, +# "-235" +# ] +# }, +# "fields": {}, +# "shadow": true, +# "topLevel": false, +# "parent": "control_if_1", +# "next": null +# }, +# "sensing_touchingobject_1": { +# "block_name": "", +# "block_type": "Sensing", +# "op_code": "sensing_touchingobject", +# "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.", +# "inputs": { +# "TOUCHINGOBJECTMENU": [ +# 1, +# "sensing_touchingobjectmenu_1" +# ] +# }, +# "fields": {}, +# "shadow": true, +# "topLevel": false, +# "parent": "control_if_2", +# "next": null +# }, +# "sensing_touchingobjectmenu_1": { +# "block_name": "touching object menu", +# "block_type": "Sensing", +# "op_code": "sensing_touchingobjectmenu", +# "functionality": "Menu for touching object block.", +# "inputs": {}, +# "fields": { +# "TOUCHINGOBJECTMENU": [ +# "Sprite1", +# null +# ] +# }, +# "shadow": true, +# "topLevel": false, +# "parent": "sensing_touchingobject_1", +# "next": null +# }, +# "event_broadcast_1": { +# "block_name": "broadcast ()", +# "block_type": "Events", +# "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": [ +# 10, +# "Game Over" +# ] +# }, +# "fields": {}, +# "shadow": false, +# "topLevel": false, +# "parent": "control_if_2", +# "next": "control_stop_1" +# }, +# "data_setvariableto_1": { +# "block_name": "set [my variable v] to ()", +# "block_type": "Data", +# "op_code": "data_setvariableto", +# "functionality": "Assigns a specific value (number, string, or boolean) to a variable.", +# "inputs": { +# "VALUE": [ +# 4, +# "1" +# ] +# }, +# "fields": { +# "VARIABLE": [ +# "score", +# "`var_score" +# ] +# }, +# "shadow": false, +# "topLevel": false, +# "parent": null, +# "next": "data_setvariableto_2" +# }, +# "data_setvariableto_2": { +# "block_name": "set [my variable v] to ()", +# "block_type": "Data", +# "op_code": "data_setvariableto", +# "functionality": "Assigns a specific value (number, string, or boolean) to a variable.", +# "inputs": { +# "VALUE": [ +# 4, +# "1" +# ] +# }, +# "fields": { +# "VARIABLE": [ +# "speed", +# "`var_speed" +# ] +# }, +# "shadow": false, +# "topLevel": false, +# "parent": null, +# "next": "data_showvariable_1" +# }, +# "data_showvariable_1": { +# "block_name": "show variable [my variable v]", +# "block_type": "Data", +# "op_code": "data_showvariable", +# "functionality": "Makes a variable's monitor visible on the stage.", +# "inputs": {}, +# "fields": { +# "VARIABLE": [ +# "score", +# "`var_score" +# ] +# }, +# "shadow": false, +# "topLevel": false, +# "parent": null, +# "next": "data_showvariable_2" +# }, +# "data_showvariable_2": { +# "block_name": "show variable [my variable v]", +# "block_type": "Data", +# "op_code": "data_showvariable", +# "functionality": "Makes a variable's monitor visible on the stage.", +# "inputs": {}, +# "fields": { +# "VARIABLE": [ +# "speed", +# "`var_speed" +# ] +# }, +# "shadow": false, +# "topLevel": false, +# "parent": null, +# "next": "control_forever_1" +# }, +# "control_if_2": { +# "block_name": "if <> then", +# "block_type": "Control", +# "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": [ +# 2, +# "sensing_touchingobject_1" +# ], +# "SUBSTACK": [ +# 2, +# "event_broadcast_1" +# ] +# }, +# "fields": {}, +# "shadow": false, +# "topLevel": false, +# "parent": "control_forever_1", +# "next": null +# } +# } \ No newline at end of file diff --git a/utils/testing.ipynb b/utils/testing.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..e27d2b9b31201d1deeee2aef62e4e7a2d93ae5dd --- /dev/null +++ b/utils/testing.ipynb @@ -0,0 +1,2297 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "id": "9adfcb20", + "metadata": {}, + "outputs": [], + "source": [ + "import re" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "de910107", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'if > then'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "stmt=\"if > then\"\n", + "s = stmt.lower().strip()\n", + "# 1) strip ALL balanced <…>\n", + "while s.startswith(\"<\") and s.endswith(\">\"):\n", + " s = s[1:-1].strip()\n", + "# 2) strip stray unmatched < or >\n", + "while s.startswith(\"<\") and not s.endswith(\">\"):\n", + " s = s[1:].strip()\n", + "while s.endswith(\">\") and not s.startswith(\"<\"):\n", + " s = s[:-1].strip()\n", + " \n", + "s" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "af367513", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n", + "the stmt was this if < and > then and parsed was this if < and > then\n" + ] + }, + { + "ename": "SyntaxError", + "evalue": "'return' outside function (234319892.py, line 51)", + "output_type": "error", + "traceback": [ + " \u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[20]\u001b[39m\u001b[32m, line 51\u001b[39m\n\u001b[31m \u001b[39m\u001b[31mreturn {\"kind\": \"block\", \"block\": block_id}\u001b[39m\n ^\n\u001b[31mSyntaxError\u001b[39m\u001b[31m:\u001b[39m 'return' outside function\n" + ] + } + ], + "source": [ + "import re\n", + "\n", + "s = \"if > then\"\n", + "\n", + "m_touch = re.fullmatch(r\"touching\\s*\\[\\s*([^\\]]+)\\s*v\\]\\??\", s, re.IGNORECASE)\n", + "print(m_touch)\n", + "if m_touch:\n", + " sprite = m_touch.group(1).strip()\n", + " val = {'mouse-pointer':'_mouse_', 'edge':'_edge_'}.get(sprite, sprite)\n", + " mid = _register_block(\n", + " \"sensing_touchingobjectmenu\", parent_key, True, pick_key_func, all_generated_blocks,\n", + " fields={\"TOUCHINGOBJECTMENU\":[val, None]}\n", + " )\n", + " bid = _register_block(\n", + " \"sensing_touchingobject\", parent_key, False, pick_key_func, all_generated_blocks,\n", + " inputs={\"TOUCHINGOBJECTMENU\":[1, mid]}\n", + " )\n", + " #all_generated_blocks[mid][\"parent\"] = bid \n", + " print(bid)\n", + " #return {\"kind\":\"block\",\"block\":bid}\n", + "\n", + "s = stmt.lower().strip()\n", + "#s = strip_outer_angle_brackets(s)\n", + "# 1) strip ALL balanced <…>\n", + "while s.startswith(\"<\") and s.endswith(\">\"):\n", + " s = s[1:-1].strip()\n", + "# 2) strip stray unmatched < or >\n", + "while s.startswith(\"<\") and not s.endswith(\">\"):\n", + " s = s[1:].strip()\n", + "while s.endswith(\">\") and not s.startswith(\"<\"):\n", + " s = s[:-1].strip()\n", + " \n", + "print(f\"the stmt was this {stmt} and parsed was this {s}\")\n", + "# 1a) Comparisons with explicit angle wrappers: < (...) op (...) >\n", + "m = re.fullmatch(\n", + " r\"\\s*<\\s*(.+?)\\s*(?P<|=|>)\\s*(.+?)\\s*>\\s*\",\n", + " s,\n", + " re.VERBOSE\n", + ")\n", + "if m:\n", + " left_txt, right_txt = m.group(1), m.group(3)\n", + " operand1_obj = parse_reporter_or_value(unparen(left_txt), None, pick_key_func, all_generated_blocks)\n", + " operand2_obj = parse_reporter_or_value(unparen(right_txt), None, pick_key_func, all_generated_blocks)\n", + " op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'}\n", + " \n", + " inputs = {\"OPERAND1\": operand1_obj, \"OPERAND2\": operand2_obj}\n", + " block_id = _register_block(op_map[m.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs)\n", + " # Set parents for nested inputs\n", + " if operand1_obj.get(\"kind\") == \"block\": all_generated_blocks[operand1_obj[\"block\"]][\"parent\"] = block_id\n", + " if operand2_obj.get(\"kind\") == \"block\": all_generated_blocks[operand2_obj[\"block\"]][\"parent\"] = block_id\n", + " return {\"kind\": \"block\", \"block\": block_id}\n", + "# 1b) Simple comparisons without angle wrappers: A op B\n", + "m_simple = re.fullmatch(r\"\\s*(.+?)\\s*(?P<|=|>)\\s*(.+?)\\s*\", s)\n", + "if m_simple:\n", + " left_txt, right_txt = m_simple.group(1), m_simple.group(3)\n", + " operand1_obj = parse_reporter_or_value(unparen(left_txt), None, pick_key_func, all_generated_blocks)\n", + " operand2_obj = parse_reporter_or_value(unparen(right_txt), None, pick_key_func, all_generated_blocks)\n", + " op_map = {'<': 'operator_lt', '=': 'operator_equals', '>': 'operator_gt'}\n", + " \n", + " inputs = {\"OPERAND1\": operand1_obj, \"OPERAND2\": operand2_obj}\n", + " block_id = _register_block(op_map[m_simple.group('op')], parent_key, False, pick_key_func, all_generated_blocks, inputs=inputs)\n", + " if operand1_obj.get(\"kind\") == \"block\": all_generated_blocks[operand1_obj[\"block\"]][\"parent\"] = block_id\n", + " if operand2_obj.get(\"kind\") == \"block\": all_generated_blocks[operand2_obj[\"block\"]][\"parent\"] = block_id\n", + " return {\"kind\": \"block\", \"block\": block_id}" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "3a315968", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[TEST] if then\n", + "→ Extracted: 'mouse down?'\n", + " Expected: 'mouse down?'\n", + " ✅ PASS\n", + "\n", + "[TEST] if > then\n", + "→ Extracted: 'not '\n", + " Expected: 'not '\n", + " ✅ PASS\n", + "\n", + "[TEST] if < and > then\n", + "→ Extracted: 'mouse down?> and > or > >> then\n", + "→ Extracted: 'mouse down?>> or '\n", + " ❌ FAIL\n", + "\n", + "[TEST] if <(x position) < (100)> then\n", + "→ Extracted: '<(x position) < (100)> then'\n", + " Expected: '(x position) < (100)'\n", + " ❌ FAIL\n", + "\n" + ] + } + ], + "source": [ + "def extract_condition_balanced(stmt):\n", + " stmt = stmt.strip()\n", + " if \"if \" in stmt.lower():\n", + " stmt = stmt.lower().split(\"if\", 1)[1].strip()\n", + "\n", + " # Handle bracket balancing\n", + " if stmt.startswith(\"<\"):\n", + " count = 0\n", + " condition = \"\"\n", + " for i, ch in enumerate(stmt):\n", + " if ch == \"<\":\n", + " count += 1\n", + " elif ch == \">\":\n", + " count -= 1\n", + " condition += ch\n", + " if count == 0:\n", + " break\n", + "\n", + " # Strip outer brackets recursively\n", + " inner = condition\n", + " while inner.startswith(\"<\") and inner.endswith(\">\"):\n", + " inner = inner[1:-1].strip()\n", + "\n", + " return inner\n", + " return stmt.strip()\n", + "\n", + "test_cases = [\n", + " (\"if then\", \"mouse down?\"),\n", + " (\"if > then\", \"not \"),\n", + " (\"if < and > then\", \"mouse down? and touching [edge v]?\"),\n", + " (\"if <<> or > >> then\", \"mouse down? or not \"),\n", + " (\"if <(x position) < (100)> then\", \"(x position) < (100)\"),\n", + "]\n", + "\n", + "for stmt, expected in test_cases:\n", + " result = extract_condition_balanced(stmt)\n", + " print(f\"[TEST] {stmt}\\n→ Extracted: '{result}'\\n Expected: '{expected}'\\n {'✅ PASS' if result == expected else '❌ FAIL'}\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "34ddb52b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Testing improved function ---\n", + "[TEST] if then\n", + "→ Extracted: 'mouse down?'\n", + " Expected: 'mouse down?'\n", + " ✅ PASS\n", + "\n", + "[TEST] repeat until \n", + "→ Extracted: 'touching [edge v]?'\n", + " Expected: 'touching [edge v]?'\n", + " ✅ PASS\n", + "\n", + "[TEST] if then\n", + "→ Extracted: 'key [left arrow v] pressed?'\n", + " Expected: 'key [left arrow v] pressed?'\n", + " ✅ PASS\n", + "\n", + "[TEST] if then\n", + "→ Extracted: 'touching [edge v]?'\n", + " Expected: 'touching [edge v]?'\n", + " ✅ PASS\n", + "\n", + "[TEST] if < and > then\n", + "→ Extracted: ' and '\n", + " Expected: 'mouse down? and touching [edge v]?'\n", + " ❌ FAIL\n", + "\n", + "[TEST] if <(x position) < (100)> then\n", + "→ Extracted: '(x position) < (100)'\n", + " Expected: '(x position) < (100)'\n", + " ✅ PASS\n", + "\n", + "[TEST] if <> then\n", + "→ Extracted: ''\n", + " Expected: 'mouse down?'\n", + " ❌ FAIL\n", + "\n", + "[TEST] if > then\n", + "→ Extracted: 'not<>'\n", + " Expected: 'not<>'\n", + " ✅ PASS\n", + "\n", + "[TEST] if <<> or <>> then\n", + "→ Extracted: '<> or <>'\n", + " Expected: '<> or <>'\n", + " ✅ PASS\n", + "\n", + "[TEST] if <> then\n", + "→ Extracted: ''\n", + " Expected: ''\n", + " ✅ PASS\n", + "\n", + "[TEST] if mouse down? then\n", + "→ Extracted: 'mouse down?'\n", + " Expected: 'mouse down?'\n", + " ✅ PASS\n", + "\n", + "[TEST] \n", + "→ Extracted: 'mouse down?'\n", + " Expected: 'mouse down?'\n", + " ✅ PASS\n", + "\n" + ] + } + ], + "source": [ + "import re\n", + "\n", + "def extract_condition_balanced(stmt):\n", + " # 1. Remove \"if\" and \"then\"\n", + " stmt = stmt.strip()\n", + " if stmt.lower().startswith(\"if \"):\n", + " stmt = stmt[3:].strip()\n", + " if stmt.lower().startswith(\"repeat until\"):\n", + " stmt = stmt[12:].strip()\n", + " if stmt.lower().endswith(\" then\"):\n", + " stmt = stmt[:-5].strip()\n", + "\n", + " # Helper to detect and strip single outer balanced angle brackets\n", + " def unwrap_balanced(s):\n", + " if s.startswith(\"<\") and s.endswith(\">\"):\n", + " depth = 0\n", + " for i in range(len(s)):\n", + " if s[i] == \"<\":\n", + " depth += 1\n", + " elif s[i] == \">\":\n", + " depth -= 1\n", + " if depth == 0 and i < len(s) - 1:\n", + " return s # Early balance → not a single outer wrapper\n", + " if depth == 0:\n", + " return s[1:-1].strip()\n", + " return s\n", + "\n", + " # Recursively simplify things like > to not \n", + " def simplify(s):\n", + " s = unwrap_balanced(s)\n", + " s = s.strip()\n", + "\n", + " # Match > pattern\n", + " m = re.fullmatch(r\"not\\s*<(.+)>\", s, re.IGNORECASE)\n", + " if m:\n", + " inner = m.group(1).strip()\n", + " inner = simplify(inner)\n", + " return f\"not <{inner}>\"\n", + "\n", + " # Match comparison operators like <(x position) < (100)>\n", + " m_comp = re.fullmatch(r\"<\\s*\\(([^<>]+?)\\)\\s*([<>=])\\s*\\(([^<>]+?)\\)\\s*>\", stmt)\n", + " if m_comp:\n", + " return f\"({m_comp.group(1).strip()}) {m_comp.group(2)} ({m_comp.group(3).strip()})\"\n", + "\n", + " return s\n", + "\n", + " return simplify(stmt)\n", + "\n", + "# Test cases\n", + "# test_cases = [\n", + "# (\"if then\", \"mouse down?\"),\n", + "# (\"if > then\", \"not \"),\n", + "# (\"if < and > then\", \" and \"),\n", + "# (\"if < or >> then\", \" or >\"),\n", + "# (\"if <(x position) < (100)> then\", \"(x position) < (100)\"),\n", + "# ]\n", + "test_cases = [\n", + " (\"if then\", \"mouse down?\"),\n", + " (\"repeat until \", \"touching [edge v]?\"),\n", + " (\"if then\", \"key [left arrow v] pressed?\"),\n", + " (\"if then\", \"touching [edge v]?\"),\n", + " (\"if < and > then\", \"mouse down? and touching [edge v]?\"),\n", + " (\"if <(x position) < (100)> then\", \"(x position) < (100)\"),\n", + " (\"if <> then\", \"mouse down?\"),\n", + " (\"if > then\", \"not<>\"),\n", + " (\"if <<> or <>> then\", \"<> or <>\"),\n", + " (\"if <> then\", \"\"),\n", + " (\"if mouse down? then\", \"mouse down?\"),\n", + " (\"\", \"mouse down?\"),\n", + "]\n", + "\n", + "print(\"--- Testing improved function ---\")\n", + "for stmt, expected in test_cases:\n", + " result = extract_condition_balanced(stmt)\n", + " print(f\"[TEST] {stmt}\\n→ Extracted: '{result}'\\n Expected: '{expected}'\\n {'✅ PASS' if result == expected else '❌ FAIL'}\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "bc324bc0", + "metadata": {}, + "outputs": [], + "source": [ + "all_logics={'logic_0': '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]',\n", + "'logic_1': '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]',\n", + "'logic_2': 'when I receive [Level Up v]\\n change [level v] by 1\\n set [ballSpeed v] to (ballSpeed * 1.1)',\n", + "'logic_3': 'when green flag clicked\\n go to x: 0 y: -100\\n forever\\n if then\\n jump\\n end\\n move 5 steps\\n if then\\n broadcast [Game Over v]\\n end\\n end',\n", + "'logic_4': 'when [space v] key pressed\\n change y by 10\\n wait 0.2 seconds\\n change y by -10',\n", + "'logic_5': '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 then\\n broadcast [Game Over v]\\n end\\n end',\n", + "'logic_6': '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]',\n", + "'logic_7': '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]',\n", + "'logic_8': 'when green flag clicked\\n go to x: 0 y: -130\\n set [shield v] to 0',\n", + "'logic_9': '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',\n", + "'logic_10': 'when I receive [Power-Up v]\\n if <(Power-Up) = [Shield v]> then\\n set [shield v] to 1\\n end',\n", + "'logic_11': '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 then\\n broadcast [Game Over v]\\n end\\n end',}" + ] + }, + { + "cell_type": "markdown", + "id": "6de2c3ab", + "metadata": {}, + "source": [ + "1. Example for the logic_1:\n", + "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]\n", + " [ Note:There are some mistake done by the llm to generate the above pseudo code:\n", + " 1. the value `0` should be enclosed in brackets `(0)` which is missing here.\n", + " 2. there should be `end` for the `when green flag clicked` at the last line of pesudo-code.\n", + " 3. correct use of variables [score v] and options [Game Start v],[blue sky v] \n", + " ]\n", + "\n", + "2. Example for the logic_2:\n", + "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]\n", + " [ Note:There are some mistake done by the llm to generate the above pseudo code:\n", + " 1. the score and High score where variable but llm conisder as string so it keep it simple but thats wrong e.g. `[High Score v]` and [score v]\n", + " 2. in `set [High Score v] to (score)` the `[High Score v]`is correct but the vairable should be ([score v])\n", + " 3. there should be `end` for the `when I receive [Game Over v]` at the last line of pesudo-code.\n", + " 4. correct use of options [Game Over v],[High Score v].\n", + " ]\n", + "\n", + "3. Example for the logic_3:\n", + "when I receive [Level Up v]\n", + " change [level v] by 1\n", + " set [ballSpeed v] to (ballSpeed * 1.1)\n", + " [ Note:There are some mistake done by the llm to generate the above pseudo code:\n", + " 1. the value `1` and should be enclosed in brackets `(1)` which is missing here.\n", + " 2. in `set [ballSpeed v] to (ballSpeed * 1.1)` the problem goes with invalid use of the operator block should be like this `(([ballSpeed v])*(1.1))`\n", + " 3. the `ballSpeed` is variable but llm conisder as string so it keep it simple but thats wrong in the set for second occurance e.g. `[ballSpeed v]` \n", + " 4. there should be `end` for the `when I receive [Level Up v]` at the last line of pesudo-code.\n", + " ]\n", + "\n", + "4. Example for the logic_4:\n", + "when green flag clicked\n", + " go to x: 0 y: -100\n", + " forever\n", + " if then\n", + " jump\n", + " end\n", + " move 5 steps\n", + " if then\n", + " broadcast [Game Over v]\n", + " end\n", + " end\n", + " [ Note:There are some mistake done by the llm to generate the above pseudo code:\n", + " 1. the value `0`,`100` and `5` should be enclosed in brackets `(0)`,`(100)` and `(5)` which is missing here.\n", + " 2. there is invalid code use that is `jump`it should be script where you use few code of with event to have jumping motion.\n", + " 3. correct use of options [Game Over v],[Ball v] and [space v]\n", + " 4. there should be `end` for the `when green flag clicked` at the last line of pesudo-code.\n", + " ]\n", + "\n", + "5. Example for the logic_5:\n", + "when [space v] key pressed\n", + " change y by 10\n", + " wait 0.2 seconds\n", + " change y by -10\n", + " [ Note:There are some mistake done by the llm to generate the above pseudo code:\n", + " 1. the value `10`,`0.2` and `-10` should be enclosed in brackets`(10)`,`(0.2)` and `(-10)` which is missing here.\n", + " 2. correct use of options [space v]\n", + " 3. there should be `end` for the `when [space v] key pressed` at the last line of pesudo-code.\n", + " ]\n", + "\n", + "6. Example for the logic_6:\n", + "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 #(pick random () to ())\n", + " if then\n", + " broadcast [Game Over v]\n", + " end\n", + " end\n", + " [ Note:There are some mistake done by the llm to generate the above pseudo code:\n", + " 1. the value `0`,`100` and `2` should be enclosed in brackets `(0)`,`(100)` and `(2)` which is missing here.\n", + " 2. incorrect use of opcode in `glide 2 seconds to x: pick random (-240, 240) y: 100` it should be `glide (2) seconds to x: (pick random (-240) to (240)) y: (100)`\n", + " 3. correct use of options [Game Over v],[Cat v].\n", + " 4. there should be `end` for the `when green flag clicked` at the last line of pesudo-code.\n", + " ]\n", + "\n", + "7. Example for the logic_7:\n", + "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]\n", + " [ Note:There are some mistake done by the llm to generate the above pseudo code:\n", + " 1. the value `0`,`1` and `1` should be enclosed in brackets `(0)`,`(1)` and `(1)` which is missing here.\n", + " 2. there should be `end` for the `when green flag clicked` at the last line of pesudo-code.\n", + " 3. correct use of variables [score v] and options [Game Start v],[blue sky v] \n", + " ]\n", + "\n", + "8. Example for the logic_8:\n", + "when green flag clicked\n", + " go to x: 0 y: -130\n", + " set [shield v] to 0\n", + " [ Note:There are some mistake done by the llm to generate the above pseudo code:\n", + " 1. the value `0`,`-130` should be enclosed in brackets `(0)`,`(-130)` which is missing here.\n", + " 2. there should be `end` for the `when green flag clicked` at the last line of pesudo-code.\n", + " 3. correct use of variables [shield v]\n", + " ]\n", + "\n", + "9. Example for the logic_9:\n", + "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\n", + " [ Note:There are some mistake done by the llm to generate the above pseudo code:\n", + " 1. the value `20`,`-130` and `-20` should be enclosed in brackets `(20)`,`(-130)` and `(-20)` which is missing here.\n", + " 2. the reportor block such as `(y position)` when used as nesting as seen `if <(y position) = -130> then` it should enclosed in `()` the correct use `if <((y position)) = (-130)> then`\n", + " 3. correct use of options [space v]\n", + " 4. there should be `end` for the `when [space v] key pressed` at the last line of pesudo-code.\n", + " ]\n", + "\n", + "10. Example for the logic_10:\n", + "when I receive [Power-Up v]\n", + " if <(Power-Up) = [Shield v]> then\n", + " set [shield v] to 1\n", + " end\n", + " [ Note:There are some mistake done by the llm to generate the above pseudo code:\n", + " 1. the value `1` should be enclosed in brackets `(1)` which is missing here.\n", + " 2. there should be `end` for the `when I receive [Power-Up v]` at the last line of pesudo-code.\n", + " 3. correct use of variables [Power-Up v] and options [Power-Up v],[Shield v] \n", + " ]\n", + "\n", + "11. Example for the logic_11:\n", + "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 then\n", + " broadcast [Game Over v]\n", + " end\n", + " end\n", + " [ Note:There are some mistake done by the llm to generate the above pseudo code:\n", + " 1. the value `50`,`100`, `5`, `240` and `-240` should be enclosed in brackets `(50)`,`(100)`, `(5)`, `(240)` and `(-240)` which is missing here.\n", + " 2. the reportor block such as `(x position)` when used as nesting as seen `if <(x position) > 240> then` and if <(x position) < -240> it should enclosed in `()` the correct use if <((x position)) < (-240)> and `if <((x position)) < (-240)>`\n", + " 3. correct use of options [Game Over v],[Cat v] \n", + " ]" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "1ee313ee", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1. Example for the logic_0:\n", + "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]\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic_1:\n", + "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]\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic_2:\n", + "when I receive [Level Up v]\n", + " change [level v] by 1\n", + " set [ballSpeed v] to (ballSpeed * 1.1)\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic_3:\n", + "when green flag clicked\n", + " go to x: 0 y: -100\n", + " forever\n", + " if then\n", + " jump\n", + " end\n", + " move 5 steps\n", + " if then\n", + " broadcast [Game Over v]\n", + " end\n", + " end\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic_4:\n", + "when [space v] key pressed\n", + " change y by 10\n", + " wait 0.2 seconds\n", + " change y by -10\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic_5:\n", + "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 then\n", + " broadcast [Game Over v]\n", + " end\n", + " end\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic_6:\n", + "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]\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic_7:\n", + "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]\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic_8:\n", + "when green flag clicked\n", + " go to x: 0 y: -130\n", + " set [shield v] to 0\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic_9:\n", + "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\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic_10:\n", + "when I receive [Power-Up v]\n", + " if <(Power-Up) = [Shield v]> then\n", + " set [shield v] to 1\n", + " end\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic_11:\n", + "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 then\n", + " broadcast [Game Over v]\n", + " end\n", + " end\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n" + ] + } + ], + "source": [ + "i=1\n", + "for key, value in all_logics.items():\n", + " print(f\"{1}. Example for the {key}:\\n{str(value)}\\n [Note:This generated json is ]\\n1.\\n2.\\n3.\\n\\n\")\n", + " i += 1\n", + "# txt = '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 then\\n broadcast [Game Over v]\\n end\\n end'\n", + "# print(str(txt))" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "4700799d", + "metadata": {}, + "outputs": [], + "source": [ + "all_logics={\"logic1\": \"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]\",\n", + "\"logic2\": \"when I receive [Game Over v]\\n broadcast [Reset Game v]\\n set [score v] to (0)\\n set [lives v] to (3)\",\n", + "\"logic3\": \"when green flag clicked\\n go to x: (0) y: (-100)\\n set [speed v] to (0)\",\n", + "\"logic4\": \"when [right arrow v] key pressed\\n change x by (10)\",\n", + "\"logic5\": \"when [left arrow v] key pressed\\n change x by (-10)\",\n", + "\"logic6\": \"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\",\n", + "\"logic7\": \"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 if <(x position) < (-235)> then\\n set x to (240)\\n end\\n if then\\n broadcast [Game Over v]\\n stop [all v]\\n end\\n end\",}\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4016c96d", + "metadata": {}, + "outputs": [], + "source": [ + "\"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]\",\n", + "\"logic\": \"when I receive [Game Over v]\\n broadcast [Reset Game v]\\n set [score v] to (0)\\n set [lives v] to (3)\",\n", + "\"logic\": \"when green flag clicked\\n go to x: (0) y: (-100)\\n set [speed v] to (0)\",\n", + "\"logic\": \"when [right arrow v] key pressed\\n change x by (10)\",\n", + "\"logic\": \"when [left arrow v] key pressed\\n change x by (-10)\",\n", + "\"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\",\n", + "\"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 if <(x position) < (-235)> then\\n set x to (240)\\n end\\n if then\\n broadcast [Game Over v]\\n stop [all v]\\n end\\n end\"," + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "2f66b647", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['logic1', 'logic2', 'logic3', 'logic4', 'logic5', 'logic6', 'logic7'])" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "all_logics.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "edff93c3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1. Example for the logic1:\n", + "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]\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic2:\n", + "when I receive [Game Over v]\n", + " broadcast [Reset Game v]\n", + " set [score v] to (0)\n", + " set [lives v] to (3)\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic3:\n", + "when green flag clicked\n", + " go to x: (0) y: (-100)\n", + " set [speed v] to (0)\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic4:\n", + "when [right arrow v] key pressed\n", + " change x by (10)\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic5:\n", + "when [left arrow v] key pressed\n", + " change x by (-10)\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic6:\n", + "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\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n", + "1. Example for the logic7:\n", + "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", + " if <(x position) < (-235)> then\n", + " set x to (240)\n", + " end\n", + " if then\n", + " broadcast [Game Over v]\n", + " stop [all v]\n", + " end\n", + " end\n", + " [Note:This generated json is ]\n", + "1.\n", + "2.\n", + "3.\n", + "\n", + "\n" + ] + } + ], + "source": [ + "i=1\n", + "for key, value in all_logics.items():\n", + " print(f\"{1}. Example for the {key}:\\n{str(value)}\\n [Note:This generated json is ]\\n1.\\n2.\\n3.\\n\\n\")\n", + " i += 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a396a819", + "metadata": {}, + "outputs": [], + "source": [ + "pseudo_code=\"\"\"\n", + "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]\n", + "\"\"\"\n", + "opcode_counts=[\n", + " {\n", + " \"opcode\": \"event_whenflagclicked\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"data_setvariableto\",\n", + " \"count\": 2\n", + " },\n", + " {\n", + " \"opcode\": \"data_showvariable\",\n", + " \"count\": 2\n", + " },\n", + " {\n", + " \"opcode\": \"event_broadcast\",\n", + " \"count\": 1\n", + " }\n", + " ]\n", + "pseudo_code=\"\"\"\n", + "when I receive [Game Over v]\n", + "broadcast [Reset Game v]\n", + "set [score v] to (0)\n", + "set [lives v] to (3)\n", + "\"\"\"\n", + "opcode_counts=[\n", + " {\n", + " \"opcode\": \"event_whenbroadcastreceived\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"event_broadcast\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"data_setvariableto\",\n", + " \"count\": 2\n", + " }\n", + " ]\n", + "\n", + "pseudo_code=\"\"\"\n", + "when green flag clicked\n", + " go to x: (0) y: (-100)\n", + " set [speed v] to (0)\n", + "\"\"\"\n", + "opcode_counts=[\n", + " {\n", + " \"opcode\": \"event_whenflagclicked\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_gotoxy\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"data_setvariableto\",\n", + " \"count\": 1\n", + " }\n", + " ]\n", + "\n", + "\n", + "pseudo_code=\"\"\"\n", + "when [right arrow v] key pressed\n", + " change x by (10)\n", + "\"\"\"\n", + "opcode_counts=[\n", + " {\n", + " \"opcode\": \"event_whenkeypressed\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_changexby\",\n", + " \"count\": 1\n", + " }\n", + " ]\n", + "\n", + "\n", + "\n", + "pseudo_code=\"\"\"\n", + "when [left arrow v] key pressed\n", + " change x by (-10)\n", + "\"\"\"\n", + "opcode_counts=[\n", + " {\n", + " \"opcode\": \"event_whenkeypressed\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_changexby\",\n", + " \"count\": 1\n", + " }\n", + " ]\n", + "\n", + "\n", + "pseudo_code=\"\"\"\n", + "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\n", + "\"\"\"\n", + "opcode_counts=[\n", + " {\n", + " \"opcode\": \"event_whenkeypressed\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"control_if\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"control_forever\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_changeyby\",\n", + " \"count\": 2\n", + " }\n", + " ]\n", + "\n", + "pseudo_code=\"\"\"\n", + "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", + " if <(x position) < (-235)> then\n", + " set x to (240)\n", + " end\n", + " if then\n", + " broadcast [Game Over v]\n", + " stop [all v]\n", + " end\n", + " end\n", + "\"\"\"\n", + "opcode_counts=[\n", + " {\n", + " \"opcode\": \"event_whenflagclicked\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_gotoxy\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_glidesecstoxy\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_xposition\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_setx\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"control_forever\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"control_if\",\n", + " \"count\": 2\n", + " },\n", + " {\n", + " \"opcode\": \"operator_lt\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"sensing_istouching\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"event_broadcast\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"control_stop\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"data_setvariableto\",\n", + " \"count\": 1\n", + " }\n", + " ]\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1ac34c30", + "metadata": {}, + "outputs": [], + "source": [ + "{\n", + " \"Stage\": {\n", + " \"description\": \"Background and global game state management, including broadcasts, rewards, and score.\",\n", + " \"plans\": [\n", + " {\n", + " \"event\": \"event_whenflagclicked\",\n", + " \"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]\",\n", + " \"motion\": [],\n", + " \"control\": [],\n", + " \"operator\": [],\n", + " \"sensing\": [],\n", + " \"looks\": [],\n", + " \"sounds\": [],\n", + " \"events\": [\n", + " \"event_broadcast\"\n", + " ],\n", + " \"data\": [\n", + " \"data_setvariableto\",\n", + " \"data_showvariable\"\n", + " ],\n", + " \"opcode_counts\": [\n", + " {\n", + " \"opcode\": \"event_whenflagclicked\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"data_setvariableto\",\n", + " \"count\": 2\n", + " },\n", + " {\n", + " \"opcode\": \"data_showvariable\",\n", + " \"count\": 2\n", + " },\n", + " {\n", + " \"opcode\": \"event_broadcast\",\n", + " \"count\": 1\n", + " }\n", + " ]\n", + " },\n", + " {\n", + " \"event\": \"event_whenbroadcastreceived\",\n", + " \"logic\": \"when I receive [Game Over v]\\n broadcast [Reset Game v]\\n set [score v] to (0)\\n set [lives v] to (3)\",\n", + " \"motion\": [],\n", + " \"control\": [],\n", + " \"operator\": [],\n", + " \"sensing\": [],\n", + " \"looks\": [],\n", + " \"sounds\": [],\n", + " \"events\": [],\n", + " \"data\": [\n", + " \"data_setvariableto\"\n", + " ],\n", + " \"opcode_counts\": [\n", + " {\n", + " \"opcode\": \"event_whenbroadcastreceived\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"event_broadcast\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"data_setvariableto\",\n", + " \"count\": 2\n", + " }\n", + " ]\n", + " }\n", + " ]\n", + " },\n", + " \"Cat\": {\n", + " \"description\": \"Main character (cat) actions\",\n", + " \"plans\": [\n", + " {\n", + " \"event\": \"event_whenflagclicked\",\n", + " \"logic\": \"when green flag clicked\\n go to x: (0) y: (-100)\\n set [speed v] to (0)\",\n", + " \"motion\": [\n", + " \"motion_gotoxy\"\n", + " ],\n", + " \"control\": [],\n", + " \"operator\": [],\n", + " \"sensing\": [],\n", + " \"looks\": [],\n", + " \"sounds\": [],\n", + " \"events\": [],\n", + " \"data\": [],\n", + " \"opcode_counts\": [\n", + " {\n", + " \"opcode\": \"event_whenflagclicked\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_gotoxy\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"data_setvariableto\",\n", + " \"count\": 1\n", + " }\n", + " ]\n", + " },\n", + " {\n", + " \"event\": \"event_whenkeypressed\",\n", + " \"logic\": \"when [right arrow v] key pressed\\n change x by (10)\",\n", + " \"motion\": [\n", + " \"motion_changexby\"\n", + " ],\n", + " \"control\": [],\n", + " \"operator\": [],\n", + " \"sensing\": [],\n", + " \"looks\": [],\n", + " \"sounds\": [],\n", + " \"events\": [],\n", + " \"data\": [],\n", + " \"opcode_counts\": [\n", + " {\n", + " \"opcode\": \"event_whenkeypressed\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_changexby\",\n", + " \"count\": 1\n", + " }\n", + " ]\n", + " },\n", + " {\n", + " \"event\": \"event_whenkeypressed\",\n", + " \"logic\": \"when [left arrow v] key pressed\\n change x by (-10)\",\n", + " \"motion\": [\n", + " \"motion_changexby\"\n", + " ],\n", + " \"control\": [],\n", + " \"operator\": [],\n", + " \"sensing\": [],\n", + " \"looks\": [],\n", + " \"sounds\": [],\n", + " \"events\": [],\n", + " \"data\": [],\n", + " \"opcode_counts\": [\n", + " {\n", + " \"opcode\": \"event_whenkeypressed\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_changexby\",\n", + " \"count\": 1\n", + " }\n", + " ]\n", + " },\n", + " {\n", + " \"event\": \"event_whenkeypressed\",\n", + " \"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\",\n", + " \"motion\": [\n", + " \"motion_changeyby\"\n", + " ],\n", + " \"control\": [\n", + " \"control_forever\",\n", + " \"control_if\"\n", + " ],\n", + " \"operator\": [],\n", + " \"sensing\": [],\n", + " \"looks\": [],\n", + " \"sounds\": [],\n", + " \"events\": [],\n", + " \"data\": [],\n", + " \"opcode_counts\": [\n", + " {\n", + " \"opcode\": \"event_whenkeypressed\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"control_if\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"control_forever\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_changeyby\",\n", + " \"count\": 2\n", + " }\n", + " ]\n", + " }\n", + " ]\n", + " },\n", + " \"ball\": {\n", + " \"description\": \"Obstacle movement and interaction\",\n", + " \"plans\": [\n", + " {\n", + " \"event\": \"event_whenflagclicked\",\n", + " \"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 \n", + " if <(x position) < (-235)> then\\n set x to (240)\\n end\\n if then\\n broadcast [Game Over v]\\n stop [all v]\\n end\\n end\",\n", + " \"motion\": [\n", + " \"motion_gotoxy\",\n", + " \"motion_glidesecstoxy\",\n", + " \"motion_xposition\",\n", + " \"motion_setx\"\n", + " ],\n", + " \"control\": [\n", + " \"control_forever\",\n", + " \"control_if\",\n", + " \"control_stop\"\n", + " ],\n", + " \"operator\": [\n", + " \"operator_lt\"\n", + " ],\n", + " \"sensing\": [\n", + " \"sensing_istouching\"\n", + " ],\n", + " \"looks\": [],\n", + " \"sounds\": [],\n", + " \"events\": [\n", + " \"event_broadcast\"\n", + " ],\n", + " \"data\": [],\n", + " \"opcode_counts\": [\n", + " {\n", + " \"opcode\": \"event_whenflagclicked\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_gotoxy\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_glidesecstoxy\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_xposition\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"motion_setx\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"control_forever\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"control_if\",\n", + " \"count\": 2\n", + " },\n", + " {\n", + " \"opcode\": \"operator_lt\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"sensing_istouching\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"event_broadcast\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"control_stop\",\n", + " \"count\": 1\n", + " },\n", + " {\n", + " \"opcode\": \"data_setvariableto\",\n", + " \"count\": 1\n", + " }\n", + " ]\n", + " }\n", + " ]\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6a14fb61", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n", + "None\n", + "None\n" + ] + } + ], + "source": [ + "def split_arithmetic(text: str):\n", + " \"\"\"\n", + " Splits `text` of the form \"(…) + (…)\" or \"a - b\" into (lhs_str, operator, rhs_str),\n", + " ignoring any + - * / inside parentheses.\n", + " Returns (None, None, None) if no top-level operator is found.\n", + " \"\"\"\n", + " depth = 0\n", + " for idx, ch in enumerate(text):\n", + " if ch == '(':\n", + " depth += 1\n", + " elif ch == ')':\n", + " depth -= 1\n", + " # If we find an operator at depth zero, that's our split point\n", + " elif ch in '+-*/' and depth == 0:\n", + " lhs = text[:idx].strip()\n", + " op = ch\n", + " rhs = text[idx+1:].strip()\n", + " return lhs, op, rhs\n", + " return None, None, None\n", + "\n", + "text = \"number 1) - (number 2\"\n", + "lhs_str, operator_symbol, rhs_str = split_arithmetic(text)\n", + "print(lhs_str)\n", + "print(operator_symbol)\n", + "print(rhs_str)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "07ffb848", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'lwrap': '(', 'left': '(x position', 'lwrap_close': ')', 'op': '*', 'rwrap': '(', 'right': 'x position) ) + ( (y position) * (y position) )', 'rwrap_close': ')'}\n", + " then` or `(((x position)) * (1))`.\n", + " - **Boolean blocks** in conditions must be inside `< >`, including nested ones: `>`, `< and >`,`< or >`.\n", + " - **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]?>`.\n", + " - **Operator expressions** must use explicit Scratch operator blocks, e.g.:\n", + " ```\n", + " (([ballSpeed v]) * (1.1))\n", + " ```\n", + " - **Every hat block script must end** with a final `end` on its own line.\n", + " \n", + "3. **Pseudo‑code formatting**:\n", + " - Represent each block or nested block on its own line.\n", + " - Indent nested blocks by 4 spaces under their parent (`forever`, `if`, etc.).\n", + " - No comments or explanatory text—just the block sequence.\n", + " - 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.\n", + "\n", + "4. **Logic content**:\n", + " - Build clear flow for mechanics (movement, jumping, flying, scoring, collisions).\n", + " - Match each action closely to a Scratch block or tight sequence.\n", + " - Do **NOT** include any justification or comments—only the raw logic.\n", + "\n", + "5. **Examples for reference**: \n", + "**Correct** pattern for a simple start script:\n", + " ```\n", + " 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]\n", + " end\n", + " ```\n", + "**Correct** pattern for updating the high score variable handling:\n", + " ```\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", + " ```\n", + "**Correct** pattern for level up and increase difficulty use:\n", + " ```\n", + " when I receive [Level Up v]\n", + " change [level v] by (1)\n", + " set [ballSpeed v] to ((([ballSpeed v]) * (1.1)))\n", + " end\n", + " ```\n", + "**Correct** pattern for jumping mechanics use:\n", + " ```\n", + " when [space v] key pressed\n", + " if <((y position)) = (-100)> then\n", + " repeat (5)\n", + " change y by (100)\n", + " wait (0.1) seconds\n", + " change y by (-100)\n", + " wait (0.1) seconds\n", + " end\n", + " end\n", + " end\n", + " ```\n", + "**Correct** pattern for continuos moving objects use:\n", + " ```\n", + " when green flag clicked\n", + " go to x: (240) y: (-100)\n", + " set [speed v] to (-5)\n", + " show variable [speed v]\n", + " forever\n", + " change x by ([speed v])\n", + " if <((x position)) < (-240)> then\n", + " go to x: (240) y: (-100)\n", + " end\n", + " end\n", + " end\n", + " ```\n", + "**Correct** pattern for continuos moving objects use:\n", + " ```\n", + " when green flag clicked\n", + " go to x: (240) y: (-100)\n", + " set [speed v] to (-5)\n", + " show variable [speed v]\n", + " forever\n", + " change x by ([speed v])\n", + " if <((x position)) < (-240)> then\n", + " go to x: (240) y: (-100)\n", + " end\n", + " end\n", + " end\n", + " ```\n", + "6. **Do not** add any explanation of logic or comments to justify or explain just put the logic content in the JSON.\n", + "7. **Output**: \n", + "Return **only** a JSON object, using double quotes everywhere, where the key for the pseudo-code will be the target name closest to the \"Script for:\" value. If no code-blocks are found, return `{\"refined_logic\": \"No Code-blocks\"}`.\n", + "\n", + "```json\n", + "{\n", + "\"refined_logic\":{\n", + " \"[Target name similar to script for]\": {\n", + " \"plan\":[\n", + " {\"pseudocode\":\"…your fully‑formatted pseudo‑code here…\"},\n", + " {\"pseudocode\":\"…your fully‑formatted pseudo‑code here…\"}\n", + " ]\n", + " }\n", + "}\n", + "}\n", + "```\n" + ] + } + ], + "source": [ + "\n", + "test=\"You are an expert in Scratch 3.0 game development, specializing in understanding block relationships (stacked, nested).\\n\\\"Analyze the Scratch code-block image and generate Pseudo-Code for what this logic appears to be doing.\\\"\\nFrom Image, you also have to detect a value of Key given in Text form \\\"Script for: \\\". Below is the example\\nExample: \\\"Script for: Bear\\\", \\\"Script for:\\\" is a key and \\\"Bear\\\" is value and check if there is related target name available.\\n\\n**Targets in Game (Sprites and Stage) available in project_json:** {', '.join(target_names)}\\n\\n--- Scratch 3.0 Block Reference ---\\n\\n### Hat Blocks\\nDescription: {hat_description}\\nBlocks:\\n{hat_opcodes_functionalities}\\n\\n### Boolean Blocks\\nDescription: {boolean_description}\\nBlocks:\\n{boolean_opcodes_functionalities}\\n\\n### C Blocks\\nDescription: {c_description}\\nBlocks:\\n{c_opcodes_functionalities}\\n\\n### Cap Blocks\\nDescription: {cap_description}\\nBlocks:\\n{cap_opcodes_functionalities}\\n\\n### Reporter Blocks\\nDescription: {reporter_description}\\nBlocks:\\n{reporter_opcodes_functionalities}\\n\\n### Stack Blocks\\nDescription: {stack_description}\\nBlocks:\\n{stack_opcodes_functionalities}\\n-----------------------------------\\n\\nYour task is to:\\nIf you don't find any \\\"Code-Blocks\\\" then,\\n **Don't generate Pseudo Code, and pass the message \\\"No Code-blocks\\\"\\nIf you find any \\\"Code-Blocks\\\" then,\\n1. **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.\\n\\n2. **Structural requirements**:\\n - **Numeric values** `(e.g., 0, 5, 0.2, -130)` **must** be in parentheses: `(0)`, `(5)`, `(0.2)`, `(-130)`.\\n - **AlphaNumeric values** `(e.g., hello, say 5, 4, hi!)` **must** be in parentheses: `(hello)`, `(say 5)`, `(4)`, `(hi!)`.\\n - **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])`.\\n - **Dropdown options** must be in the form `[option v]` (e.g., `[Game Start v]`, `[blue sky v]`). example use `when [space v] key pressed`.\\n - **Reporter blocks** used as inputs must be double‑wrapped: `((x position))`, `((y position))`. example use `if <((y position)) = (-130)> then` or `(((x position)) * (1))`.\\n - **Boolean blocks** in conditions must be inside `< >`, including nested ones: `>`, `< and >`,`< or >`.\\n - **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]?>`.\\n - **Operator expressions** must use explicit Scratch operator blocks, e.g.:\\n ```\\n (([ballSpeed v]) * (1.1))\\n ```\\n - **Every hat block script must end** with a final `end` on its own line.\\n \\n3. **Pseudo‑code formatting**:\\n - Represent each block or nested block on its own line.\\n - Indent nested blocks by 4 spaces under their parent (`forever`, `if`, etc.).\\n - No comments or explanatory text—just the block sequence.\\n - 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.\\n\\n4. **Logic content**:\\n - Build clear flow for mechanics (movement, jumping, flying, scoring, collisions).\\n - Match each action closely to a Scratch block or tight sequence.\\n - Do **NOT** include any justification or comments—only the raw logic.\\n\\n5. **Examples for reference**: \\n**Correct** pattern for a simple start script:\\n ```\\n 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]\\n end\\n ```\\n**Correct** pattern for updating the high score variable handling:\\n ```\\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 ```\\n**Correct** pattern for level up and increase difficulty use:\\n ```\\n when I receive [Level Up v]\\n change [level v] by (1)\\n set [ballSpeed v] to ((([ballSpeed v]) * (1.1)))\\n end\\n ```\\n**Correct** pattern for jumping mechanics use:\\n ```\\n when [space v] key pressed\\n if <((y position)) = (-100)> then\\n repeat (5)\\n change y by (100)\\n wait (0.1) seconds\\n change y by (-100)\\n wait (0.1) seconds\\n end\\n end\\n end\\n ```\\n**Correct** pattern for continuos moving objects use:\\n ```\\n when green flag clicked\\n go to x: (240) y: (-100)\\n set [speed v] to (-5)\\n show variable [speed v]\\n forever\\n change x by ([speed v])\\n if <((x position)) < (-240)> then\\n go to x: (240) y: (-100)\\n end\\n end\\n end\\n ```\\n**Correct** pattern for continuos moving objects use:\\n ```\\n when green flag clicked\\n go to x: (240) y: (-100)\\n set [speed v] to (-5)\\n show variable [speed v]\\n forever\\n change x by ([speed v])\\n if <((x position)) < (-240)> then\\n go to x: (240) y: (-100)\\n end\\n end\\n end\\n ```\\n6. **Do not** add any explanation of logic or comments to justify or explain just put the logic content in the JSON.\\n7. **Output**: \\nReturn **only** a JSON object, using double quotes everywhere, where the key for the pseudo-code will be the target name closest to the \\\"Script for:\\\" value. If no code-blocks are found, return `{\\\"refined_logic\\\": \\\"No Code-blocks\\\"}`.\\n\\n```json\\n{\\n\\\"refined_logic\\\":{\\n \\\"[Target name similar to script for]\\\": {\\n \\\"plan\\\":[\\n {\\\"pseudocode\\\":\\\"…your fully‑formatted pseudo‑code here…\\\"},\\n {\\\"pseudocode\\\":\\\"…your fully‑formatted pseudo‑code here…\\\"}\\n ]\\n }\\n}\\n}\\n```\"\n", + "print(test)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "e3fac594", + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "BASE_DIR = Path.cwd() \n", + "#BASE_DIR = Path(__file__).parent\n", + "BLOCKS_DIR = BASE_DIR / \"blocks\"\n", + "STATIC_DIR = BASE_DIR / \"static\"\n", + "GEN_PROJECT_DIR = BASE_DIR / \"generated_projects\"\n", + "BACKDROP_DIR = BLOCKS_DIR / \"Backdrops\"\n", + "SPRITE_DIR = BLOCKS_DIR / \"sprites\"\n", + "OUTPUT_DIR = BASE_DIR / \"OUTPUTS\"\n", + "DETECTED_IMAGE_DIR = OUTPUT_DIR / \"DETECTED_IMAGE\"\n", + "IMAGE_DIR = OUTPUT_DIR / \"SCANNED_IMAGE\"\n", + "JSON_DIR = OUTPUT_DIR / \"EXTRACTED_JSON\"\n", + "\n", + "folder_image_paths = folder_image_paths = [\n", + " #backdrops\n", + " BACKDROP_DIR / \"badroom3.sb3\" / \"8cc0b88d53345b3e337e8f028a32a4e7.png\",\n", + " BACKDROP_DIR / \"baseball2.sb3\" / \"7be1f5b3e682813dac1f297e52ff7dca.png\",\n", + " BACKDROP_DIR / \"beach_malibu.sb3\" / \"050615fe992a00d6af0e664e497ebf53.png\",\n", + " BACKDROP_DIR / \"castle2.sb3\" / \"951765ee7f7370f120c9df20b577c22f.png\",\n", + " BACKDROP_DIR / \"hall.sb3\" / \"ea86ca30b346f27ca5faf1254f6a31e3.png\",\n", + " BACKDROP_DIR / \"jungle.sb3\" / \"f4f908da19e2753f3ed679d7b37650ca.png\",\n", + " #sprite\n", + " SPRITE_DIR / \"Batter.sprite3\" / \"baseball_sprite_motion_1.png\",\n", + " SPRITE_DIR / \"Bear.sprite3\" / \"bear_motion_2.png\",\n", + " SPRITE_DIR / \"Beetle.sprite3\" / \"46d0dfd4ae7e9bfe3a6a2e35a4905eae.png\",\n", + " SPRITE_DIR / \"cat\" / \"cat_motion_1.png\",\n", + " SPRITE_DIR / \"Centaur.sprite3\" / \"2373556e776cad3ba4d6ee04fc34550b.png\",\n", + " SPRITE_DIR / \"Crab.sprite3\" / \"bear_element.png\",\n", + " SPRITE_DIR / \"Soccer Ball.sprite3\" / \"cat_football.png\",\n", + " ]" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "ee3c8d01", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[WindowsPath('d:/DEV PATEL/2025/scratch_VLM/scratch_agent/utils/blocks/Backdrops/badroom3.sb3/8cc0b88d53345b3e337e8f028a32a4e7.png'),\n", + " WindowsPath('d:/DEV PATEL/2025/scratch_VLM/scratch_agent/utils/blocks/Backdrops/baseball2.sb3/7be1f5b3e682813dac1f297e52ff7dca.png'),\n", + " WindowsPath('d:/DEV PATEL/2025/scratch_VLM/scratch_agent/utils/blocks/Backdrops/beach_malibu.sb3/050615fe992a00d6af0e664e497ebf53.png'),\n", + " WindowsPath('d:/DEV PATEL/2025/scratch_VLM/scratch_agent/utils/blocks/Backdrops/castle2.sb3/951765ee7f7370f120c9df20b577c22f.png'),\n", + " WindowsPath('d:/DEV PATEL/2025/scratch_VLM/scratch_agent/utils/blocks/Backdrops/hall.sb3/ea86ca30b346f27ca5faf1254f6a31e3.png'),\n", + " WindowsPath('d:/DEV PATEL/2025/scratch_VLM/scratch_agent/utils/blocks/Backdrops/jungle.sb3/f4f908da19e2753f3ed679d7b37650ca.png'),\n", + " WindowsPath('d:/DEV PATEL/2025/scratch_VLM/scratch_agent/utils/blocks/sprites/Batter.sprite3/baseball_sprite_motion_1.png'),\n", + " WindowsPath('d:/DEV PATEL/2025/scratch_VLM/scratch_agent/utils/blocks/sprites/Bear.sprite3/bear_motion_2.png'),\n", + " WindowsPath('d:/DEV PATEL/2025/scratch_VLM/scratch_agent/utils/blocks/sprites/Beetle.sprite3/46d0dfd4ae7e9bfe3a6a2e35a4905eae.png'),\n", + " WindowsPath('d:/DEV PATEL/2025/scratch_VLM/scratch_agent/utils/blocks/sprites/cat/cat_motion_1.png'),\n", + " WindowsPath('d:/DEV PATEL/2025/scratch_VLM/scratch_agent/utils/blocks/sprites/Centaur.sprite3/2373556e776cad3ba4d6ee04fc34550b.png'),\n", + " WindowsPath('d:/DEV PATEL/2025/scratch_VLM/scratch_agent/utils/blocks/sprites/Crab.sprite3/bear_element.png'),\n", + " WindowsPath('d:/DEV PATEL/2025/scratch_VLM/scratch_agent/utils/blocks/sprites/Soccer Ball.sprite3/cat_football.png')]" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "folder_image_paths" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9475e0ac", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "scratch_env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}