Howdy! I'm new to Pico8 and have really enjoyed getting up to speed at creating carts.
An early project that I'm interested in tackling is a Raspberry Pi installation that scrapes the twitter account @Pico8bot and uses the attached functions to recreate the gifs on that account directly in Pico-8.
Looking at the account's github, it's clear that the bot is written entirely in javascript, only utilizing the RGB values of the pico palette and translating the math functions used back into Lua syntax before posting. The gifs are also created at a higher resolution: 320x320.
I've got the skeleton of a cart that re-creates one of the gifs on that account. I'm wondering if I have faithfully recreated the logic of the javascript in Lua, and if there are any tricks I can use to allow my carts to align closer to the source material.
Here is an example of a Pico8bot tweet, and the cart I made that re-creates it (possible seizure warning):
Here is the code used in the above cart:
--using sample function from https://twitter.com/pico8bot/status/1076636083989798912 function getval(x,y,t) return max(t*t, 6.07+t)*sin(9.14)+sqrt(y)*abs(x+3.05) end --mod result of function down to pico color space function getcol(v) return abs(flr(v)) % 16 end cls() w, h = 128,128 --real bot uses 320x320, which is not possible in Pico t = 0 function _draw() for i = -w/2, w/2,1 do --x coordinate for j = -h/2,h/2,1 do --y coordinate val = getcol(getval(i,j,t)) --js bot draws rectangles instead of setting pixel color values directly rectfill(i+w/2,j+h/2,i+w/2+1,j+h/2+1,val) end end --bot produces gif that loops over t = [0,19] t += 1 if t == 20 then t = 0 end end |
Thanks!


quick update: I've tweaked the lua code a little bit, and also made a python script to scrape the pico8bot twitter for the functions used. I think I'm all ready to go on setting up an always-on raspberry pi to show weird, glitch-y math.
Here is the python script used to generate the lua file and run pico-8 (i placed this in the folder above where pico-8 lives, and this runs on linux). I'll also put a sample of what one of the generated pico scripts looks like below that:
import subprocess as sp import time import requests from bs4 import BeautifulSoup p1 = '''pico-8 cartridge // http://www.pico-8.com version 16 __lua__ ''' p2 = '''function getcol(v) return abs(flr(v)) % 16 end cls() w, h = 128,128 --real bot uses 320x320, which is not possible in Pico t = 0 --time value of functions, [0,19] l = 0 --number of times t is looped, 6 fi = 1 --what function to use to draw the screen flag = 0 --checks for blank frames, moves to next function after 5 consecutive ones function dscreen(f) cs = 0 for i = -w/2, w/2,1 do --x coordinate for j = -h/2,h/2,1 do --y coordinate val = getcol(f(i,j,t)) rectfill(i+w/2,j+h/2,i+w/2+1,j+h/2+1,val) cs += val end end return cs end function _draw() tes = dscreen(fns[fi]) t += 1 if tes == 0 then if flag < 5 then flag += 1 else flag = 0 t = 0 l = 0 fi += 1 cls() end else flag = 0 end if t == 20 then t = 0 l += 1 end if l == 6 then l = 0 fi += 1 flag = 0 cls() end if fi > #fns then fi = 1 end end''' an = './pico-8/pico8' wn = '-windowed 0' rn = '-run' def makefile(l): with open(l,'w') as fobj: fobj.write(sout) def runpico(l): cm = ' '.join([an,wn,rn,l]) sp.Popen(cm,shell=True) def killpico(): pid = int(sp.check_output(["pidof","-s",an])) sp.call(['kill',str(pid)]) def getfun(): r = requests.get('https://twitter.com/pico8bot') soup = BeautifulSoup(r.content,'html.parser') ts = soup.find_all('p',class_="TweetTextSize TweetTextSize--normal js-tweet-text tweet-text") fns = [i.get_text().split(' #DZY')[0] for i in ts] return fns def makefuns(ls): fst = [] for i in range(len(fns)): f1 = 'function ' f2 = 'gv'+str(i) f3 = '(x,y,t) return ' f4 = fns[i] f5 = ' end' fst.append(f1+f2+f3+f4+f5) return '\n'.join(fst) def makelist(ls): fl1 = 'fns = {' fl2 = ','.join(['gv'+str(i) for i in range(len(fns))]) fl3 = '}\n' return fl1+fl2+fl3 while True: fn = 'p8b.p8' fns = getfun() sout = p1+'\n'+makefuns(fns)+ '\n'+makelist(fns)+p2 makefile(fn) runpico(fn) time.sleep(60*10) killpico() |


One more update:
I've now emulated the construction of the math functions, so I no longer need to scrape the twitter account, allowing this to run fully offline. I've also added pico8 to my ~/.bashrc file, so this python script can now be run anywhere. Finally, I've tweaked the lua code somewhat to draw the colors pixel-by-pixel, instead of doing a full-screen refresh. I found that some iterations were too seizure-y to look at. The scanline approach produces a calmer image and gives it a CRT kind of feel.
I'd like to add support for changing to the next function in the queue via keyboard, and a way of displaying the function used on screen. It'd also be nice to be able to flag a function as a 'favorite' to be saved for future use. The inability for pico-8 to write text files complicates this.
Here's what the python script now looks like:
import subprocess as sp import time import re import random import math p1 = '''pico-8 cartridge // http://www.pico-8.com version 16 __lua__ ''' p2 = '''function getcol(v) return abs(flr(v)) % 16 end cls() w, h = 128,128 --real bot uses 320x320, which is not possible in Pico t = 0 --time value of functions, [0,19] l = 0 --number of times t is looped, 6 fi = 1 --what function to use to draw the screen flag = 0 --checks for blank frames, moves to next function after 5 consecutive ones function shuffle(t) for n=1,#t*2 do -- #t*2 times seems enough local a,b=flr(1+rnd(#t)),flr(1+rnd(#t)) t[a],t[b]=t[b],t[a] end return t end function dscreen(f) cs = 0 for j = -h/2, h/2,1 do --y coordinate for i = -w/2,w/2,1 do --x coordinate for k = 1,400 do end val = getcol(f(i,j,t)) rectfill(i+w/2,j+h/2,i+w/2+1,j+h/2+1,val) cs += val end end return cs end while true do tes = dscreen(fns[fi]) t += 1 if tes == 0 then if flag < 5 then flag += 1 else flag = 0 t = 0 l = 0 fi += 1 end else flag = 0 end if t == 20 then t = 0 l += 1 end if l == 2 then l = 0 fi += 1 flag = 0 end if fi > #fns then fi = 1 fns = shuffle(fns) end end''' an = 'pico8' wn = '-windowed' rn = '-run' funs = ['cos(expr)','sin(expr)','expr-expr','expr*expr','expr+expr','expr%expr','abs(expr)', 'sqrt(expr)','flr(expr)','max(expr,expr)','min(expr,expr)','atan2(expr,expr)'] var = ['x','y','t'] def sfun(x): return random.choice(funs) def snum(x): if random.random() > 0.5: return random.choice(var) else: return str(round(10*random.random(),2)) def getfun(): fns = [] for i in range(50): s1 = 'expr*expr*expr' ll = math.floor(random.random()*80+30) iters = 0 while len(s1) < ll and iters < 50: s1 = re.sub('expr',sfun,s1) iters += 1 s1 = re.sub('expr',snum,s1) fns.append(s1) return fns def makefile(l): with open(l,'w') as fobj: fobj.write(sout) def runpico(l,fs=1): cm = ' '.join([an,wn,str(fs),rn,l]) sp.Popen(cm,shell=True) def killpico(): pid = int(sp.check_output(["pidof","-s",an])) sp.call(['kill',str(pid)]) def makefuns(ls): fst = ['function gv'+str(i)+'(x,y,t) return '+fns[i]+' end' for i in range(len(fns))] return '\n'.join(fst) def makelist(ls): return 'fns = {'+','.join(['gv'+str(i) for i in range(len(fns))])+'}\n' while True: fn = 'p8b.p8' fns = getfun() sout = p1+'\n'+makefuns(fns)+ '\n'+makelist(fns)+p2 makefile(fn) runpico(fn) time.sleep(60*30) killpico() |
[Please log in to post a comment]