A Processing Quine

Hi There,

Earlier I made a Quine in Processing, that is code that prints it’s source code without any input.

It works, it prints to the canvas what I can see in the text editor.

I am wondering if anyone can see any further optimizations that I could be making?
My java/processing knowledge isn’t exactly vast so I’m keen for any input!

I’ve based my Processing sketch on this java example: Github Java Quine Example

My Code is as below:

//Quine
//Thomas Day 19/7/21
//Processing 3.5.4

import processing.svg.*;
PFont font;

char R = 82; char o = 111; char m = 109; char a = 97; char n = 110; char S = 83;
char dot = 46; char s = 115; char v = 118; char g = 103;

void setup() {
  size(891, 1260);
  noLoop();
  String RomanS = new StringBuilder().append(R).append(o).append(m).append(a).append(n).append(S).toString();
  font = createFont(RomanS, 12);
}

void draw() {
  String file = new StringBuilder().append(S).append(dot).append(s).append(v).append(g).toString();
  beginRecord(SVG, file);
  char quote = 34;
  String[] string = {
    "//Quine", 
    "//Thomas Day 19/7/21", 
    "//Processing 3.5.4", 
    "", 
    "import processing.svg.*;", 
    "PFont font;", 
    "", 
    "char R = 82; char o = 111; char m = 109; char a = 97; char n = 110; char S = 83;", 
    "char dot = 46; char s = 115; char v = 118; char g = 103;", 
    "", 
    "void setup() {", 
    "  size(594, 840);", 
    "  noLoop();", 
    "String RomanS = new StringBuilder().append(R).append(o).append(m).append(a).append(n).append(S).toString();", 
    "  font = createFont(RomanS, 12);", 
    "}", 
    "", 
    "void draw() {", 
    "  String file = new StringBuilder().append(S).append(dot).append(s).append(v).append(g).toString();", 
    "  beginRecord(SVG, file);", 
    "  char quote = 34;", 
    "  String[] string = {", 
    "  };", 
    "",
    "fill(0);",
    "  for (int i=0; i<22; i++)", 
    "    text(string[i], 50, i*font.getSize() + font.getSize() + i*2);", 
    "  for (int i=0; i<string.length; i++)", 
    "    text(quote + string[i] + quote + ',', 50, 22*font.getSize() + i*font.getSize() + font.getSize() + 22*2 + i*2);", 
    "  for (int i=22; i<string.length; i++)", 
    "    text(string[i], 50, string.length*font.getSize() + i*font.getSize() + font.getSize() + string.length*2 + i*2);", 
    "  endRecord();", 
    "}", 
  };

  fill(0);
  for (int i=0; i<22; i++) 
    text(string[i], 50, i*font.getSize() + font.getSize() + i*2);
  for (int i=0; i<string.length; i++) 
    text(quote + string[i] + quote + ',', 50+font.getSize(), 22*font.getSize() + i*font.getSize() + font.getSize() + 22*2 + i*2);
  for (int i=22; i<string.length; i++) 
    text(string[i], 50, string.length*font.getSize() + i*font.getSize() + font.getSize() + string.length*2 + i*2);
  endRecord();
}
3 Likes

Heh, nice one! :slight_smile:

I was half expecting some kind of self-modifying (or rather self-reading) code somehow. I guess the “without any input” part would make that tricky. I’ve no idea how to do that though, nor how to optimize it.

Wow very nice! :wink:

Next step is to have syntax color highlighting :grin:

Haha! :joy:
Reading the .pde file would definitely be cheating!

Here’s my approuch:

import processing.svg.*;

String[] code;

void setup() {
    size(400, 500);

    String path = sketchPath(getClass().getSimpleName() + ".pde");
    File sketch = new File(path);

    code = loadStrings(sketch);
}

void draw() {
    beginRecord(SVG, "output.svg");
    background(255);
    textAlign(LEFT, TOP);
    fill(0);

    int y = 0;
    for (String line : code) {
        text(line, 0, y);
        y += textAscent()+textDescent();
    }
    endRecord();
}

Have in mind that you need to save the sketch before running it and everytime you make changes to it.

2 Likes

That is a terrifying but brilliant idea.
I’m gonna let you figure that one out though! Too much for me! :rofl:

1 Like

Oh really? sorry i didn’t get that part :sweat_smile:

Yeah…
But your solution does look much neater with a file read!

I wonder how concise the code could get if you if you allowed inputs such as the source file?

We could have a (slightly cheaty) Processing Quine-lympics! :rofl:

Quine? Here’s mine:

That’s right. No code at all. When run, it generates itself. Do I win?

Ahah it doesn’t seem to work, from the Rosetta code quine page :

Empty programs producing no output are not allowed.

Because your program doesn’t even output “nothing”, it just doesn’t output anything :wink:

@TomD you should contribute to it and send your program!

A slight modification of the example by @TomD to simplify things:

  1. drop SVG, just use the screen and screenFrame() for output and use its default filenaming
  2. use the built-in default font
  3. drop all the chars except inline char(34) for "

TL;DR – a bunch of the code translated from the Java Quine example isn’t needed in Processing because it already does all those things for you.

// Quine2 by Jeremy Douglass 2021-09-20
// based on Quine by Thomas Day 19/7/21
// Processing 3.5.4 https://discourse.processing.org/t/a-processing-quine/31332
int off = 12;
int lh = 12;
void setup() {
  size(800, 600);
  noLoop();
}
void draw() {
  background(0);
  String[] code = {
    "// Quine2 by Jeremy Douglass 2021-09-20",
    "// based on Quine by Thomas Day 19/7/21",
    "// Processing 3.5.4 https://discourse.processing.org/t/a-processing-quine/31332",
    "int off = 12;",
    "int lh = 12;",
    "void setup() {",
    "  size(800, 600);",
    "  noLoop();",
    "}",
    "void draw() {",
    "  background(0);",
    "  String[] string = {",
    "  };",
    "  for (int i=0; i<off; i++) text(code[i], lh, lh*(i+1));",
    "  for (int i=0; i<code.length; i++)",
    "    text(char(34) + code[i] + char(34) + ',', lh*3, lh*(i+1+off));",
    "  for (int i=lh; i<code.length; i++) text(code[i], lh, lh*(i+1+code.length));",
    "  saveFrame();",
    "}", 
  };
  for (int i=0; i<off; i++) text(code[i], lh, lh*(i+1));
  for (int i=0; i<code.length; i++)
    text(char(34) + code[i] + char(34) + ',', lh*3, lh*(i+1+off));
  for (int i=off; i<code.length; i++) text(code[i], lh, lh*(i+1+code.length));
  saveFrame();
}
1 Like

Followup: Here is a minified version of the same thing,

screen-0001

It removes comments and whitespace, makes loop indexes concrete, and reuses a global “int i” across all three loops to save a bit more space. It also takes advantage of non-linear writing to the screen – unlike the original example, the second for loop is actually the third visually so the index i can simply continue.

Note that not all of the possible whitespace et cetera has been squeezed out – left in line breaks that make it easy to still see where the Quine trick happens. Still–pretty small.

int h=3,o=12,i=0;
void setup(){size(480,240);noLoop();}void draw(){background(0);
  String[] c = {
    "int h=3,o=12;i=0",
    "void setup(){size(480,240);noLoop();}void draw(){background(0);",
    "  String[] c = {",
    "  };",
    "  for(;i<h;i++)text(c[i],o,o*(i+1));",
    "  for(;i<9;i++)text(c[i],o,o*(i+10));",
    "  for(i=0;i<9;i++)text(char(34)+c[i]+char(34)+',',o*3,o*(i+1+h));",
    "  saveFrame();",
    "}",
  };
  for(;i<h;i++)text(c[i],o,o*(i+1));
  for(;i<9;i++)text(c[i],o,o*(i+10));
  for(i=0;i<9;i++)text(char(34)+c[i]+char(34)+',',o*3,o*(i+1+h));
  saveFrame();
}
2 Likes

One more quine, a short one-liner that just prints itself to the console.

Code:

String p="String p=%c%s%1$c;System.out.printf(p,34,p);";System.out.printf(p,34,p);

Output:

String p=“String p=%c%s%1$c;System.out.printf(p,34,p);”;System.out.printf(p,34,p);

Here printf(p,34,p) says "take the string p, and put 34 and p into its variables. Its variables %c%s%1$c say "a char, a string, and the first char again. ASCII 34 is the " character, so the String is placed inside itself, wrapped in quotes, completing the quine, with the character-based templating of quotes as a way to get around the problem of avoiding character-escaping (as things can’t be escaped in the inner and not in the outer - or vice-versa - without it failing to become a quine).

This example is more compact than the Java quine it is based on because in Processing immediate mode you don’t need a wrapping class or even a main method that takes arguments. Interestingly, Processing doesn’t have a printf method, and its built-in print and println won’t work, so you still need to call System.out.printf to get compact templating. That isn’t very idiomatic to Processing, but it works.

Edit: both examples above added to Rosetta Code : Quine : Processing.

1 Like

Wow! Really happy that you ran with the idea of the quine!

The original purpose of my code was to be plotted to paper, hence the otherwise unnecessary code for SVG export and font changes.

But am impressed by how much you were able to reduce the amount of code!

1 Like

Out of interest, are you using a tool to minify your code?
I’ve been struggling to find one for java that doesn’t introduce bugs.

I did not, I just did it manually. Also, it isn’t fully minified–I left some spaces and line breaks for readability.

The main tricks: remove optional spaces, drop for loop brackets when looping a single instruction, drop the loop variable declaration, use single letter variable names. That’s about it–nothing too fancy. The thing about Java is, it is pretty verbose even when optimally minified…

Thanks! I suspected you’d done it manually.

The one online resource for it that I found has a nasty habit of dropping the + operator :upside_down_face: