Follow
olus2000
Snake AI
by
Meta Tic-Tac-Toe
by
Brainf**k Fantasy Console
by
 3
[ :: Read More :: ]

Cart #spirograph_cg_43-1 | 2023-09-26 | Code ▽ | License: CC4-BY-NC-SA
3

My submission for code guessing round #43. It's a visualisation of a spirograph with adjustable parameters:

• R1: radius of the blue, centered circle
• R2: radius of the white, rolling circle
• D: length of the drawing rod coming from the rolling circle

Edit it online!

### Controls

• Up/Down: select the parameter to adjust
• Left/Right: decrease or increase the parameter by 10 (or 1 if circle is held)
• X/M/Cross: flip sign of R2 (change between hypotrochoid and epitrochoid)
• Z/C/N/Circle: hold to change the parameter step from 10 to 1

Curve drawing stumped me for some time: they have infinite smoothness and calculating every pixel they go through sounds like an absolute pain and efficiency disaster. With parametric curves, like spiros and beziers, you can't even do that well, you can only make your parameter step as small as possible and hope it hits all the pixels. This is not a usable approach.

Then I realised: curves may be smooth, but PICO-8 display is not. I can approximate them with straight lines, which PICO-8 calculates for me! The only thing needed is a bunch of points on the line dense enough to create an illusion of smoothness, which PICO-8 doesn't require a lot of. In addition to that in most parametric curves go "slower" at sharp turns (have low derivative w.r.t. parameter) focusing the detail there, and approximating less curved sections with longer line segments.

You can see the function that calculates the points in tab 1 function `curve()`. It's calculated once every parameter change because it is a fair bit more resource intensive than just drawing it. `i` parameter indicates how far did the rolling circle roll around the centered circle, and based on that calculates angles and positions of the rolling circle and the drawing line, and the tip of the drawing rod is added to the list of points. Exactly the same approach is shown in the `_draw` function but split into steps: for a given `t` (how far the circle rolled) it calculates `cx` and `cy` (position of the rolling circle) and from that `dx` and `dy` (tip of the drawing rod).

### Changelog

Version 1 (current): fixed edge cases of `R1 <= 0` and `R2 == 0`

Version 0: upload of the submission

Cart #spirograph_cg_43-1 | 2023-09-26 | Code ▽ | License: CC4-BY-NC-SA
3

P#134827 2023-09-24 06:57 ( Edited 2023-09-26 08:26)

 3
[ :: Read More :: ]

It's a very minor issue but may be important for some carts. In the web version of PICO-8 using menuitems in the pause menu causes `_update` and `_draw` to be called.

Version: 0.2.4c4 web applet

### How to replicate

Make a cart that has an `_update` function defined that will cause a visible change on screen. Define a menuitem with a callback that returns `true` so that the menu stays open after the menu item is used. Run the cart, pause it and use the menuitem.

### Expected behavior

The callback runs and the state of the program is only changed in the way the callback changed it. (This is waht happens in the full desktop app version 0.2.4)

### Actual behavior

A frame progresses before the callback is resolved. The calling order seems to be: `_update`, `_draw`, callback.

### Examples:

Simple example:

Cart #rosoyugima-0 | 2022-05-11 | Code ▽ | License: CC4-BY-NC-SA
3

Example in a real cart:
This also shows how the frame updates before the callback: changing the SNAKE setting doesn't affect the incorrect frame update and only takes effect the next frame.

Cart #speedsnake_ai-9 | 2022-05-11 | Code ▽ | License: CC4-BY-NC-SA
12

P#111661 2022-05-11 08:38 ( Edited 2022-05-11 08:41)

 12
[ :: Read More :: ]

Cart #speedsnake_ai-9 | 2022-05-11 | Code ▽ | License: CC4-BY-NC-SA
12

This is an implementation of an algorithm that plays snake very well. It's based on a path through all board cells and calculated shortcuts to get more efficiency.

Controls:
X - show/hide the path

ALG - controls which variant of the algorithm is used.
SPEED - Snake's speed, lower = faster.
SNAKE - Change snake visualisation.

The path was generated based on a Hilbert curve. It would work with any path that goes through all board cells, but the Hilbert curve has some nice properties and looks.

I have implemented two variants of this algorithm here, togglable in the menu. "Tail" algorithm tends to follow it's tail very closely when it can't get directly to food. "Blocky" algorithm curls on itself until it has a clear path to food resulting in big patches of snake body later in the game. I believe "Tail" is more efficient than "Blocky".

Algorithm explanation:

### General idea

If there is a path through all cells of the board the snake can just follow it forever. It will reach all the food and grow to the maximum size. It will be quite slow though, having to go through an average of half of the board's area to reach every food. It can be made quicker in the early game by making some shortcuts.

### Path preparation

The path is described as a 16x16 grid of cells. Each cell stores coordinates of the next cell on the path. They are also assigned consecutive numbers from 0 to 255 according to their order on the path. Each cell will also store some metadata during the algorithm execution.

### What shortcuts are allowed?

Obviously only shortcuts that get the snake closer to its goal should be used. In addition, to ensure safety, every shortcut should bring the snake closer to its goal on the path, meaning that every cell of path between the snake and its goal remains empty, and the path between the snake and its goal only decreases. A reasonable approach is to make steps that skip over as big parts of the path as possible, but that isn't efficient on a Hilbert curve path because sometimes following the path can lead to a larger skip than just taking every possible shortcut.

### The goals

Another thing is deciding what the snake's goal is. If the snake always went straight for the food it would quickly run into itself and die. In order to ensure its safety it must never overtake its own tail on the path. This means that if the path is clear between the food and snake's head it can target the food, but if it isn't it has to find some other target while waiting for its tail to get out of the way. The 'Tail' algorithm the snake will target the cell right behind its tail (according to the path). This way it's trying to go as far as possible so it has a good start when the path to the food is finally clear. If the 'Blocky' setting is ON the snake will just follow the path until there's a clear way from it to the food and only use the shortcuts for getting to food.

### The Algorithm

Each frame the best cell to move to is calculated from the snake's head. Cells are scored like this (lower is better):

1. If the cell already has a score metadata stored return that score.
2. If the cell is at the goal, score is 0. Save the score as metadata and return 0.
3. Set best score to 2000 (arbitrary big value more than the cells on the board).
4. For each neighbouring cell do:
a. It it's not between the currently evaluated cell and the goal on the path, ignore it.
b. Otherwise, get it's score using this algorithm.
c. If it's smaller then the current best score update the best score.
5. This cell's score is best score + 1.
6. Save it as metadata of this cell, and return it.

All cells adjacent to the snake's head that are further along the path are evaluated and the smallest one is chosen. Remember, the goal doesn't have to allways be the food, and the path should always be clear between the head and the goal, so choose your goals carefully!

### Complexity and final regards

This algorithm has O(n) time complexity where n is the distance from the head to the goal, which is never bigger than the size of the board, so I would call that a good one. PICO-8 can process it at 60FPS for a 16x16 board. It also has O(n) space complexity where n is the size of the board, which is the same as the game itself.

My explanations tend to be all over the place, so if you are confused about anything feel free to ask!

Inspired by: Code Bullet (YT)

P#111610 2022-05-10 13:32 ( Edited 2022-05-11 08:01)

 4
[ :: Read More :: ]

Cart #julia_sets-1 | 2021-11-06 | Code ▽ | License: CC4-BY-NC-SA
4

### Instructions

Left/Right - change real part of the parameter
Up/Down - change imaginary part of the parameter
X - redraw with new parameter
Z - show/hide the parameter

### Explanation

This is a visualisation of what is (wrongly*) called Julia sets after a French mathematician Gaston Julia who devised the formula governing them. They are drawn by assigning each pixel a complex value based on its coordinates and looking at a certain sequence based on that value. If the sequence shoots off to infinity in either direction the pixel is coloured based on how quickly it shoots off. If it stays bounded it is colored black.

The formula for the sequence is

 ```f(0) = z f(n + 1) = f(n)^2 + c```

Where z is the pixel value and c is the parameter. My implementation looks at the first 20 values of the sequence and if the magnitude of one of them exceeds 2*sqrt(2) the corresponding pixel is coloured based on how fast that happened: sequences that grow slowly will be coloured bright and sequences that grow fast will be coloured dark. If a big enough value is not found in the first 20 values the sequence is considered convergent and the pixel is coloured black.

(*) The term Julia set actually refers just to points on the boundary of the black and coloured regions and are defined for many other functions than the one I'm using there.

P#99606 2021-11-04 21:00 ( Edited 2021-11-06 06:44)

 3
[ :: Read More :: ]

# Dijkstra Map visualizer

Cart #dijkstra_visualizer-0 | 2021-08-07 | Code ▽ | License: CC4-BY-NC-SA
3

While trying to follow a talk on map generation on youtube I created some demos. Here is one that is most functional and most pretty so far.

### Controls:

• Arrows: move the cursor
• Z: Calculate a new map starting from the cursor
• X: Generate new cave

### Explanation:

Dijkstra maps contain information about distance from each point on the graph to a set starting point. In this case the graph is a generated cave-like map and the point is picked at random, but can be changed with arrow keys + Z.

After the Dijkstra map is calculated the cave map is colored like this:

• Walls are black.
• Inaccessible areas are dark blue.
• Accessible area is colored based on the distance, changing color every 20 pixels. It also shifts the colors as time progresses.

P#95752 2021-08-07 16:21

 3
[ :: Read More :: ]

I just found that the Shift + Enter shortcut doesn't work after the 'else' keyword. I know it was supposed to be context aware, I see it properly adds 'until' after 'repeat' which is nice, but this case seems to have been omitted in the update which makes coding slightly annoying.

I'm not sure if this is a bug or a (not so useful) feature, because it's consistent with the changelog which states that as of 0.2.2b:
> shift+enter in code editor only auto-completes block for DO, THEN, REPEAT or FUNCTION
and not ELSE. If this is intended I'll move the post to the workshop section as a feature request.

Version: 0.2.2c

P#90570 2021-04-14 22:50 ( Edited 2021-04-15 00:01)

 2
[ :: Read More :: ]

On my linux machine I use a window manager that swaps between tabs using win + tab number. PICO-8 treats win same as ctrl and if I have PICO on tabs 6 through 9 it makes screenshots, gifs and label images when I switch to (or sometimes from) it. In config there's an option to disable this functionality with function keys F6 through F9, but I would like it to work ONLY on function keys or just not recognise win as ctrl, though I might be the only one. Til then I'll keep PICO-8 on tab 5.

P#90542 2021-04-14 15:20 ( Edited 2021-04-14 15:21)

 1
[ :: Read More :: ]

### Example

Cart #coroutine_bug-6 | 2021-04-07 | Code ▽ | License: CC4-BY-NC-SA
1

#### Controls

Up/Down - change number of pixels drawn per frame by 20

### Steps to reproduce

Write a program that runs nested coroutines when the frame end takes place.

### Resulting and expected behavior

All coroutines except from the top one yield().

It's expected that coroutines resume normal work after frame end.

### Example explanation

It's a program supposed to color the whole screen generating coordinates to color with a recursive coroutine (tab 2, "coroutine itself"). It is wrapped by a function that ensures all errors will be propagated and unwraps the output when no error is encountered (tab 1, "coroutine wrapper").

In pseudo code:

 ```function coroutine(depth) if (depth == 0) yield{0,0} return c = cocreate(coroutine) x,y = wrap(c, depth - 1) -- a wrapper around coresume while x do -- this assumes that if x is nil then the coroutine yield{x,y} -- has reached a return or end. x,y = wrap(c) end c = cocreate(coroutine) x,y = wrap(c, depth - 1) while x do yield shifted {x,y} x,y = wrap(c) end end```

#### Info written on screen:

1. Number of pixels coloured/number of pixels on screen
2. Number of pixels coloured per frame (adjustable with Up/Down)
3. Number of calls to subroutines of certain depth/expected number of calls

### Failed workarounds

Adding a global table to hold recursive coroutines changes nothing.

### Working workarounds

Limit the number of coroutine calls per frame with flip() outside the coroutines.

A wrapper that catches the nil yield and checks if the coroutine is dead. Doesn't work on coroutines that are supposed to yeld() or yield(nil)

WARNING! Skipps some coroutine outputs if flip() is called.

 ```old_coresume = coresume function coresume(c, ...) flag, out = old_coresume(c,...) if not flag or out != nil or costatus(c) == 'dead' then return flag, out -- coroutine raised an error, yielded a non-nil value or finished else return coresume(c) -- resume from the unexpected yield() end end```

The example using an equivalent wrapper:

Cart #coroutine_bug-9 | 2021-04-07 | Code ▽ | License: CC4-BY-NC-SA
1

P#89873 2021-04-02 17:54 ( Edited 2021-04-08 18:41)

[ :: Read More :: ]

I tried making a 60 FPS game and noticed a weird behavior of my PICO-8: it's refresh rate stayed at 29-30 FPS even though the game was supposed to be running on 60 FPS according to stat(7) and CTRL-P widget. Movement looked choppy and the builtin FPS counter (stat(9) and show_fps config) shown 29-30 FPS. My monitor is capable of 60hz refresh rate and my CPU and GPU usage wasn't even remotely close to 100% (or even 8%, which is half of a processor core and can be a limit for single-threaded apps). It works properly on the web version.

Pls halp!

P.S.: It works just fine on Linux on the same machine with the same cart and default config.

### Relevant config:

 ```show_fps 1 foreground_sleep_ms 5 background_sleep_ms 10 host_framerate_control 0```

 ```stat(7) and CTRL-P: 60/60 stat(9) and show_fps: ~30 stat(1) and CTRL-P: ~0.3 CPU usage: ~7% GPU usage (3D): ~30%```

### Soft/hardware:

 ```PICO-8 v.0.2.2 Windows 10 Intel core i7 gen9 Nvidia GeForce GTX (I don't know which exactly but not a bad one) Screen refresh rate 60hz```

#### Cartridge:

Cart #cube_warld-3 | 2021-02-19 | Code ▽ | License: CC4-BY-NC-SA

P#87885 2021-02-19 12:35 ( Edited 2021-02-21 15:44)

 18
[ :: Read More :: ]

Cart #meta_ttt-13 | 2021-02-13 | Code ▽ | License: CC4-BY-NC-SA
18

### Controls

Arrows to move cursor
X to place mark/go level deeper
Hold Z for 1 second to restart game
Press Z to recreate demo in demo modes

• enable demo modes (auto - computer vs computer, fill - random pattern)
• switch between single and multiplayer
• open settings menu (this will restart the game!)

• meta level: ranges from 1 (normal tic-tac-toe) to 4 (81x81, four levels of nested boards)
• AI difficulty: how long in seconds will the AI think about the move. 0 is random moves, 3 never loses normal tic-tac-toe.
• game modes: singleplayer, multiplayer, automatic (AI vs AI) or randomly fill board.

### Rules

Each board contains 9 a smaller boards. The lowest meta boards are just normal tic-tac-toe boards where you can place your signs. Where you move in the lowest board controls on which higher board your oponent can make their next move (indicated by the colored squares). Winning a lower board replaces it with a sign in a higher board. Win the highest board to win the game.

If you didn't understand that's my fault, I suck at explanations. Try it for yourself at small meta levels, it's not as complicated as it seems.

### Changelog

#### Version 1.6:

• 4th meta level
• endgame sonuds
• changed AI
• bugfixes

Previous versions:

#### Version 1.5:

Cart #meta_ttt-11 | 2021-02-10 | Code ▽ | License: CC4-BY-NC-SA
18

• computer vs computer demo
• better AI

#### Version 1.4:

Cart #meta_ttt-10 | 2021-02-08 | Code ▽ | License: CC4-BY-NC-SA
18

• first sound
• single player mode with random AI (avaliable from menu)
• it's harder to accidentally restart game
• various bugfixes

#### Version 1.3

Cart #meta_ttt-7 | 2021-02-05 | Code ▽ | License: CC4-BY-NC-SA
18

• initial release
P#87254 2021-02-05 10:47 ( Edited 2021-02-13 16:46)

[ :: Read More :: ]

## Expected behavior

When running any cart on BBS pressing CTRL-V makes stat(4) read pasted content.

## Actual behavior

If the first cart that is run after loading the page is not the top cart then stat(4) fails to read pasted content regardless of which cart is run, even if the top cart is run later.

Firefox 83.0
Windows 10

## Example

All three example carts are the same cart uploaded three times as three revisions to prevent another BBS quirk, which may or may not be a bug.

Run this first and it works:

Cart #ropejibeja-2 | 2020-12-13 | Code ▽ | License: CC4-BY-NC-SA

Run this first and it breaks:

Cart #ropejibeja-1 | 2020-12-13 | Code ▽ | License: CC4-BY-NC-SA

Run this first and it breaks:

Cart #ropejibeja-0 | 2020-12-13 | Code ▽ | License: CC4-BY-NC-SA

P#85361 2020-12-13 19:59

[ :: Read More :: ]

While trying to make a "class" (metatable + constructor function) for big integers I stumbled upon a weird error. It went off a couple of times before, but I decided to finish debugging the "class" before I report it in case it went away. It's not a memory error or a stack overflow, because they get reported properly. I don't use any actual coroutines, nor you will find "yield" anywhere in my code. The code itself is a bit computation and memory heavy, there's probably a better approach to this, but none of it justifies such an error. Another thing I noticed is that it breaks on seemingly random lines, and almost any change to the code makes it change the line it reports as the source of the error. It even breaks in a different spot on the web than on my computer! (look at the line shown on the thumbnail vs what actually happens when you press and hold X)

The showcase is a simple program showing more and more powers of 15 as you press X. An interesting fact is that it breaks on different values depending on wether you press-and-hold X or repeatedly press X. It's all very confusing.

Cart #berozopugi-0 | 2020-12-13 | Code ▽ | License: CC4-BY-NC-SA

P#85345 2020-12-13 12:17 ( Edited 2020-12-13 12:19)

 1
[ :: Read More :: ]

# Advent (of code) is here!

On 1st December every year since 2015 starts the best advent calendar: Advent of Code! It's a series of 50 small programming challenges that get progressively harder every day and can be solved in any programming language!

Quote from the creator, Eric Wastl:

Advent of Code is an Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like. People use them as a speed contest, interview prep, company training, university coursework, practice problems, or to challenge each other.

I can't find any mentions about AoC on BBS which is a true shame, because PICO-8 is perfect for solving and visualising AoC puzzles! (At least the first ones.) I wanted to share this awesome contest with you and in this post I will be including carts with my solutions for as long as I can keep up. GL;HF!

If you want to compete here's my private leaderboard code: `979701-cf5ded3a`

### My solutions:

Days 1-6 (need to paste inputs):

Cart #aoc_2020_olus2000-5 | 2020-12-13 | Code ▽ | License: CC4-BY-NC-SA
1

Left/Right to cycle between days.
Up/Down to toggle between part 1 and part 2.
Paste your input and press O to run, then press O again to go back to day selection.
!!!WARNING!!!
Make sure you have pasted the input from the correct day, or the cartridge will break.

Days 1-3 (with my inputs):

Cart #aoc_2020_olus2000-4 | 2020-12-03 | Code ▽ | License: CC4-BY-NC-SA
1

Left/Right to cycle between days.
Up/Down to toggle between part 1 and part 2.
Future solutions will be delayed in order not to spoil them.

P#84854 2020-11-30 10:09 ( Edited 2020-12-13 19:48)

 5
[ :: Read More :: ]

Cart #k8_caves_v1_1-0 | 2020-11-02 | Code ▽ | License: CC4-BY-NC-SA
5

I found a cool cave generation algorithm and I'm building a game around it. Currently you can only explore a single random level with 100 bats that fly randomly.

Features:

• random 128x64 map that wraps around
• rendering y-sorted mobs
• Kate, who can run, dash and stand still

Todo:

• damage system
• attack for Kate
• better sprites for the level (pls help, I suck at esthetics)
• progression
• more enemies
• enemy AI
• collision detection between mobs
• more sounds
• and a LOT more

Any feedback or suggestion are appreciated.

Versioin history:
1.1 (latest)

• dash
• stamina
• less noisy walls

1.0
Cart #k8_caves_v1_0-0 | 2020-10-29 | Code ▽ | License: CC4-BY-NC-SA
5

• First release
P#83447 2020-10-29 09:11 ( Edited 2020-11-02 21:38)

 5
[ :: Read More :: ]

ladies and gentelmen, behold the thing no one asked for, the pinnacle of console and cart design alike, my opus magnum:

# Brainf**k Fantasy Console!

Cart #brainf__k_console_1_1-1 | 2020-11-30 | Code ▽ | License: CC4-BY-NC-SA
5

Have you ever got lost in the many interface options your console provides? Have you ever felt overrun with the abundance of commands included in normal programming languages that everybody knows? Do you feel flabbergasted looking at the vibrant interface colors of the mainstream fantasy consoles? My newest addition to the market has you covered! It has:

• A full-featured memory editor optimised for the purpose of programming the BFC!
• A brand new programming language based on the popular Brainf**k!
• A whopping 4096 bytes of program+data memory!
• In-depth docs written by someone who almost knows English grammar!
• And not a lot more!

### Specs

Display: 128x128px, fixed 16 color palette
Input: 6-button controllers
Cartridge size: 32k
Sound: Oops, i totally forgot about that. There's no way sound makes sense in this console so mabe play something from youtube in the background or what?
Code: Brainf**k Assembly Language (yes, I made it up)

### Brainf**k Assembly Language

BAL is based on a popuar esoteric language brainf**k. For more info check out it's esolangs wiki page, you can find full specification there.

### Interface help

In most cases d-pad is used for movement, Z for going back and X for accepting the choice. Only notable differences are in the memory editor itself, where you kan edit cells by holding X and using the d-pad. Pressing Z while holding X will trigger the copying sequence which lets you mark two ends of selection and the beginning of where to paste it. Do this three times on the same cell to paste contents of your clipboard. If you forgot to include an end code for your program you can always press Z+X to force exit. For more info see included docs, it can't be all easy, it's brainf**k after all.

### Feedback

I don't think I'll be updating this anytime soon, but I would appreciate any posotive feedback and take note of suggestions. I know BFC is not perfect, especially esthetically. If you notice any bugs please comment what you were doing and how you found them! I'm absolutely sure that BFC is completely bug-free, so if some bugs are still there I want to hint them down ASAP!

### Notes

Note 1: program counter is incremented after every command so [ and ] actually jump count * 2 + 1 and count * 2 - 1 respectively

Note 2: since code and data share memory you can modify the code during runtime, be careful not to do that accidentally though.

Note 3: your program memory is actually saved to the cartridge rom, so if you download the console you can save multiple copies of it with different programs. Neat, innit? Unfortunately 256 bytes of standard PICO-8 local storage is too little, so sucks to be you, web users.

Note 4: I use shared map/gfx memory to store the BAL memory, so don't mess with the lower half of the map and sprites unless you know what you're doing.

### Version history

1.2 (latest):

• clicking X+Z three times on the same cell will paste contents of your clipboard, for easy AdventOfCode input pasting
• fixed a pixel in the word "CONSOLE" in the demo

1.1:

Cart #brainf__k_console_1_1-0 | 2020-10-29 | Code ▽ | License: CC4-BY-NC-SA
5

• rng accesible by ,31
• updated demo with procedural dithering

1.0:

Cart #brainf__k_console_1_0-0 | 2020-10-26 | Code ▽ | License: CC4-BY-NC-SA
5

• First official release
P#83373 2020-10-26 18:47 ( Edited 2021-04-20 09:24)

 6
[ :: Read More :: ]

Cart #sepbowka-0 | 2020-10-19 | Code ▽ | License: CC4-BY-NC-SA
6

A small showcase of how to generate random 2D caves using a cellular automaton. Inside the code there are some parameters worth tweaking, maybe I'll add an interface for this in a later update.

The automaton is really simple: it changes based on the majority of it's neighbours and remains unchanged when tied.

Parameters:

• width and height - self explanatory, cave will be displayed in the upper-left corner
• live and text color - coloring of the cave and the "reset" message
• iterations - more iterations of the automaton mean less small noise and more smooth walls. Over 20 will not change much.
• density - chance of a cell being dead at the start. Very susceptible to change, more will create wider corridors.

More on density:

• less than .30 - will create an almost full array of live cells, not very cave-ish
• .30 - .45 - many small caves, walls are a majority
• .45 - .50 - a couple separate caves with many small holes around
• .50 - .53 - a quickly increasing chance of forming one connected cave consisting of long thin corridors
• .53 - .60 - tables have turned, now the walls are just blobs in the empty space around them and they're getting more and more sparse
• .60 - .70 - an empty room dotted with rare live cells
• .70 and up - hardly any cells survive first few iterations, only live cells end up at the walls since out of bounds is always considered live
P#83128 2020-10-19 20:29 ( Edited 2020-10-19 20:32)