scratch_agent / utils /plan_generator_3.py
WebashalarForML's picture
Upload 24 files
3d3703f verified
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": "<not <>>", "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": "<touching [edge v]?>", "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": "<touching color ()?>", "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": "<color () is touching ()?>", "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": "<key () pressed?>", "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": "<mouse down?>", "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
}
}
# <not <>> (operator_not)
m = re.search(r"<not\s*(.+?)\s*>", 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
}
}
# <touching [edge v]?> (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}
}
}
# <touching color ()?> (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}
}
}
# <color () is touching ()?> (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}
}
}
# <key () pressed?> (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}
}
}
# <mouse down?> (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 <cond> then' or 'if <cond> 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 <cond>'
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 <touching [edge v]?>"
{"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 "<touching color [#FF0000]?>"
{"opcode":"sensing_coloristouchingcolor","count":1}, # For "<color [#00FF00] is touching [#FF0000]?>"
{"opcode":"operator_and","count":1}, # For "<<mouse down?> and <touching [mouse-pointer]?> >"
{"opcode":"operator_or","count":1}, # For "<<key [left arrow v] pressed?> or <key [a v] pressed?>>"
{"opcode":"operator_not","count":1}, # For "<not <touching [Sprite2 v]?>>"
{"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 = [
# 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 <key [space v] pressed?> 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 <touching [color (red) v]?> 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 <touching [edge v]?>
move (5) steps
end
""",
"""
if <(health) = (0)> then
stop [all v]
end
""",
"""
when I start as a clone
wait until <touching [edge v]?>
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 <<mouse down?> and <touching [mouse-pointer]?> > then
say [You're clicking me!]
end
""",
"""
if <<key [left arrow v] pressed?> or <key [a v] pressed?>> then
change x by (-10)
end
""",
"""
if <not <touching [Sprite2 v]?>> then
say [I'm safe!]
end
""",
"""
if <[answer] contains [yes]?> then
say [Great!]
end
""",
"""
if <touching [Sprite v]?> then
broadcast [Game Over v]
end
""",
"""
if <touching [edge v]?> then
bounce off edge
end
""",
"""
if <touching color [#FF0000]?> then
change [health v] by (-1)
end
""",
"""
if <color [#00FF00] is touching [#FF0000]?> then
say [Collision!]
end
""",
"""
forever
if <key [space v] pressed?> then
broadcast [shoot v]
end
end
""",
"""
if <mouse down?> 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)