
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.1
Overview
Strawberry Src is intended as an equivalent app to Picotron's in-built code or text editors, with a few quality or life improvements to make navigating and editing code and text easier.
Important notes
Sandboxing
For now, Strawberry Src should be unsandboxed - it's my aim that this won't be the case forever (it shouldn't need to be unsandboxed), but for now there are some serious bugs in Picotron with sandboxed carts permissions. The bugs are documented here: https://www.lexaloffle.com/bbs/?tid=149013
You can unsandbox the cart by either loading it using load -u #strawberry_src-4
, or by right-clicking on a local copy and clicking 'about strawberry_src.p64', then unchecking the box in the bottom-left corner.


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.


