Log In  


I help lots of people with their code, and I often see questions that boil down to "my code isn't doing what I want, and I don't know what to do. help?" If you feel completely lost when your code doesn't work, this tutorial is for you! Or anyone really -- I personally use PRINTH/PQ constantly and it makes programming about a hundred times easier.

PRINTH

PRINTH ("print to host console") is the single most helpful tool I know for pico-8 debugging. When programming, your head can get out of sync with the computer, and you won't understand what it's doing. The best way I know to bring them back in sync is to slap down some PRINTHs all over the place! Print out a bunch of info to see what your code is actually doing, and figure out exactly where the code goes off-track.

PQ is my custom version of PRINTH, details to follow. (It's very similar but much nicer to use)

setup

Here's a video showing how to launch pico-8 with an attached console on Windows. You'll need to do this somehow, because PRINTH messages are only visible in the attached console.

The first 30 seconds of the video show how to create a shortcut to pico8.exe, and then edit that shortcut's "target" so that it will run from a console. After making it, you can move this shortcut anywhere you want.

The rest of the video walks through a simple debugging scenario, and ends by showing off PQ (see explanation below)

Linux/Mac:


You can make this work on Linux or Mac too of course, but I haven't made a video tutorial. Here's some tips to get you started:

Linux

Run pico8 from a terminal. There are many ways to do this:

  • open a terminal, run pico8. you'll probably need to type in the full path
  • or, right-click pico8 and choose "Run in Terminal" or similar
  • or, edit the application launch settings inside your OS settings. In KDE for example, click on the app launcher and choose "Edit Applications": https://softwarerecs.stackexchange.com/a/80868 (then choose "Run in terminal"). But this might not work if you don't already have a pico8.desktop file, I'm not sure.
  • or, create a pico8.desktop file with Terminal=true -- that's my setup. Here's an example .desktop file:
    [Desktop Entry]
    Name=PICO-8
    Comment=A fantasy console for making, sharing and playing tiny games
    Exec=/home/pancelor/.local/app/pico8/latest/pico8
    Icon=/home/pancelor/.local/app/pico8/latest/lexaloffle-pico8.png
    Terminal=true
    Type=Application
    Categories=Game;Development;

    You'll need to edit the paths and save this file somewhere. I'm using KDE, so I saved mine as ~/.local/share/applications/pico8.desktop

Mac

Run pico8 from a terminal. I'm not sure how to do this in a convenient way. Let me know if you have a good setup! Saving this file as pico8.command (and chmod-ing it to be executable) might work, but I haven't tested it:

#!/bin/bash

/full/path/to/pico8.app

(You'll need to use the program's actual path)


PQ, a better PRINTH

PRINTH itself can be a bit annoying to use, so I built a replacement function called PQ that's nicer to use. Feel free to copy it into any of your projects. If you find my functions helpful and you'd like to credit me, I'd appreciate it!

PRINTH annoyances

  • printh({x=1,y=3}) just prints "[table]". not very helpful!
  • to print multiple things at once, you need to type printh("xy "..x.." "..y), which is cumbersome.
  • just kidding, that will crash if x is nil (which happens frequently while debugging) What you actually have to type is: printh("xy "..tostr(x).." "..tostr(y)).

These annoyances are fixed by PQ: pq("xy",x,y) is much easier to type and will nicely print each argument, even tables.

Being easy to type is very important -- you'll add a lot of these statements temporarily while debugging, to be removed after you figure out the problem. Reducing friction as much as possible lets you fix the problem faster. This is also why it's named "pq": it's easy to type and easy to search for (to remove afterwards)

PQ library code


92 tokens:

-- pq-debugging v11, by  @pancelor
-- https://www.lexaloffle.com/bbs/?tid=42367
printh'---'

-- quote all args and print to host console
-- usage:
--   pq("handles nils", many_vars, {tables=1, work=22, too=333})
function pq(...)
	printh(qq(...))
	return ...
end

-- quote all arguments into a string,
-- usage:
--   x=2 y=3 ?qq("x",x,"y",y)
function qq(...)
	local res=""
	for ix=1,select("#",...) do
		res..=_quote(select(ix,...),4).." "
	end
	return res
end

-- quote a single thing
-- like tostr(), but works for tables too
-- (don't call this directly; call pq or qq instead)
function _quote(tab,maxdepth)
	--avoid inf loop
	if type(tab)~="table" or maxdepth<=0 then
		return tostr(tab)
	end

	local res="{"
	for k,v in next,tab do
		res..=tostr(k).."=".._quote(v,maxdepth-1)..","
	end
	return res.."}"
end

PQ library, minified version


90 tokens:

-- pq by pancelor: lexaloffle.com/bbs/?tid=42367
function pq(...)printh(qq(...))return...end
function qq(...)local r=""for i=1,select("#",...)do r..=_q(select(i,...),4).." "end return r end
function _q(t,d)if(type(t)~="table"or d<=0)return tostr(t)
local r="{"for k,v in next,t do r..=tostr(k).."=".._q(v,d-1)..","end return r.."}"end

why not just PRINT?

Why go to the trouble of setting up a host console and printing there with PRINTH/PQ instead of just printing to the screen with PRINT? A few reasons:

  • If you PRINT during the _UPDATE half of your game, those prints will be overwritten by the CLS at the start of your _DRAW function.
  • Your game has bugs that you're trying to track down, that's why you're debugging. Are you sure that those bugs aren't somehow preventing or overwriting your PRINT statements?
  • You can PRINTH the same thing on multiple frames then scroll through the history in the host console.

There's just too much extra stuff that happens between PRINT and when your eyes see the results. You want to be confident that your debugging system doesn't itself have bugs, so dumping the text as fast as possible (to the host console) is perfect.

alternative: STOP

If you can't set up a host console for some reason, STOP is an ok alternative to PRINTH. Call stop("x="..tostr(x)) or stop(qq("x=",x)), or use this helper function:

function stopq(...)
    stop(qq(...))
    return ...
end

Type r to resume your game after it hits the STOP statement.


example cart

This cart isn't too interesting to play, but I put it here to make it easy to download (load #printh_helpers)

Cart #printh_helpers-2 | 2022-08-07 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
37

Alright that's it, my One Secret Trick for programming/debugging. Let me know if you found this helpful!

37


1

Excellent tutorial! I'm surprised it's so easy to do, I'd expect you'd have to make some PICO-8 game to receive it.


1

This is quite useful thanks!


Can this be done on a mac?


1

yes, sometimes I use a mac and I open Terminal in the folder where pico8 lives and run ./pico8 (I'm unsure what the exact name is). then the Terminal window stays open and the printh output is shown in that terminal.

I'm not happy with that setup, there's probably a more convenient way to do it. but I'm hardly ever on mac -- someone else would be able to make better instructions than me


1

pq() is super useful, was looking for something like this. Thanks!


1

I finally made an account purely to be able to comment that this is a game changer, thank you so much for putting this together. Just making my way through a tutorial and got stuck on a bit - haven't worked out what exactly is wrong yet, but I have so much more visibility about what's happening under the hood!


1

Very useful. Thank you!


This is my poor man's debugger which can be used to check values during runtime on screen. It is useful when the program is not crushing, but does not do what you want to do, and you don't know why.

function debug(infos, x, y)
 x, y = x or 0, y or 0
 local off = 1
 for info in all(infos) do
  for i = -1,1 do
  	for j = -1,1 do
  		print(info[1] .. ":" .. tostr(info[2]), x + i, y + j + off,0)
  	end
  end
  print(info[1] .. ":" .. tostr(info[2]), x, y + off,7)
  off += 8
 end
end

function _init()
	mp = 0
end

function _update()
	mp += 1
end

function _draw()
	cls(1)
	debug({
	{"hp", 19},
	{"mp", mp},
	{"lava"},
	{"lamp", true},
	{"moth","mother"}
	},32,32)
end

2

@pancelor , Just realized I never thanked you for pq() and qq(), when I'm using them in pretty much any pico-8 project I debug.

I'm using different variants of the _quote function depending on the need. (sometimes I need to distinguish between "2" and 2, especially when using arithmetic on strings for example).
Most of the time, I'm using that one :

function _quote(tab,maxdepth)
 --avoid inf loop
 if type(tab)~="table" or maxdepth<=0 then
  return tostr(tab)
 end

 local res="{"
 local res2="{"
 local i=1
 for k,v in next,tab do
  res..=tostr(k).."=".._quote(v,maxdepth-1)..","
  if res2 then
   if k==i then
    res2..=_quote(v,maxdepth-1)..","
    i+=1
   else
    res2=nil   
   end
  end
 end
 return (res2 or res).."}"
end

The idea is to omit the keys for regular arrays, witch is less verbose and easier to read when printing onscreen, but also useful with pq as arrays that should be regular but have some junk values thrown in the mix because of typos or mistakes in variable names suddenly pop up in the console logs due to the format change.
Those misplaced values usually don't disturb the array, but are missing from where they are needed and usually hard to spot.



[Please log in to post a comment]