18
\$\begingroup\$

So, you were sitting at your desk, golfing a program to calculate the first 20 digits of pi, and along comes your boss and throws your apple IIe out the window. You are now working on a new project and this computer does not yet have any text capability. None. No fonts. Nothing.

Now let's finish that program. Calculate and Display the first 20 characters of pi without using any fonts that are not part of your program. Your output can be displayed or written to standard out as an image file (jpeg, png, gif, svg(as long as you don't use any characters), bmp, xpm). You can use any language, but you can not use your language's font functions, text display or similar.

small bonus (10 characters) If it will work on a Lisa.

Edit: for those who didn't figure it out my inspiration was the first mac, and the title is a pun. A big kudos to @Sukminder whose animated gif is just cool. The contest is not over if a better answer comes along.

\$\endgroup\$
18
  • \$\begingroup\$ I like the challenge, but technically wouldn't such a system also be incapable of displaying source code? Except for Piet, of course. \$\endgroup\$ Commented Jan 18, 2014 at 21:40
  • 2
    \$\begingroup\$ @ValekHalfHeart you can load the source code from a different machine \$\endgroup\$ Commented Jan 18, 2014 at 21:41
  • 1
    \$\begingroup\$ And how do you define human readable? For example, my handwriting is readable to some humans (at least one) and not to others. (By the way, 2^(2x2)=16, enough glyphs for all 11 digits. ;) ) \$\endgroup\$ Commented Jan 18, 2014 at 22:19
  • 4
    \$\begingroup\$ I don't understand the title at all, I don't understand how ASCII art can be ok when we cannot use text display, and the question very much needs a definition of "calculate PI". \$\endgroup\$ Commented Jan 19, 2014 at 9:07
  • 2
    \$\begingroup\$ What does "calculate pi" really mean? Could I hard-code a bitmap of the first 20 decimals? (Doesn't make use of any built-in PI constant or similar) \$\endgroup\$
    – FireFly
    Commented Jan 19, 2014 at 18:40

10 Answers 10

20
\$\begingroup\$

Python, 217 bytes

Requires the Python Imaging Library

import Image
x=p=141
i=Image.new('1',(x,11))
while~-p:x=p/2*x/p+2*10**19;p-=2
for c in str(x):[i.putpixel((j%7/5*4-~j%7/4*~j/7+p,j%7*3%14%8+j%14/10+2),1&ord('}`7gjO_a\177o'[int(c)])>>j%7)for j in range(17)];p+=7
i.show()

The byte count assumes that the escaped character \177 is replaced with its literal equivalent (char 127).

Output will appear as the following (it will open in your default *.bmp viewer):

Note that this could easily be parameterized to print any number of digits you like. The following will accept an integer input from stdin, and display that many digits:

import Image
n=input()
x=p=n*7|1
i=Image.new('1',(x,11))
while~-p:x=p/2*x/p+2*10**(n-1);p-=2
for c in str(x):[i.putpixel((j%7/5*4-~j%7/4*~j/7+p,j%7*3%14%8+j%14/10+2),1&ord('}`7gjO_a\177o'[int(c)])>>j%7)for j in range(17)];p+=7
i.show()

Output for n=80:


Pi Calculation

while~-p:x=p/2*x/p+2*10**19;p-=2

Yup, that's it. The formula used is the result of applying Euler's Transform to the Leibniz Series, and then factoring out each term from the remainder of the sum. The formula converges linearly; each digit requires log2(10) ≈ 3.32 iterations. For those interested in the derivation, see Appendix A.

Display

PIL is used for image generation, because it's the most convenient library that I know of. A blank 141×11 black and white bitmap is created, and then white lines are drawn on it in a seven-segment fashion, one pixel at a time. The positions required to draw each segment are stored in a bitmask string, with bits corresponding to the following positions:

 000
3   5
3   5
 111
4   6
4   6
 222

The bit of magic (j%7/5*4-~j%7/4*~j/7+p,j%7*3%14%8+j%14/10+2) produces the each pixel in the following order (base-18):

(2, 2), (2, 5), (2, 8), (1, 3), (1, 6), (5, 3), (5, 6),
(3, 2), (3, 5), (3, 8), (1, 4), (1, 7), (5, 4), (5, 7),
(4, 2), (4, 5), (4, 8)

 07e
3   5
a   c
 18f
4   6
b   d
 29g

Appendix A

Euler's Transform is a convergence acceleration technique that works for any series which displays absolute monotonic convergence. The resulting series will converge linearly, typically at the rate of one bit per term (note that if the original series was already super-linear, the resulting series will actually converge slower). The purely mathematical description is a bit hard to follow, so I'll be taking a procedural approach.

We'll start with the Leibniz Series:

Then split each term in half, combining neighboring terms:

Simplified:

Generalized:

Notice that the leading ½ didn't have a partner term, and thus was excluded from the rest of the sum. This is the first term of the transformed series. To find the next term, we repeat the process again:

And again:

And again:

And once more for good measure:

At this point we have the first five terms, and the sixth term is evident. This should be enough to generalize, so we'll stop here. We'll start by factoring the numerators and denominators:

The denominators evidently contain a Double Factorial of 2n+1, so we'll patch that in:

Everything fits, except for the first two terms which have an unaccounted for 2 in the denominator. We can fix that by multiplying the entire expression by 2:

23 = 2·4, so:

The numerator can now be easily identified as n!.

Notice that the factor added to each successive term, n/(2n+1), approaches ½ as n becomes large, implying a linear convergence at the rate of one bit per term - this is in fact by design. A nice result, but it'd be even nicer without the factorials in there. What we can do here is to factor out each successive term from the rest of the sum, which will generate a nested expression:



This can be rewritten as a recurrence relation:

Where n counts backwards from ⌈log2(10)·d⌉ .. 0, where d is the number of digits required.

It might be interesting to note that the stable point of this recurrence is exactly 2 (or 4 if you've doubled it, as I have in the implmentation above), so you can save a number of iterations by initializing properly. Though, initializing to a random value you need elsewhere, and throwing a few extra iterations on the top is generally cheaper byte-wise.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Very nice, thanks for the lesson! What I don't get is what the p in p/2 * x/p + ... is doing.. AIUI Python supports automatic promotion to a biginteger-ish datatype, so it shouldn't be a precision thing, but somehow those ps matter and don't cancel out like I imagine them to... what am I missing here? \$\endgroup\$
    – FireFly
    Commented Jan 21, 2014 at 16:54
  • 1
    \$\begingroup\$ @FireFly p initialized odd, so that p/2/p is equivalent - under integer division - to ((p-1)/2)/p. This produces the 1/3, 2/5, 3/7, etc. terms derived above. \$\endgroup\$
    – primo
    Commented Jan 21, 2014 at 17:03
12
+50
\$\begingroup\$

#C – 777 Characters

C – 731 Characters

Prints GIF to stdout.

  • Quirk: No comma after first 3.

Stitching together GIF from pre-configured header + each digit represented by home brewed (embedded) font of 5x5 pixels.

Result

enter image description here

It is there ---^

Note that GIF vanishes, sometimes, in Chrome after one run.

#include <stdio.h>
#define G 68,30
#define F(e,i)for(i=0;i<e;++i)
#define B w[++k]
unsigned char r[][10]={{4,18,150,199,188,159,10,0},{4,18,102,169,188,122,64,1},{G,160,166,104,217,80,1},{G,160,166,184,140,66,1},{68,96,153,193,135,138,66,1},{G,6,107,199,155,80,40},{68,128,150,22,173,218,90,1},{G,160,182,169,254,84,1},{G,6,138,153,140,10,0},{G,6,138,185,250,66,1},{0,0,0,5,0,5,0,0,2,8}},w[440]={71,73,70,56,57,97,100,0,5,0,144,0,0,255,255,255,0,0,0};int main(){int a=10000,b=0,c=70,d,e=0,f[71],g;int i,j,k=18,s=0;char m[5];for(;b<c;)f[b++]=a/5;for(;d=0,g=c*2;c-=14,e=d%a){for(b=c;d+=f[b]*a,f[b]=d%--g,d/=g--,--b;d*=b);sprintf(m,"%d",e+d/a);F(4,i){B=44;B=s++*5;F(10,j)B=r[10][j];F(8,j)B=r[m[i]-'0'][j];B=0;}}B=59;fwrite(w,1,k,stdout);}

Short introduction:

Calculation of PI

Pi is calculated using a slightly modified version of Dik Winter and Achim Flammenkamp's implementation of Rabinowitz and Wagon's algorithm for computing digits of π.

int a=10000,b,c=2800,d,e,f[2801],g;main(){for(;b-c;)f[b++]=a/5;for(;d=0,g=c*2;c
-=14,printf("%.4d",e+d/a),e=d%a)for(b=c;d+=f[b]*a,f[b]=d%--g,d/=g--,--b;d*=b);}

GIF generation

GIF images has a canvas property in header. We can use this in combination with displaying multiple images by setting left property for each digit accordingly – where each digit is a (embedded) image in itself.

Documentation.

Example:

Header: Canvas Width  100 pixels
        Canvas Height   5 pixels

3 : left  0 pixels
1 : left  5 pixels
4 : left 10 pixels
… and so on.

Expanded code (with loads of comments)

Messy, but that is part of the minimization:

#include <stdio.h>
#define G 68,30
#define F(e,i)for(i=0;i<e;++i)
#define B w[++k]

/* Font + Image Descriptor + Start of Image Data. 
 *
 * Font glyphs are black and white pixels making a 5x5 picture.
 * Each glyph has its own entry in array.
 * Pixels (White,White,Black,Black ...) are further compressed using LZW
 * compression.
 *
 * Next entry in array is Image Descriptor which is added before each glyph.
 * Last entry is start of Image Data.
 *
 * - "0" and comma are 7 and 5 bytes, but hacked to fill 8 bytes to make it 
 * easier to handle in minified code.
 * */
unsigned char r[][10]={
        /* Images representing glyphs. */
        { 4,   18, 150, 199, 188, 159, 10,  0}, /* 0 */
        { 4,   18, 102, 169, 188, 122, 64,  1}, /* 1 */
        { 68,  30, 160, 166, 104, 217, 80,  1}, /* 2 */
        { 68,  30, 160, 166, 184, 140, 66,  1}, /* 3 */
        { 68,  96, 153, 193, 135, 138, 66,  1}, /* 4 */
        { 68,  30,   6, 107, 199, 155, 80, 40}, /* 5 */
        { 68, 128, 150,  22, 173, 218, 90,  1}, /* 6 */
        { 68,  30, 160, 182, 169, 254, 84,  1}, /* 7 */
        { 68,  30,   6, 138, 153, 140, 10,  0}, /* 8 */
        { 68,  30,   6, 138, 185, 250, 66,  1}, /* 9 */
        {132, 143, 121, 177,  92,   0,  0,  0}, /* , (removed as not used) */
        {
        /* Image Descriptor */
        /* 0x2C    Image separator (Embedded in code)   */
           0,   /* Image Left   (LSB embedded in code.  */
        0, 0,   /* Image top    (16-bit Little endian)  */
        5, 0,   /* Image Width  (16-bit Little endian)  */
        5, 0,   /* Image Height (16-bit Little endian)  */
        0,      /* Packed byte  (Local color table (not used, etc.)) */
        /* Start of Image Data */
        2,      /* Starting size of LZW 2 + 1 = 3 */
        8       /* Number of bytes in data */
        }
};
/* GIF Header + Global Color table. 
 *
 * GIF's has a standard header.
 * Canvas size is the are on which to paint.
 * Usually this is size of whole image, but in this code I've spanned it out
 * and paint glyphs by rendering pictures on a canvas of size:
 * 20 * width_of_1_image (5 * 20 = 100)
 *
 * Each image can have an optional color table, but if not present the global
 * color table is used. In this code only global color table is used. It
 * consist of only black and white. (Though very easy to change if wanted.)
 * */
unsigned char buf[440] = {
        71, 73, 70,     /* Signature     "GIF" */
        56, 57, 97,     /* Version       "89a" */
        100, 0,         /* Canvas width  (16-bit Little endian) 5 * 20 = 100*/
          5, 0,         /* Canvas height (16-bit Little endian) 5 pixels.   */
        144,            /* Packed byte: 1 001 0 000
                                  1 : Has global color table.
                                001 : Color resolution.
                                  0 : Sorted Color Table (No)
                                000 : Size of Global Color table (2^(value+1))
                                        or 2 << value ...
                        */
        0,              /* Background Color index. */
        0,              /* Pixel aspect ratio. */
        /* Global color table. */
        255, 255, 255,  /* Index 0: White */
          0,   0,   0   /* Index 1: Black */
};

int main(void){
        /* PI generation variables. */
        int a = 10000, 
            b = 0,
            c = 70,
            d,
            e = 0,
            f[71],
            g;
        /* General purpose variables */
        int i,
            j,
            k = 18,     /* Current Index in out buffer. */
            s = 0;      /* Image counter:
                           (Tells us what "left/x" value should be). */
        char m[5];      /* Print next 4 digits of PI to this buffer. */
        /* Prepare / pre-fill for PI math. */
        for(;b < c;)
                f[b++] = a/5;
        /* Calculate 4 and 4 digits of PI and push onto out buffer. */
        for(; d = 0, g = c * 2; c -= 14 , e = d % a) { 
                for (b = c; d += f[b] * a, f[b] = d % --g, d /= g--, --b; d *= b);
                /* sprintf next 4 digits to temprary buffer.     */
                sprintf(m, "%d", e + d/a);
                /* We are served 4 and 4 digits. 
                 * Here we transalte them to glyphs and push onto out buffer*/
                for (i = 0; i < 4; ++i) {  
                        buf[++k] = 0x2C;     /* 0x2C : Image separator.        */
                        buf[++k] = s++ * 5;  /* xx   : Image left (x) on canvas.*/
                        for (j = 0; j < 10; ++j) {
                                /* Push "Start of Image Data" onto buffer      */
                                buf[++k] = r[11][j];
                        }
                        for (j = 0; j < 8; ++j) {
                                /* Push data of glyph (LZW-compressed) onto buffer. */
                                buf[++k] = r[m[i]-'0'][j];
                        }
                        /* Start of image data informs how big the image data 
                         * is. End with zero to mark that this is EOI. */
                        buf[++k] = 0;       
                }
        }
        /* 0x3b is Trailer, marking end of file. */
        buf[k] = 0x3b;
        /* Write buffer to standard output. 
         * 'k' holds length of data, though as we know this image is 
         * 100x5 etc. we can pre-define it as well.
         * */
        fwrite(buf, 1, k, stdout);
}

Looking to use a shorter / other algorithm for calculating π.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ I don't see the dot after the first 3 in the image. \$\endgroup\$ Commented Jan 19, 2014 at 23:11
  • 1
    \$\begingroup\$ Do you have a link to info about the pi decimals generation algorithm? I played around with your code a bit (after stripping the GIF stuff out), but I don't really see why it results in digits of pi... \$\endgroup\$
    – FireFly
    Commented Jan 20, 2014 at 9:38
7
\$\begingroup\$

JavaScript, 680 chars

<html><body></body><script>v=["","41L70L7e","24C223060Ca0b587C592b2eLae","30L90L55L65C95a7a9Cac9e6eC5e3e2c","aaL2aL80L8e","90L40L36C455565C95a7a9Cac9e6eC5e3e2c","70C52272aC2c3e6eC9eacaaCa89666C36282a","20La0C745a5e","60C202435C465666C96a8aaCac9e6eC3e2c2aC283666C768695Ca4a060","a4Ca69868C382624C223060C90a2a4Ca77c5e","6dC7d7e6eC5e5d6d"];v["."]=v[10];a=(""+(4*Math.atan(1))).split("");s="";for(i in a)s+="<path d='M "+v[a[i]].split("").map(function(c){return+-c||c>"Z"?parseInt(c,16):c;}).join(" ")+"'transform='translate("+i*33+".5,10.5)scale(3,3)'fill='none'stroke='#333'stroke-linecap='round'stroke-linejoin='round'/>";document.body.innerHTML="<svg>"+s+"</svg>";</script></html>

This can be viewed in a web browser; the numbers are output as SVG paths.

Screenshot of SVG output in a web browser

  • It doesn't compute pi in an interesting way, and JS lacks a number type with precision to display 20 digits.

  • To save characters, I omitted the path data for "0", since it doesn't show up in the sequence.

\$\endgroup\$
1
  • \$\begingroup\$ Ooh, a vector-based approach. Very nice, good job on the font too. \$\endgroup\$
    – FireFly
    Commented Jan 24, 2014 at 8:14
6
\$\begingroup\$

Python, 222 chars

n=[10**20*277991633/1963319607/10**i%10 for i in range(19,1,-1)]
print' *     *'
print' * **    '+' '.join(' ** * ***** *****  *'[2*d:2*d+2]for d in n)
print'**     * '+' '.join('**  *    * ** ***** '[2*d:2*d+2]for d in n)

The first line calculates the digits of pi using the approximation pi-3 ~= 277991633/1963319607. The next three lines output 20 characters of pi using ASCII art Nemeth Braille.

 *     *
 * **    *  ** *  *   * *  ** *  ** *  *   * **  * ** *  ** * 
**     *     *     * *  *  *   *     * ** *  ** *     *     **

I'm pushing the boundaries in two directions here, both in the "calculating Pi" and "human readable" senses.

\$\endgroup\$
3
  • 3
    \$\begingroup\$ What the heck? I thought we weren't supposed to use any text output. How's your computer going to render the *'s and spaces without a font? \$\endgroup\$
    – boothby
    Commented Jan 21, 2014 at 0:32
  • \$\begingroup\$ @boothby: It's ASCII art. Think of * as a 1x1 black pixel and ` ` as a 1x1 white pixel. \$\endgroup\$ Commented Jan 21, 2014 at 0:43
  • 3
    \$\begingroup\$ He's got a point. You can not render a * without using fonts, I think you're disqualified \$\endgroup\$
    – Allison
    Commented Feb 18, 2014 at 13:24
5
\$\begingroup\$

Java - 866 860 857 853 chars, plus a cheating version with 574 chars

Using the Simon Plouffe formula from 1996, outputs a x.png file with white digital-clock-like numbers in a black background:

pi

This is the compressed code:

import java.math.BigDecimal;class E{static java.awt.Graphics g;public static void main(String[]h)throws Exception{java.awt.image.BufferedImage i=new java.awt.image.BufferedImage(213,17,1);g=i.getGraphics();BigDecimal y=v(-3);for(int n=1;n<99;n++)y=y.add(v(n).multiply(v(2).pow(n)).multiply(f(n).pow(2)).divide(f(2*n),42,0));int j=2;for(char c:y.toPlainString().substring(0,21).toCharArray()){if(j!=12){c-=48;boolean b=c!=1&c!=4;t(b,j,2,8,3);t(c<1|c>3&c!=7,j,2,3,8);t(c<5|c>6,j+5,2,3,8);t(c>1&c!=7,j,7,8,3);t(c%2==0&b,j,7,3,8);t(c!=2,j+5,7,3,8);t(b&c!=7,j,12,8,3);}j+=10;}t(true,17,12,3,3);javax.imageio.ImageIO.write(i,"png",new java.io.File("x.png"));}static BigDecimal v(int k){return BigDecimal.valueOf(k);}static BigDecimal f(int k){return k<2?v(1):f(k-1).multiply(v(k));}static void t(boolean x,int a,int b,int c,int d){if(x)g.fillRect(a,b,c,d);}}

That, with identing and some whitespaces would be this:

import java.math.BigDecimal;

class E {

    static java.awt.Graphics g;

    public static void main(String[] h) throws Exception {
        java.awt.image.BufferedImage i = new java.awt.image.BufferedImage(213, 17, 1);
        g = i.getGraphics();
        BigDecimal y = v(-3);

        // Calculate PI using the Simon Plouffe formula, 1996.
        for (int n = 1; n < 99; n++)
            y = y.add(v(n).multiply(v(2).pow(n)).multiply(f(n).pow(2)).divide(f(2 * n), 42, 0));

        int j = 2;
        for (char c : y.toPlainString().substring(0, 21).toCharArray()) {
            if (j != 12) {
                c -= 48;
                boolean b = c != 1 & c != 4;
                t(b, j, 2, 8, 3);
                t(c < 1 | c > 3 & c != 7, j, 2, 3, 8);
                t(c < 5 | c > 6, j + 5, 2, 3, 8);
                t(c > 1 & c != 7, j, 7, 8, 3);
                t(c % 2 == 0 & b, j, 7, 3, 8);
                t(c != 2, j + 5, 7, 3, 8);
                t(b & c != 7, j, 12, 8, 3);
            }
            j += 10;
        }
        t(true, 17, 12, 3, 3);
        javax.imageio.ImageIO.write(i, "png", new java.io.File("x.png"));
    }

    static BigDecimal v(int k) {
        return BigDecimal.valueOf(k);
    }

    static BigDecimal f(int k) {
        return k < 2 ? v(1) : f(k - 1).multiply(v(k));
    }

    static void t(boolean x, int a, int b, int c, int d) {
        if (x) g.fillRect(a, b, c, d);
    }
}

Cheating the rules and considering that the calculation of PI can be done as "the numeric representation of the String 3.1415926535897934384", this can be reduced to 574 chars:

class F{static java.awt.Graphics g;public static void main(String[]h)throws Exception{java.awt.image.BufferedImage i=new java.awt.image.BufferedImage(213,17,1);g=i.getGraphics();int j=2;for(char c:"3.1415926535897932384".toCharArray()){if(j!=12){c-=48;boolean b=c!=1&c!=4;t(b,j,2,8,3);t(c<1|c>3&c!=7,j,2,3,8);t(c<5|c>6,j+5,2,3,8);t(c>1&c!=7,j,7,8,3);t(c%2==0&b,j,7,3,8);t(c!=2,j+5,7,3,8);t(b&c!=7,j,12,8,3);}j+=10;}t(true,17,12,3,3);javax.imageio.ImageIO.write(i,"png",new java.io.File("x.png"));}static void t(boolean x,int a,int b,int c,int d){if(x)g.fillRect(a,b,c,d);}}
\$\endgroup\$
4
\$\begingroup\$

Java - 642 622 characters

Copying from my previous answer, using the Simon Plouffe formula from 1996. But outputs ASCII-art instead:

import java.math.BigDecimal;class H{public static void main(String[]h)throws Exception{int[]t={31599,4681,31183,29647,5101,29671,31719,4687,31727,29679,8192};BigDecimal y=v(-3);for(int n=1;n<99;n++)y=y.add(v(n).multiply(v(2).pow(n)).multiply(f(n).pow(2)).divide(f(2*n),42,0));for(int z=0;z<5;z++){for(char c:y.toPlainString().substring(0,21).toCharArray()){if(c<48)c=58;int a=(t[c-48]>>>z*3)&7;e(a/4);e(a/2&1);e(a&1);e(0);e(0);}e(10);}}static void e(int c){System.out.print((char)(c<2?c*3+32:c));}static BigDecimal v(int k){return BigDecimal.valueOf(k);}static BigDecimal f(int k){return k<2?v(1):f(k-1).multiply(v(k));}}

All of that, with some identing and spaces, and a bit of help to the reader understand the meaning of the magic numbers:

import java.math.BigDecimal;

class H {

    public static void main(String[] h) throws Exception {
        // Each block corresponds to a line. Each char has 5 lines with a 3-char width.
        int[] t = {
            0b111_101_101_101_111,
            0b001_001_001_001_001,
            0b111_100_111_001_111,
            0b111_001_111_001_111,
            0b001_001_111_101_101,
            0b111_001_111_100_111,
            0b111_101_111_100_111,
            0b001_001_001_001_111,
            0b111_101_111_101_111,
            0b111_001_111_101_111,
            0b010_000_000_000_000
        };

        // Calculate PI using the Simon Plouffe formula, 1996.
        BigDecimal y = v(-3);
        for (int n = 1; n < 99; n++)
            y = y.add(v(n).multiply(v(2).pow(n)).multiply(f(n).pow(2)).divide(f(2 * n), 42, 0));

        for (int z = 0; z < 5; z++) {
            for (char c : y.toPlainString().substring(0, 21).toCharArray()) {
                if (c < 48) c = 58;
                int a = (t[c - 48] >>> z * 3) & 7;
                e(a / 4);
                e(a / 2 & 2);
                e(a & 1);
                e(0);
                e(0); // Not needed, but makes a better art with the cost of 5 chars.
            }
            e(10);
        }
    }

    static void e(int c) {
        System.out.print((char) (c < 2 ? c * 3 + 32 : c));
    }

    static BigDecimal v(int k) {
        return BigDecimal.valueOf(k);
    }

    static BigDecimal f(int k) {
        return k < 2 ? v(1) : f(k - 1).multiply(v(k));
    }
}

Output:

###         #  # #    #  ###  ###  ###  ###  ###  ###  ###  ###  ###  ###  ###  ###  ###  ###  ###  # #  
  #         #  # #    #  #    # #    #  #    #      #  #    # #  # #    #  # #    #    #    #  # #  # #  
###         #  ###    #  ###  ###  ###  ###  ###  ###  ###  ###  ###    #  ###  ###  ###  ###  ###  ###  
  #         #    #    #    #    #  #    # #    #    #    #  # #    #    #    #    #  #      #  # #    #  
###   #     #    #    #  ###  ###  ###  ###  ###  ###  ###  ###  ###    #  ###  ###  ###  ###  ###    # 
\$\endgroup\$
4
\$\begingroup\$

C, 253 250 chars

Approximates pi using the algorithm in @Sukminder's code (shamelessly borrowing and refactoring their code a bit). Outputs a binary PBM image, which could then e.g. be converted with ImageMagick.

b,c=70,e,f[71],g;v[71],j,k;L[5]={1072684944,792425072,492082832,256581624};
main(d){for(puts("P4\n8 100");b<c;)f[b++]=2;for(;d=0,g=--c*2;e=d%10){
for(b=c;d+=f[b]*10,f[b]=d%--g,d/=g--,--b;d*=b);v[j++]=e+d/10;}
for(;k<100;k++)putchar(L[k%5]>>3*v[k/5]&7);}

Here's what the output looks like with my Braille-based PPM renderer:

Screenshot of output

Has the same quirk as @Sukminder's answer in that it lacks a decimal separator. In addition, the output of mine is vertical, and whether it's human-readable is arguable...

Edit: applied @ugoren's suggestions.

\$\endgroup\$
1
  • \$\begingroup\$ Tiny improvements: move puts into for initialization, define L[5] and omit ,0. Make d a parameter to main (save a comma). \$\endgroup\$
    – ugoren
    Commented Jan 20, 2014 at 13:30
4
\$\begingroup\$

PHP 380

requires gd enabled for image output

<? header('Content-Type: image/png');$i=imagecreatetruecolor(84,5);$n=['71775777770','51115441550','51777771770','51411151510','71771771712'];$c=imagecolorallocate($i,255,255,255);$m=(6.28318/2).(5307*5).(28060387*32);$k=5;while($k--)for($j=0;$j<21;$j++){$p=str_pad(decbin($n[$k][($m[$j]!='.')?$m[$j]:10]),3,'0',0);$l=3;while($l--)$p[$l]&&imagesetpixel($i,$l+$j*4,$k,$c);}imagepng($i);

enter image description here

pi calculation: since base php has a default precision of 14 and i didn't want to recompile the server with the arbitrary precision extensions enabled, i couldn't even aproximate PI with the required decimals, so instead it calculates tau/2 and then the rest of the decimals

since the graphic is made of 0's and 1's, i may try using WBMP as the format later to see if i can remove gd

\$\endgroup\$
11
  • \$\begingroup\$ that image is red on black, and really small, but at 500% you can read it if you look close. (and aren't colorblind.) \$\endgroup\$
    – hildred
    Commented Jan 21, 2014 at 19:15
  • \$\begingroup\$ @hildred each character is 3x5 with 1 px between chars. the color is red just to reduce 4 chars, but considering that i won't win, i'll change it to white for readability \$\endgroup\$
    – Einacio
    Commented Jan 21, 2014 at 19:29
  • \$\begingroup\$ My comment was not intended as a criticism, but as an explanation, to encourage up votes. \$\endgroup\$
    – hildred
    Commented Jan 21, 2014 at 19:32
  • \$\begingroup\$ Would imagecreateindex save any characters? does such function exist? \$\endgroup\$
    – hildred
    Commented Jan 21, 2014 at 19:33
  • \$\begingroup\$ @hildred when working with a pallete image (imagecreate), the first invocation of imagecolorallocate sets the background color, and a second one is needed to set the writing color. so it ends longer \$\endgroup\$
    – Einacio
    Commented Jan 21, 2014 at 19:35
4
\$\begingroup\$

C+LaserWriter printer 599 - 10 = 589

Pipe the output to your LaserWriter! :) This should work on a Lisa (with a C compiler).

It calculates pi in the printer by calculating the sum of the lengths of the line-segments which approximate a Bezier curve sequence which approximates a half-circle, divided by diameter, times 2.

main(){
printf("/dist{dtransform dup mul exch dup mul add sqrt}def");
printf("/len{3 2 roll sub 3 1 roll exch sub dist}def");
printf("/pi{0 0 2 index 0 180 arc closepath flattenpath");
printf("[{2 copy}{2 copy 6 2 roll len 3 1 roll}{}{counttomark -2 roll len\n");
printf("counttomark 2 add 1 roll counttomark 1 sub{add}repeat\n");
printf("exch pop exch pop exch div 2 mul}pathforall}def\n");
printf("matrix setmatrix 100 dup scale 10 setflat 100 pi 10 string cvs\n");
printf("matrix defaultmatrix setmatrix/Palatino-Roman findfont 10 scalefont setfont\n");
printf("100 700 moveto show showpage");
}

Ungolfed Level-1 (1985-compatible) PostScript:

%!
/dist { % dx dy  .  dz  
    dtransform
    dup mul exch dup mul add sqrt
} def 

/len { % x1 y1 x2 y2  .  dist(y2-y1,x2-x1)
    3 2 roll % x1 x2 y2 y1
    sub 3 1 roll exch sub % y2-y1 x2-x1
    dist
} def 

/pi { % rad 
    0 0 2 index 0 180 arc closepath % rad 
    flattenpath
    [   
    { % rad [ x(0) y(0)     (m)print
        2 copy 
    } %moveto proc
    { % rad [ ... x(n-1) y(n-1) x(n) y(n)     (l)print
        2 copy 6 2 roll len % rad [ ... x(n) y(n) dist
        3 1 roll % rad [ ... dist x(n) y(n)
    } %lineto proc
    {} %curveto proc % n.b. flattenpath leaves no curve segments
    { % rad [ x(0) y(0) dist(1) dist(2) ... dist(n-1) x(n) y(n)     (c)print
        counttomark -2 roll len % rad [ dist(1) dist(2) ... dist(n)
        counttomark 2 add 1 roll % dist(n) rad [ dist...
        counttomark 1 sub { add } repeat % dist(n) rad [ sum_dist
        exch pop % dist(n) rad sum_dist
        exch pop % dist(n) sum_dist
        exch % sum_dist dist(n)
        div  % length_of_half_circle/diameter
        2 mul % C/d 
    } %closepath proc
    pathforall
} def 

matrix setmatrix
100 dup scale
10 setflat
100 pi 10 string cvs 
matrix defaultmatrix setmatrix
/Palatino-Roman findfont 10 scalefont setfont
100 700 moveto show

Output:

ps_pi

\$\endgroup\$
4
  • \$\begingroup\$ I suppose I need to make a font, too. \$\endgroup\$ Commented Feb 28, 2014 at 0:06
  • \$\begingroup\$ Hmm. Never going to get enough digits this way. PS only has 32-bit floats. \$\endgroup\$ Commented Feb 28, 2014 at 0:13
  • \$\begingroup\$ cool idea, I like postscript for golfing. \$\endgroup\$
    – hildred
    Commented Feb 28, 2014 at 2:51
  • \$\begingroup\$ I do have a bitmap font for the digits but golfing will just ruin it! \$\endgroup\$ Commented Mar 1, 2014 at 11:49
2
\$\begingroup\$

Java, 1574 2643 1934 characters

Compressed 1934 characters:

    public static void main(String[] args){int[][][]num={{{1,1,1},{1,0,1},{1,0,1},{1,0,1},{1,1,1}},{{0,0,1},{0,0,1},{0,0,1},{0,0,1},{0,0,1}},{{1,1,1},{0,0,1},{1,1,1},{1,0,0},{1,1,1}},{{1,1,1},{0,0,1},{1,1,1},{0,0,1},{1,1,1}},{{1,0,1},{1,0,1},{1,1,1},{0,0,1},{0,0,1}},{{1,1,1},{1,0,0},{1,1,1},{0,0,1},{1,1,1}},{{1,1,1},{1,0,0},{1,1,1},{1,0,1},{1,1,1}},{{1,1,1},{0,0,1},{0,0,1},{0,0,1},{0,0,1}},{{1,1,1},{1,0,1},{1,1,1},{1,0,1},{1,1,1}},{{1,1,1},{1,0,1},{1,1,1},{0,0,1},{0,0,1}},{{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,1}}};BufferedImage image=new BufferedImage(103,5,BufferedImage.TYPE_3BYTE_BGR);for(int q=0;q<103;q++){for(int w=0;w<5;w++){image.setRGB(q,w,0xFFFFFF);}}int loc = 0;String g=String.valueOf(pi(20));for(int w=0;w<g.length()-1;w++){Integer n=0;if(g.charAt(w)=='.'){n=10;}else{n=Integer.parseInt(String.valueOf(g.charAt(w)));}for(int t=0;t<5;t++){for(int q=0;q<3;q++){int c=num[n][t][q]==1?0x000000:0xFFFFFF;image.setRGB(loc+q,t,c);}}loc+=5;}try{BufferedImage bi=image;File f=new File("o.png");ImageIO.write(bi,"png",f);}catch(IOException e){}}public static BigDecimal pi(final int SCALE){BigDecimal a=BigDecimal.ONE;BigDecimal b=BigDecimal.ONE.divide(sqrt(new BigDecimal(2),SCALE),SCALE,BigDecimal.ROUND_HALF_UP);BigDecimal t=new BigDecimal(0.25);BigDecimal x=BigDecimal.ONE;BigDecimal y;while(!a.equals(b)){y=a;a=a.add(b).divide(new BigDecimal(2),SCALE,BigDecimal.ROUND_HALF_UP);b=sqrt(b.multiply(y),SCALE);t=t.subtract(x.multiply(y.subtract(a).multiply(y.subtract(a))));x=x.multiply(new BigDecimal(2));}return a.add(b).multiply(a.add(b)).divide(t.multiply(new BigDecimal(4)),SCALE,BigDecimal.ROUND_HALF_UP);}public static BigDecimal sqrt(BigDecimal A,final int SCALE){BigDecimal x0=new BigDecimal("0");BigDecimal x1=new BigDecimal(Math.sqrt(A.doubleValue()));while(!x0.equals(x1)){x0=x1;x1=A.divide(x0,SCALE,BigDecimal.ROUND_HALF_UP);x1=x1.add(x0);x1=x1.divide(new BigDecimal(2),SCALE,BigDecimal.ROUND_HALF_UP);}return x1;}}

Expanded 2643 characters:

public static void main(String[] args) {
    int[][][] num = { { { 1, 1, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 1, 1 } },
            { { 0, 0, 1 }, { 0, 0, 1 }, { 0, 0, 1 }, { 0, 0, 1 }, { 0, 0, 1 } },
            { { 1, 1, 1 }, { 0, 0, 1 }, { 1, 1, 1 }, { 1, 0, 0 }, { 1, 1, 1 } },
            { { 1, 1, 1 }, { 0, 0, 1 }, { 1, 1, 1 }, { 0, 0, 1 }, { 1, 1, 1 } },
            { { 1, 0, 1 }, { 1, 0, 1 }, { 1, 1, 1 }, { 0, 0, 1 }, { 0, 0, 1 } },
            { { 1, 1, 1 }, { 1, 0, 0 }, { 1, 1, 1 }, { 0, 0, 1 }, { 1, 1, 1 } },
            { { 1, 1, 1 }, { 1, 0, 0 }, { 1, 1, 1 }, { 1, 0, 1 }, { 1, 1, 1 } },
            { { 1, 1, 1 }, { 0, 0, 1 }, { 0, 0, 1 }, { 0, 0, 1 }, { 0, 0, 1 } },
            { { 1, 1, 1 }, { 1, 0, 1 }, { 1, 1, 1 }, { 1, 0, 1 }, { 1, 1, 1 } },
            { { 1, 1, 1 }, { 1, 0, 1 }, { 1, 1, 1 }, { 0, 0, 1 }, { 0, 0, 1 } },
            { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 1 } } };

    BufferedImage image = new BufferedImage(103, 5, BufferedImage.TYPE_3BYTE_BGR);

    for (int q = 0; q < 103; q++) {
        for (int w = 0; w < 5; w++) {
            image.setRGB(q, w, 0xFFFFFF);
        }
    }

    int loc = 0;

    String g = String.valueOf(pi(20));
    for (int w = 0; w < g.length()-1; w++) {
        Integer n = 0;
        if (g.charAt(w) == '.') {
            n = 10;
        } else {
            n = Integer.parseInt(String.valueOf(g.charAt(w)));
        }
        for (int t = 0; t < 5; t++) {
            for (int q = 0; q < 3; q++) {
                int c = num[n][t][q] == 1 ? 0x000000 : 0xFFFFFF;
                image.setRGB(loc + q, t, c);
            }
        }
        loc += 5;
    }
    try {
        BufferedImage bi = image;
        File outputfile = new File("out2.png");
        ImageIO.write(bi, "png", outputfile);
    } catch (IOException e) {

    }
}

public static BigDecimal pi(final int SCALE) {
    BigDecimal a = BigDecimal.ONE;
    BigDecimal b = BigDecimal.ONE.divide(sqrt(new BigDecimal(2), SCALE), SCALE, BigDecimal.ROUND_HALF_UP);
    BigDecimal t = new BigDecimal(0.25);
    BigDecimal x = BigDecimal.ONE;
    BigDecimal y;

    while (!a.equals(b)) {
        y = a;
        a = a.add(b).divide(new BigDecimal(2), SCALE, BigDecimal.ROUND_HALF_UP);
        b = sqrt(b.multiply(y), SCALE);
        t = t.subtract(x.multiply(y.subtract(a).multiply(y.subtract(a))));
        x = x.multiply(new BigDecimal(2));
    }
    return a.add(b).multiply(a.add(b)).divide(t.multiply(new BigDecimal(4)), SCALE, BigDecimal.ROUND_HALF_UP);

}

public static BigDecimal sqrt(BigDecimal A, final int SCALE) {
    BigDecimal x0 = new BigDecimal("0");
    BigDecimal x1 = new BigDecimal(Math.sqrt(A.doubleValue()));
    while (!x0.equals(x1)) {
        x0 = x1;
        x1 = A.divide(x0, SCALE, BigDecimal.ROUND_HALF_UP);
        x1 = x1.add(x0);
        x1 = x1.divide(new BigDecimal(2), SCALE, BigDecimal.ROUND_HALF_UP);
    }
    return x1;
}

Pi method gathered from: https://stackoverflow.com/questions/8343977/calculate-pi-on-an-android-phone?rq=1

\$\endgroup\$
3
  • \$\begingroup\$ It looks like you used a constant instead of calculating PI. \$\endgroup\$
    – hildred
    Commented Jan 19, 2014 at 1:43
  • \$\begingroup\$ That is a nice twist. Working on it now. \$\endgroup\$
    – Clayton
    Commented Jan 19, 2014 at 2:02
  • \$\begingroup\$ You may compress it a bit more by adding throws Exception in main and removing the try-catch block. Further, you may rename pi and sqrtmethods and the loc, args, SCALE, x0 and x1 variables to 1 char identifiers. And by the way you must add the complete class, this includes the class Foo{ declaration and the imports. \$\endgroup\$ Commented Jan 19, 2014 at 3:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.