| import gradio as gr |
| import torch |
| from transformers import AutoModelForCausalLM, AutoTokenizer |
| from scratch3 import Project |
|
|
| |
| model_name = "TheBloke/LLaMa-7B-GGML" |
| tokenizer = AutoTokenizer.from_pretrained(model_name) |
| model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto") |
|
|
| |
| scratch_blocks = { |
| "motion": [ |
| {"name": "move", "parameters": {"steps": "number"}}, |
| {"name": "turn", "parameters": {"angle": "number"}}, |
| {"name": "go to", "parameters": {"x": "number", "y": "number"}}, |
| {"name": "point in direction", "parameters": {"direction": "number"}} |
| ], |
| "sound": [ |
| {"name": "play sound", "parameters": {"sound": "string"}}, |
| {"name": "stop all sounds", "parameters": {}} |
| ], |
| "event": [ |
| {"name": "when green flag clicked", "parameters": {}}, |
| {"name": "when this sprite clicked", "parameters": {}} |
| ], |
| "control": [ |
| {"name": "wait", "parameters": {"seconds": "number"}}, |
| {"name": "repeat", "parameters": {"times": "number"}}, |
| {"name": "if then", "parameters": {"condition": "string"}}, |
| ], |
| "variables": [ |
| {"name": "set variable", "parameters": {"variable": "string", "value": "number"}}, |
| {"name": "change variable", "parameters": {"variable": "string", "value": "number"}}, |
| ], |
| "looks": [ |
| {"name": "say", "parameters": {"message": "string"}}, |
| {"name": "think", "parameters": {"message": "string"}}, |
| {"name": "show", "parameters": {}}, |
| {"name": "hide", "parameters": {}} |
| ], |
| "drawing": [ |
| {"name": "draw line", "parameters": {"x1": "number", "y1": "number", "x2": "number", "y2": "number"}}, |
| {"name": "draw circle", "parameters": {"x": "number", "y": "number", "radius": "number"}}, |
| {"name": "draw rectangle", "parameters": {"x": "number", "y": "number", "width": "number", "height": "number"}}, |
| ] |
| } |
|
|
| def generate_script(prompt): |
| """ |
| Generate a Scratch script based on the provided prompt using the AI model. |
| """ |
| inputs = tokenizer(prompt, return_tensors="pt").to(model.device) |
| with torch.no_grad(): |
| outputs = model.generate(inputs["input_ids"], max_length=300, num_return_sequences=1) |
| |
| generated_script = tokenizer.decode(outputs[0], skip_special_tokens=True) |
| return generated_script |
|
|
| def parse_generated_script(generated_script): |
| """ |
| Parse the generated script into instructions. |
| """ |
| instructions = [] |
| for line in generated_script.split('.'): |
| line = line.strip() |
| if line: |
| instructions.append(line) |
| return instructions |
|
|
| def create_scratch_project(title, sprites, sounds, custom_prompt): |
| """ |
| Create a Scratch project based on user input and generated script. |
| """ |
| |
| prompt = f"Create a Scratch project titled '{title}' featuring sprites {sprites} with sounds {sounds}. " \ |
| f"Additional details: {custom_prompt}" |
| generated_script = generate_script(prompt) |
|
|
| |
| instructions = parse_generated_script(generated_script) |
|
|
| |
| project = Project() |
|
|
| |
| sprite_list = [sprite.strip() for sprite in sprites.split(',')] |
| for sprite in sprite_list: |
| sprite_block = { |
| "objName": sprite, |
| "skin": 1, |
| "x": 0, |
| "y": 0, |
| "direction": 90, |
| "visible": True, |
| "size": 100 |
| } |
| project.sprites.append(sprite_block) |
|
|
| |
| for sound in sounds.split(','): |
| sound = sound.strip() |
| if sound: |
| sound_block = { |
| "name": sound, |
| "path": f"sounds/{sound}.wav" |
| } |
| project.sounds.append(sound_block) |
|
|
| |
| for instruction in instructions: |
| if "move" in instruction: |
| steps = extract_number(instruction) |
| project.add_motion_block({"name": "move", "parameters": {"steps": steps}}) |
| elif "turn" in instruction: |
| angle = extract_number(instruction) |
| project.add_motion_block({"name": "turn", "parameters": {"angle": angle}}) |
| elif "play sound" in instruction: |
| project.add_sound_block({"name": "play sound", "parameters": {"sound": sounds.split(',')[0].strip()}}) |
| elif "when green flag clicked" in instruction: |
| project.add_event_block({"name": "when green flag clicked"}) |
| elif "repeat" in instruction: |
| times = extract_number(instruction) |
| project.add_control_block({"name": "repeat", "parameters": {"times": times}}) |
| elif "say" in instruction: |
| message = extract_message(instruction) |
| project.add_looks_block({"name": "say", "parameters": {"message": message}}) |
| elif "draw line" in instruction: |
| x1, y1, x2, y2 = extract_drawing_parameters(instruction) |
| project.add_drawing_block({"name": "draw line", "parameters": {"x1": x1, "y1": y1, "x2": x2, "y2": y2}}) |
| elif "draw circle" in instruction: |
| x, y, radius = extract_circle_parameters(instruction) |
| project.add_drawing_block({"name": "draw circle", "parameters": {"x": x, "y": y, "radius": radius}}) |
| elif "draw rectangle" in instruction: |
| x, y, width, height = extract_rectangle_parameters(instruction) |
| project.add_drawing_block({"name": "draw rectangle", "parameters": {"x": x, "y": y, "width": width, "height": height}}) |
| elif "remove sprite" in instruction: |
| sprite_name = extract_sprite_name(instruction) |
| project.remove_sprite(sprite_name) |
| elif "rename sprite" in instruction: |
| old_name, new_name = extract_rename_sprite_parameters(instruction) |
| project.rename_sprite(old_name, new_name) |
|
|
| |
| sb3_file_path = f"{title}.sb3" |
| with open(sb3_file_path, "wb") as f: |
| f.write(project.export()) |
| |
| return sb3_file_path |
|
|
| def extract_number(instruction): |
| """ |
| Extract a number from the instruction string. |
| """ |
| import re |
| match = re.search(r'\d+', instruction) |
| return int(match.group(0)) if match else 0 |
|
|
| def extract_message(instruction): |
| """ |
| Extract a message from the instruction string. |
| """ |
| return instruction.split("say")[-1].strip().strip('"') |
|
|
| def extract_drawing_parameters(instruction): |
| """ |
| Extract parameters for drawing a line from the instruction string. |
| """ |
| numbers = list(map(int, re.findall(r'\d+', instruction))) |
| return numbers |
|
|
| def extract_circle_parameters(instruction): |
| """ |
| Extract parameters for drawing a circle from the instruction string. |
| """ |
| numbers = list(map(int, re.findall(r'\d+', instruction))) |
| return numbers |
|
|
| def extract_rectangle_parameters(instruction): |
| """ |
| Extract parameters for drawing a rectangle from the instruction string. |
| """ |
| numbers = list(map(int, re.findall(r'\d+', instruction))) |
| return numbers |
|
|
| def extract_sprite_name(instruction): |
| """ |
| Extract the sprite name from the instruction string for removal. |
| """ |
| return instruction.split("remove sprite")[-1].strip() |
|
|
| def extract_rename_sprite_parameters(instruction): |
| """ |
| Extract old and new names for renaming a sprite from the instruction string. |
| """ |
| parts = instruction.split("rename sprite")[-1].strip().split("to") |
| old_name = parts[0].strip() |
| new_name = parts[1].strip() if len(parts) > 1 else None |
| return old_name, new_name |
|
|
| |
| iface = gr.Interface( |
| fn=create_scratch_project, |
| inputs=[ |
| gr.Textbox(label="Project Title"), |
| gr.Textbox(label="Sprite Names (comma separated)"), |
| gr.Textbox(label="Sound Names (comma separated)"), |
| gr.Textbox(label="Custom Prompt (optional)"), |
| ], |
| outputs=gr.File(label="Download Your Scratch Project (.sb3)"), |
| title="Scratch Project Generator", |
| description="Create a Scratch project using AI. Provide a title, sprites, sounds, and a custom prompt." |
| ) |
|
|
| |
| iface.launch() |
|
|