If you create a new text editor using the gui library, the :detach() function of the returned editor doesn't work as you would expect it to, only detaching the content of the editor, not the editor itself. This is an issue if you create/remove a number of editors, as each created editor still uses a small amount of CPU even after being detached.
This is caused because :attach_text_editor() returns the content, not the container, but it is the container that is added to the gui.
There is a simple workaround for this - instead of using text_ed:detach(), use text_ed.parent:detach(), where text_ed is the variable holding what is returned from :attach_text_editor(). However, this is not very intuitive for the user.
A simple fix would be to overwrite the :detach() function for the content so that it detaches its parent instead. I can't see many instances where this would be an issue/you'd actually want to detach just the content?
Hope this helps!
Perfect Pastry Parcels
Help Ingrid pack a selection of delicious pastries into neat little boxes as quickly as possible! But beware, some of her customers has ordered some oddly shaped brownies...
Pack a few small boxes with A Sweet Treat, or take on the ultimate pastry challenge with The Baker's Dozen - 13 boxes, each more challenging than the last!
Each puzzle is randomly generated, meaning you can play and compete for the best time over and over. Can you do better than Ingrid herself?
Controls
Click and drag the pastries into the boxes and right click, double click or scroll to rotate them. Once the boxes are full, they automatically close and the next one arrives!
.jpg)
Following some experimentation, I've found a couple of issues with sandboxed apps and how they are given permissions to read/write files outside of the 'special locations'.
Loading cart with sandboxed workspace app can cause data loss
When you load a cart, Picotron helpfully reopens the workspaces you were using to edit that cart. If one of those workspaces is a sandboxed app, however, the app appears to be given write access but not read access.
This results in the app 'opening' the files, but just creating blank, new files and then immediately saving the blank versions over the original files in RAM. This happens without the user initiating a save or doing anything beyond loading the cart. This makes it very easy for a user to lose data if they save the cart before noticing the issue (yes, I am speaking from experience...)
This assumes all the loading/saving is done through wrangle_working_file, I haven't tested otherwise.
The behaviour can be replicated by creating a sandboxed copy of code.p64 and using it to edit a cart, then saving and reloading that cart.
Strawberry Src
A slightly-less-simple code & text editor
Author: Matt Sutton / @xietanu.bsky.social
Version: 1.2.3
Overview
Strawberry Src is intended as an equivalent app to Picotron's in-built code or text editors, with new quality or life improvements to make navigating and editing code and text easier.
TIP:
You can set Strawberry Src as your default editor using the default_app command in the terminal.
Go to the terminal and type:
default_app <extension> <path to Strawberry Src cart>
E.g.
default_app lua /desktop/strawberry_src.p64
Change log

As the title says, attempting to unpod the string, "str", whether typed directly or stored in a variable causes either an out of memory error, or fully crashes picotron to the desktop - though the latter is rarer.
Attempting to unpod other strings just returns nil, which seems like the behaviour I would expect?
I've not found any other strings which cause this behaviour.
Hope this is helpful!

When using batch drawing with sprites (i.e. supplying spr with a userdata), the sprites are drawn ignoring how camera is set using camera().
For example, if the userdata gives the position x=10, y=10, the sprite will always be drawn 10 pixels from the top of the screen and 10 from the left, regardless of how camera() has been set.
This isn't how other batch drawing operations work (which do seem to respect the camera) or how spr() works normally, and also doesn't (by my reading) match what's documented here: https://www.lexaloffle.com/dl/docs/picotron_gfx_pipeline.html. As such, I assume this isn't intended and is a bug?
Fireworks over Düsseldorf
A simple demo/screensaver that came about from a desire to get my head around userdata operations, particle systems and batch drawing, along with some affection for my current home.
Has five different displays, each of which have some variations. Cycles through them randomly, tetris-bag style.
Code is lightly commented in case anyone wants to take a look!

A bit of a weird one...
I'm getting random results from code that doesn't have any random/changing elements.
I have a cart that, whenever I run it, it has one of two behaviours, apparently at random? However, the code doesn't involve any randomness (and I seeded the random number generator just in case), and doesn't load anything from the disk or mess around with memory in weird ways - I would expect the results of this code to be completely deterministic prior to user input. It should just execute one line at a time, with all the same initial values. However, the result is something that is inconsistent.
This is demonstrated below. All I am doing below is hitting ctrl-r to run the cartridge. You can see when this happens by the timer at the bottom. Each time I run it, shows one of two behaviours, seemingly at random:
This happens both when running the cart from the RAM and running it normally, it's just easier to demonstrate from the RAM.
The code for determining the dimensions of the white rectangles is a bit complicated and involves quite a few recursive calls, which I'm guessing might be the cause of this? e.g. behind the scenes Picotron's doing some stuff in parallel that's leading to a weird race condition?

SpaghettiCode - a Picotron code editor
v0.2.0 - alpha
SpaghettiCode is a code editor for Picotron that aims to provide more features than the built-in code editor, providing a little extra help and quality of life.
Important notes before you start using SpaghettiCode!
SpaghettiCode is in alpha
Please back up your carts before editing them with SpaghettiCode! I am not aware of any bugs or issues that could lead to corruption or data loss, but until it has seen a bit more of a shakedown, I don't want to risk being responsible for anyone losing work.
Also, let me know if you run into weird issues or have ideas! Feedback would be greatly appreciated.

As part of a project I'm working on, I wrote a quick json parser that loads a json file into a picotron table and thought it might be worth sharing.
E.g.
{
"a":{"b":1,"c":-3.5},
"d":["e",true,false]
} |
is converted to:
{
a={
b=1,
c=-3.5
},
d={"e",true,false}
} |
So if you have some data in json format already, it's easy enough to load in. Hopefully a bit useful for storing configurations or data needed by carts outside of the code itself for neatness.
It should follow json specifications (e.g. whitespace outside of strings doesn't matter), and any null values will be handed as picotron's nil - however, because of the ways nils are added to tables, they won't work/appear in the way it does in the json itself.
Also, if the parser runs into any issues, it tries to offer some helpful errors to identify where the problem is.
I've done some light testing of the code, but let me know if you run into any issues with it.
Code is hidden below:
[hidden]
function init_json_reader()
J_WHITESPACE = {}
J_WHITESPACE[" "]=true
J_WHITESPACE[" "]=true
J_WHITESPACE[chr(10)]=true
J_WHITESPACE[chr(13)]=true
J_START_TABLE = "{"
J_STOP_TABLE="}"
J_START_LIST="["
J_STOP_LIST="]"
J_QUOTE="\""
J_COLON=":"
J_COMMA=","
J_OBJ_STARTS={
n=read_json_null,
t=read_json_true,
f=read_json_false,
}
J_OBJ_STARTS[J_QUOTE]=read_json_key_string
J_OBJ_STARTS[J_START_TABLE]=read_json_table
J_OBJ_STARTS[J_START_LIST]=read_json_list
J_OBJ_STARTS["-"]=read_json_number
for i = 0,9 do
J_OBJ_STARTS[tostr(i)] = read_json_number
end
json_init = true
end
function load_json_file(filepath)
-- Load and read a json file and return a list or table
if not json_init then init_json_reader() end
local text = fetch(filepath)
assert(text!=nil,"Failed to load json file: "..filepath)
return read_json(text)
end
function read_json(string)
if not json_init then init_json_reader() end
-- Read a json string and return a list or table.
if #string == 0 then
return nil
end
local i=skip_json_whitespace(string,1)
if string[i] == J_START_TABLE then
return read_json_table(string,i)
elseif string[i] == J_START_LIST then
return read_json_list(string,i)
else
assert(false,"Unexpected initial character encountered in json file: "..string[i])
end
end
function skip_json_whitespace(string,i)
-- Skip to the first non-whitespace character from position i
while J_WHITESPACE[string[i]] do
i+=1
assert(i<=#string,"Unexpectedly hit end of file while skipping whitespace\nin json file")
end
return i
end
function read_json_table(string,i)
local eot = false
local tab = {}
local k, v = nil, nil
if string[i]==J_START_TABLE then
i+=1
end
while not eot do
k, v, i = read_json_table_entry(string, i)
tab[k] = v
i = skip_json_whitespace(string,i)
if string[i]==J_COMMA then
i+=1
elseif string[i]==J_STOP_TABLE then
i+=1
eot=true
else
assert(
false,
"Unexpected character encounted after reading json entry with\nkey '"..tostr(k).."': "..tostr(string[i]).." "
)
end
end
return tab, i
end
function read_json_table_entry(string, i)
local k, v = nil, nil
i = skip_json_whitespace(string,i)
k, i = read_json_key_string(string,i)
i = skip_json_whitespace(string,i)
assert(
string[i] == J_COLON,
"Expected colon following json key '"..k.."', found: "..string[i]
)
i = skip_json_whitespace(string,i+1)
assert(
J_OBJ_STARTS[string[i]]!=nil,
"Unexpected value encounted while reading json entry\n'"..k.."', found: "..string[i]
)
v,i=J_OBJ_STARTS[string[i]](string,i)
return k, v, i
end
function read_json_key_string(string,i)
assert(
string[i]!=J_STOP_TABLE,
"Table ended while expecting entry, make sure you don't have a misplaced comma."
)
assert(
string[i]==J_QUOTE,
"Expected json key/string to start with double quote,\ninstead found: "..sub(string,i,i+10).."..."
)
i+=1
local s = i
while string[i]!=J_QUOTE do
i+=1
assert(
i<=#string,
"Encountered end of json while reading key/string:\n"..sub(string,i,i+10).."..."
)
end
return sub(string,s,i-1), i+1
end
function read_json_list(string, i)
local eol = false
local lis = {}
local value = nil
if string[i]==J_START_LIST then
i+=1
end
while not eol do
i = skip_json_whitespace(string,i)
assert(
string[i]!=J_STOP_LIST,
"List ended while expecting entry, make sure you don't have a misplaced comma."
)
assert(
J_OBJ_STARTS[string[i]]!=nil,
"Unexpected value encounted while reading json list,\nfound: "..sub(string,i,i+10).."..."
)
value,i=J_OBJ_STARTS[string[i]](string,i)
add(lis,value)
i = skip_json_whitespace(string,i)
if string[i]==J_COMMA then
i+=1
elseif string[i]==J_STOP_LIST then
i+=1
eol=true
else
assert(
false,
"Unexpected character encounted after reading json list entry: "..string[i]
)
end
end
return lis, i
end
function read_json_null(string,i)
assert(sub(string,i,i+3)=="null","Was expecting to read null during json file read, instead\nfound: "..sub(string,i,i+10).."...")
i+=4
return nil, i
end
function read_json_true(string,i)
assert(sub(string,i,i+3)=="true","Was expecting to read true during json file read, instead\nfound: "..sub(string,i,i+10).."...")
i+=4
return true, i
end
function read_json_false(string,i)
assert(sub(string,i,i+4)=="false","Was expecting to read false during json file read, instead\nfound: "..sub(string,i,i+10).."...")
i+=5
return false, i
end
function read_json_number(string,i)
local s = i
while not (
J_WHITESPACE[string[i]] or
string[i]==J_COMMA or
string[i]==J_STOP_TABLE or
string[i]==J_STOP_LIST
) do
i+=1
assert(i<=#string,"Unexpectedly hit the end of json string while reading a number.")
end
return tonum(sub(string,s,i-1)), i
end |

String Theory
A simple productivity app for making evidence boards - cards with information linked by string.
A very normal way of mapping all kinds of information - stories, families, conspiracies, etc.
Includes the ability to save, load and customize boards in a variety of ways.
(Mostly made to help learn Picotron, but hope it might be fun/useful for someone!)

Terra Nova Pinball
Ricochet around an alien world in this Pico-8 pinball table.


Basic Gameplay
Launch the ball onto the table using ⬆️/⬇️ to control the launch power and 🅾️/❎ to release the ball.
Use the flippers (⬅️/➡️ by default, can be configured in the menu) to keep the ball from draining out the bottom of the table, hitting targets and bumpers and completing minigames (see below) to score points.
You have 3 balls to score as many points as you can, and record a new highscore!
If the ball drains within 15s of launching, you get a free relaunch.

Quincunx - a Bean Machine
A Quincunx, otherwise known as a bean machine or Galton board, is a device designed to demonstrate how bell curves appear from combinations of random events, and so why they appear so much in nature.

A physics-based statistics toy
This cart (roughly) simulates marbles bouncing through a grid of pegs and landing in bins at the bottom - for anyone who enjoys watching marbles fall or maybe wants to demonstrate some statistical principles themselves.
The bins have a bell curve overlaid on them that should match the approximate pattern of the balls once they have all fallen, so you can test how consistent the simulation is with the theory.





0 comments