Discuss Scratch

NFlex23
Scratcher
1000+ posts

Boiga: Generate Scratch code using Python

I've successfully implemented my scope engine in Boiga. I also made it portable by abstracting it into a class so multiple sprites can have the same engine.
from boiga import *

class ScopeEngine:
def __init__(self, sprite, error_func = lambda locals, message: []):
current_scope_names = sprite.new_list('current scope names')
current_scope_values = sprite.new_list('current scope values')
scopes = sprite.new_list('scopes', monitor=[320, 0, 160, 120])
variable_names = sprite.new_list('variable names', monitor=[0, 0, 160, 120])
variable_values = sprite.new_list('variable values', monitor=[160, 0, 160, 120])

@sprite.proc_def('Error: message [message]')
def error(locals, message): return error_func(locals, message)

@sprite.proc_def('Reset')
def reset(locals): return [
scopes.delete_all(),
variable_names.delete_all(),
variable_values.delete_all(),
]

@sprite.proc_def('Get scope [scope]', locals_prefix='[get scope] _')
def get_scope(locals, scope): return [
current_scope_names.delete_all(),
current_scope_values.delete_all(),
locals.i[scopes.item(scope):variable_names.len()+1] >> [
current_scope_names.append(variable_names.item(locals.i)),
current_scope_values.append(variable_values.item(locals.i)),
],
]

@sprite.proc_def('Open scope')
def open_scope(locals): return [
scopes.append(variable_names.len() + 1),
]

@sprite.proc_def('Close scope')
def close_scope(locals): return [
If (scopes.len() == 0) [
error("Can't close nonexistent scope")
].Else [
RepeatUntil(variable_names.len() == scopes.item('last') - 1) [
variable_names.delete_at1(scopes.item('last')),
variable_values.delete_at1(scopes.item('last')),
],
scopes.delete_at1('last'),
],
]

@sprite.proc_def('Define variable: name [name] value [value]')
def define_variable(locals, name, value): return [
get_scope('last'),
If (current_scope_names.contains(name)) [
error(Literal('Variable already exists, "').join(name).join('"')),
].Else [
variable_names.append(name),
variable_values.append(value),
],
]

@sprite.proc_def('Set variable: name [name] value [value]', locals_prefix='[set variable] _')
def set_variable(locals, name, value): return [
If (variable_names.contains(name).NOT()) [
error(Literal('Variable not found, "').join(name).join('"')),
].Else [
locals.i <= variable_names.len(),
Forever [
If (variable_names.item(locals.i) == name) [
variable_values.item(locals.i) <= value,
StopThisScript(),
],
locals.i.changeby(-1),
],
],
]

@sprite.proc_def('Get variable: name [name]', locals_prefix='[get variable] _')
def get_variable(locals, name): return [
If (variable_names.contains(name).NOT()) [
error(Literal('Variable not found, "').join(name).join('"')),
].Else [
locals.i <= variable_names.len(),
Forever [
If (variable_names.item(locals.i) == name) [
locals.value <= variable_values.item(locals.i),
StopThisScript(),
],
locals.i.changeby(-1),
],
],
]

self.sprite = sprite
self.error = error
self.reset = reset
self.get_scope = get_scope
self.open_scope = open_scope
self.close_scope = close_scope
self.define_variable = define_variable
self.set_variable = set_variable
self.get_variable = get_variable
from boiga import *
from scope_engine import ScopeEngine

project = Project()
sprite = project.new_sprite('Engine')
demo = sprite.new_list('demo', monitor=[0, 120, 480, 240])

engine = ScopeEngine(sprite, lambda _, message: [
demo.delete_at1('last'),
demo.append(Literal('error> ').join(message)),
])

sprite.on_flag([
Hide(),
demo.delete_all(),
engine.reset(),
engine.open_scope(),
demo.append('[o]pen scope; [c]lose scope; [d]efine variable; [s]et variable; [g]et variable'),
])

sprite.on_press('o', [
demo.append('> New scope opened'),
engine.open_scope(),
])

sprite.on_press('c', [
demo.append('> Last scope closed'),
engine.close_scope(),
])

demo_name = sprite.new_var('[demo] _name')
sprite.on_press('d', [
AskAndWait('Name'),
demo_name <= Answer(),
AskAndWait('Value'),
demo.append(
Literal('> Variable "')
.join(demo_name)
.join('" defined with value "')
.join(Answer())
.join('"')
),
engine.define_variable(demo_name, Answer()),
])

sprite.on_press('s', [
AskAndWait('Name'),
demo_name <= Answer(),
AskAndWait('Value'),
demo.append(
Literal('> Variable "')
.join(demo_name)
.join('" set to value "')
.join(Answer())
.join('"')
),
engine.set_variable(demo_name, Answer()),
])

sprite.on_press('g', [
AskAndWait('Name'),
demo.append(Literal('> Getting value of "').join(Answer()).join('"')),
engine.get_variable(Answer()),
If (
(demo.item('last')[4] == 'r').AND
(demo.item('last')[5] == '>').NOT()
) [
demo.append(engine.get_variable.value),
],
])

project.save('scope.sb3')
I didn't use syntax highlighting so the code doesn't get squished together, which looks even worse than having no highlighting IMO.

Last edited by NFlex23 (June 22, 2022 21:51:16)

MagicCrayon9342
Scratcher
1000+ posts

Boiga: Generate Scratch code using Python

Alright… 3D game engine?
Retr0id
Scratcher
68 posts

Boiga: Generate Scratch code using Python

NFlex23 wrote:

I've successfully implemented my scope engine in Boiga. I also made it portable by abstracting it into a class so multiple sprites can have the same engine.

-snip-

Very cool
NFlex23
Scratcher
1000+ posts

Boiga: Generate Scratch code using Python

Retr0id wrote:

(#23)

NFlex23 wrote:

I've successfully implemented my scope engine in Boiga. I also made it portable by abstracting it into a class so multiple sprites can have the same engine.

-snip-

Very cool
Thanks! Boiga is super cool… but why is it called “Boiga”?
Retr0id
Scratcher
68 posts

Boiga: Generate Scratch code using Python

NFlex23 wrote:

Retr0id wrote:

(#23)

NFlex23 wrote:

I've successfully implemented my scope engine in Boiga. I also made it portable by abstracting it into a class so multiple sprites can have the same engine.

-snip-

Very cool
Thanks! Boiga is super cool… but why is it called “Boiga”?

Thank you too. That's a good question…

https://en.wikipedia.org/wiki/Boiga
“Boiga is a large genus of opisthoglyphous (rear-fanged), mildly venomous snakes, known commonly as cat-eyed snakes or simply cat snakes”

Scratch Cat + Python Snake => Cat Snake => Boiga
imfh
Scratcher
1000+ posts

Boiga: Generate Scratch code using Python

NFlex23 wrote:

MagicCrayon9342 wrote:

(#17)
4K 3D Scratch racing game?
Scratch isn't capable of 4K.
You can put a high resolution image into a vector costume and then change the costume size so the image fits on the stage. (example)
NFlex23
Scratcher
1000+ posts

Boiga: Generate Scratch code using Python

Retr0id wrote:

(#25)

NFlex23 wrote:

Retr0id wrote:

(#23)

NFlex23 wrote:

I've successfully implemented my scope engine in Boiga. I also made it portable by abstracting it into a class so multiple sprites can have the same engine.

-snip-

Very cool
Thanks! Boiga is super cool… but why is it called “Boiga”?

Thank you too. That's a good question…

https://en.wikipedia.org/wiki/Boiga
“Boiga is a large genus of opisthoglyphous (rear-fanged), mildly venomous snakes, known commonly as cat-eyed snakes or simply cat snakes”

Scratch Cat + Python Snake => Cat Snake => Boiga
That's really neat and unique.
NFlex23
Scratcher
1000+ posts

Boiga: Generate Scratch code using Python

Bump. Has anyone else made anything cool with Boiga?
MagicCrayon9342
Scratcher
1000+ posts

Boiga: Generate Scratch code using Python

I've been meaning to try Boiga, it's in the queue of ideas. My time is being invested in rust though.
NFlex23
Scratcher
1000+ posts

Boiga: Generate Scratch code using Python

Bump. I think a library like this but for Node.js would be really cool.
Retr0id
Scratcher
68 posts

Boiga: Generate Scratch code using Python

By the way, I have a cool project coming up which will make use of boiga - I'm going to port cnlohr's rv32ima emulator to Scratch, such that it can boot real Linux. It'll probably be very slow, but it should *work* at least.
NFlex23
Scratcher
1000+ posts

Boiga: Generate Scratch code using Python

Retr0id wrote:

By the way, I have a cool project coming up which will make use of boiga - I'm going to port cnlohr's rv32ima emulator to Scratch, such that it can boot real Linux. It'll probably be very slow, but it should *work* at least.
That's a good idea; I'll definitely check it out when its finished.
GFXAJ
New Scratcher
1 post

Boiga: Generate Scratch code using Python

Any similar repo (except for leopard) that supports ts / js env development?
caftingdead261
Scratcher
100+ posts

Boiga: Generate Scratch code using Python

GFXAJ wrote:

Any similar repo (except for leopard) that supports ts / js env development?
Please don't Necropost the last post on this topic was 3 years ago

Last edited by caftingdead261 (April 4, 2025 17:09:35)

gilbert_given_189
Scratcher
1000+ posts

Boiga: Generate Scratch code using Python

I wish there's a better way of “decorating” sprites than this monstrosity I devised:
from boiga import *
from boiga.boiga.codegen import *
from utils import Namespace, For, export
__namespace__ = Namespace("Shoutbox")  # appends "Shoutbox." to a bunch of things
_ns = __namespace__
def impl_reader(sprite: Sprite) -> Sprite:
    """Provides procedures to read Shoutbox messages."""
    number = sprite.new_var(_ns("number"))
    source = sprite.new_var(_ns("source"))
    dest = sprite.new_var(_ns("dest"))
    subject_id = sprite.new_var(_ns("subject_id"))
    subject = sprite.new_var(_ns("subject"))
    body = sprite.new_var(_ns("body"))
    @sprite.proc_def("Shoutbox: parse message [msg]")
    @__namespace__
    def parse(locals, msg): return [
        locals.i <= 1,
        # A Shoutbox message is structured like this:
        # NUM:SOURCE>DEST:SUBJECT.ID=BODY
        # - NUM: The global message number.
        # - SOURCE: The sender clone ID of this message.
        # - DEST: The recepient clone ID of this message.
        # - SUBJECT: What the message is about.
        # - ID (optional): An identifier of this message, used for marking interprogram discussions.
        # - BODY: The main matter of the message.
        number <= "",
        source <= "",
        dest <= "",
        subject_id <= "",
        subject <= "",
        body <= "",
        *For (None, msg.item(locals.i) == ":", locals.i.changeby(1)) [  # bespoke While loop
            number <= number.join(msg.item(locals.i)),
        ],
        locals.i.changeby(1),  # Skip the ":"
        *For (None, msg.item(locals.i) == ">", locals.i.changeby(1)) [
            source <= source.join(msg.item(locals.i)),
        ],
        locals.i.changeby(1),  # Skip the ">"
        *For (None, msg.item(locals.i) == ":", locals.i.changeby(1)) [
            dest <= dest.join(msg.item(locals.i)),
        ],
        locals.i.changeby(1),  # Skip the ":"
        *For (None, (msg.item(locals.i) == "=").OR(msg.item(locals.i) == "."), locals.i.changeby(1)) [
            subject <= subject.join(msg.item(locals.i)),
        ],
        If (msg.item(locals.i) == ".") [
            locals.i.changeby(1),
            *For (None, msg.item(locals.i) == "=", locals.i.changeby(1)) [
                subject_id <= subject_id.join(msg.item(locals.i)),
            ],
        ],
        locals.i.changeby(1),  # Skip either the "=" or the "."
        *For (None, msg.item(locals.i) == "", locals.i.changeby(1)) [
            body <= body.join(msg.item(locals.i)),
        ],
    ]
    return export(_ns.name, locals())  # makes a pseudo-module (which is just a named tuple)
# There should be a impl_writer decorator which I didn't implement before giving up
from boiga import *
from shoutbox import *
project = Project()
test = project.new_sprite("test")
Shoutbox = impl_reader(test)

I can only do this because I'm already very fluent at Python; if this was written in Lua or—God forbid—Rust, I would probably have stopped very early on the project

Last edited by gilbert_given_189 (April 4, 2025 17:58:53)

Powered by DjangoBB