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\$
20
  • 1
    \$\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
  • 2
    \$\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\$ Commented Jan 19, 2014 at 18:40

11 Answers 11

23
\$\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_ao'[int(c)])>>j%7)for j in range(17)];p+=7 i.show() 

The byte count assumes that the escaped character `` 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_ao'[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:

$$\frac{\pi}{4}=\sum\limits_{n=0}^\infty\frac{(-1)^n}{2n+1}=1-\frac{1}{3}+\frac{1}{5}-\frac{1}{7}+\frac{1}{9}-\frac{1}{11}+\frac{1}{13}-\dots$$

Then split each term in half, combining neighboring terms:

$$\frac{\pi}{4}=\frac{1}{2}+(\frac{1}{2}-\frac{1}{6})-(\frac{1}{6}-\frac{1}{10})+(\frac{1}{10}-\frac{1}{14})-(\frac{1}{14}-\frac{1}{18})+(\frac{1}{18}-\frac{1}{22})-(\frac{1}{22}-\frac{1}{26})+\dots$$

Simplified:

$$\frac{\pi}{4}=\frac{1}{2}+\frac{1}{3}-\frac{1}{15}+\frac{1}{35}-\frac{1}{63}+\frac{1}{99}-\frac{1}{143}+\dots$$

Generalized:

$$\frac{\pi}{4}=\frac{1}{2}+\sum\limits_{n=0}^\infty\frac{(-1)^n}{(2n+1)(2n+3)}$$

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:

$$\frac{\pi}{4}=\frac{1}{2}+\frac{1}{6}+(\frac{1}{6}-\frac{1}{30})-(\frac{1}{30}-\frac{1}{70})+(\frac{1}{70}-\frac{1}{126})-(\frac{1}{126}-\frac{1}{198})+(\frac{1}{198}-\frac{1}{286})-\dots$$ $$\frac{\pi}{4}=\frac{1}{2}+\frac{1}{6}+\frac{2}{15}-\frac{2}{105}+\frac{2}{315}-\frac{2}{693}+\frac{2}{1287}-\dots$$

And again:

$$\frac{\pi}{4}=\frac{1}{2}+\frac{1}{6}+\frac{1}{15}+(\frac{1}{15}-\frac{1}{105})-(\frac{1}{105}-\frac{1}{315})+(\frac{1}{315}-\frac{1}{693})-(\frac{1}{693}-\frac{1}{1287})+\dots$$ $$\frac{\pi}{4}=\frac{1}{2}+\frac{1}{6}+\frac{1}{15}+\frac{2}{35}-\frac{2}{315}+\frac{2}{1155}-\frac{2}{3003}+\dots$$

And again:

$$\frac{\pi}{4}=\frac{1}{2}+\frac{1}{6}+\frac{1}{15}+\frac{1}{35}+(\frac{1}{35}-\frac{1}{315})-(\frac{1}{315}-\frac{1}{1155})+(\frac{1}{1155}-\frac{1}{3003})-\dots$$ $$\frac{\pi}{4}=\frac{1}{2}+\frac{1}{6}+\frac{1}{15}+\frac{1}{35}+\frac{8}{315}-\frac{8}{3465}+\frac{8}{15015}-\dots$$

And once more for good measure:

$$\frac{\pi}{4}=\frac{1}{2}+\frac{1}{6}+\frac{1}{15}+\frac{1}{35}+\frac{4}{315}+(\frac{4}{315}-\frac{4}{3465})-(\frac{4}{3465}-\frac{4}{15015})+\dots$$ $$\frac{\pi}{4}=\frac{1}{2}+\frac{1}{6}+\frac{1}{15}+\frac{1}{35}+\frac{4}{315}+\frac{8}{693}-\frac{8}{9009}+\dots$$

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:

$$\frac{\pi}{4}=\frac{1}{2}+\frac{1}{2\cdot+3}+\frac{1}{3\cdot+5}+\frac{1}{5\cdot+7}+\frac{2^2}{3^2\cdot+5\cdot+7}+\frac{2^2}{3^2\cdot+7\cdot+11}+\dots$$

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

$$\frac{\pi}{4}=\frac{1}{2}+\frac1{2\cdot+3}+\frac1{3\cdot+5}+\frac{3}{3\cdot+5\cdot+7}+\frac{2^2\cdot+3}{3\cdot+5\cdot+7\cdot+9}+\frac{2^2\cdot+3\cdot+5}{3\cdot+5\cdot+7\cdot+9\cdot+11}+\dots$$

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:

$$\frac{\pi}{2}=1+\frac{1}{3}+\frac{2}{3\cdot+5}+\frac{2\cdot+3}{3\cdot+5\cdot+7}+\frac{2^3\cdot+3}{3\cdot+5\cdot+7\cdot+9}+\frac{2^3\cdot+3\cdot+5}{3\cdot+5\cdot+7\cdot+9\cdot+11}+\dots$$

23 = 2·4, so:

$$\frac{\pi}{2}=1+\frac{1}{3}+\frac{2}{3\cdot+5}+\frac{2\cdot+3}{3\cdot+5\cdot+7}+\frac{2\cdot+3\cdot+4}{3\cdot+5\cdot+7\cdot+9}+\frac{2\cdot+3\cdot+4\cdot+5}{3\cdot+5\cdot+7\cdot+9\cdot+11}+\dots$$

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:

$$\frac{\pi}{2}=1+\frac{1}{3}(1+\frac{2}{5}+\frac{2\cdot+3}{5\cdot+7}+\frac{2\cdot+3\cdot+4}{5\cdot+7\cdot+9}+\frac{2\cdot+3\cdot+4\cdot+5}{5\cdot+7\cdot+9\cdot+11}+\dots$$
$$\frac{\pi}{2}=1+\frac{1}{3}(1+\frac{2}{5}(1+\frac{3}{7}+\frac{3\cdot+4}{7\cdot+9}+\frac{3\cdot+4\cdot+5}{7\cdot+9\cdot+11}+\dots$$
$$\frac{\pi}{2}=1+\frac{1}{3}(1+\frac{2}{5}(1+\frac{3}{7}(1+\frac{4}{9}+\frac{4\cdot+5}{9\cdot+11}+\dots$$ $$\frac{\pi}{2}=1+\frac{1}{3}(1+\frac{2}{5}(1+\frac{3}{7}(1+\frac{4}{9}(1+\frac{5}{11}(1+\dots+\frac{n}{2n+1}(1+\dots$$

This can be rewritten as a recurrence relation:

$$x_{n}=1+\frac{n}{2n+1}x_{n+1}$$

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\$ 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\$ 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\$ Commented Jan 20, 2014 at 9:38
8
\$\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\$ Commented Jan 24, 2014 at 8:14
7
\$\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\$ 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
5
\$\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\$
4
  • 5
    \$\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\$ 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
  • 5
    \$\begingroup\$ He's got a point. You can not render a * without using fonts, I think you're disqualified \$\endgroup\$ Commented Feb 18, 2014 at 13:24
  • \$\begingroup\$ Change it to actually output black and white pixels with some imaging library and you should be okay right? \$\endgroup\$ Commented Nov 2 at 11:54
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\$ 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\$ 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\$ 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\$ Commented Jan 21, 2014 at 19:32
  • \$\begingroup\$ Would imagecreateindex save any characters? does such function exist? \$\endgroup\$ 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\$ Commented Jan 21, 2014 at 19:35
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\$ Commented Jan 19, 2014 at 1:43
  • \$\begingroup\$ That is a nice twist. Working on it now. \$\endgroup\$ 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
0
\$\begingroup\$

Reusing and adapting my own answer to Rabinowitz-Wagon π formula

Tcl/Tk, 153 bytes

incr n incr d time {set n ([incr d [expr 2*$d*$k]]+$n*$k) incr k -1} [set k 99] pack [canvas .c] .c cr text 99 4 -te [expr 2*$n/$d.] update .c p -fi c.ps 

This generates a Postscript file, with the pi text.

Can't demonstrate online, because for it to work, it needs the Tk component.

But I can show a screenshot of the generated document: enter image description here


If output to terminal was allowed, I would save some bytes and ditch the need of Tk.

Tcl, 100 bytes

incr n incr d time {set n ([incr d [expr 2*$d*$k]]+$n*$k) incr k -1} [set k 99] puts [expr 2*$n/$d.] 

Try it online!


Tcl natively does not support floats with more precision than double, so here it is my answer with only 18 chars and 16 decimal places, where the last one does not match:

My result: 3.1415926535897936 Expected: 3.141592653589793238 
\$\endgroup\$
0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.