views:

2598

answers:

24

The challenge

The shortest code by character count to generate seven segment display representation of a given hex number.

Input

Input is made out of digits [0-9] and hex characters in both lower and upper case [a-fA-F] only. There is no need to handle special cases.

Output

Output will be the seven segment representation of the input, using those ASCII faces:

  _       _   _       _   _   _   _   _   _       _       _   _  
 | |   |  _|  _| |_| |_  |_    | |_| |_| |_| |_  |    _| |_  |_  
 |_|   | |_   _|   |  _| |_|   | |_|  _| | | |_| |_  |_| |_  |

Restrictions

The use of the following is forbidden: eval, exec, system, figlet, toilet and external libraries.

Test cases:

Input:
    deadbeef

Output:
        _  _        _  _  _ 
     _||_ |_| _||_ |_ |_ |_ 
    |_||_ | ||_||_||_ |_ |  


Input:
    4F790D59

Output:
        _  _  _  _     _  _ 
    |_||_   ||_|| | _||_ |_|
      ||    | _||_||_| _| _|

Code count includes input/output (i.e full program).

+28  A: 

C89 (181 characters; args)

char*q,a=3;p(l){putchar(*q?"|#]u&rzc~vn:X=ZJ"[*q-(*q&64?55:48
)&15]+1>>l&1?"|_||_|_"[l]:32:10);}main(r,v)char**v;{for(;a--
;p())for(q=v[1];*q;++q)for(r=3;r--;)p(a-2?5-r-a*3:r-1?7:6);}

C89 (192 characters; stdin)

char*q,b[9],gets(char*),a=3;p(l){putchar(*q?"|#]u&rzc~vn:X=ZJ"[*
q-(*q&64?55:48)&15]+1>>l&1?"|_||_|_"[l]:32:10);}main(r){for(gets
(b);a--;p())for(q=b;*q;++q)for(r=3;r--;)p(a-2?5-r-a*3:r-1?7:6);}

Explanation:

char*q,b[9],gets(char*),a=3;
p(l){
    putchar(*q?
        /*
         * Each element of the magic string is decremented so 0x7F is
         * '~' (0x7E).  Each bit before the decrement represents
         * a segment (thus "8" is 0x7F (0x7E), i.e. all 7 bits on).
         * Bit 7 is always cleared so p(7) always prints a space.
         */
        "|#]u&rzc~vn:X=ZJ"
        [*q-(*q&64?55:48)&15]+1 /* d[ascii2hex(*q)] + 1 */
        >>l&1                   /* Is bit 'l' set? */
            ?"|_||_|_"[l]       /* Yes; choose _ or | by position. */
            :32                 /* No; enter a space. */
    :10);                       /* End of line. */
}
main(r){
    for(gets(b);a--;p())                /* Grab input and loop through each of 3 lines (a = 2, 1, 0). */
        for(q=b;*q;++q)                 /* Iterate across input. */
            for(r=3;r--;)               /* For each of three characters across... */
                p(a-2?5-r-a*3:r-1?7:6); /* Print the segment, mapping position to the bit. */
}
strager
you got to explain this...
Nifle
oh my head hurts now...
CmdrTallen
Hah. I'll format it and add some comments...
strager
You can save 4 more characters in the args version if you remove the explicit `int` for `r` in the `main` declaration.
dreamlax
@dreamlax, Sadly, no; that is not possible. If one argument is typed in C89, all must be.
strager
@strager: You could do `main(r,v)char**v;{.....` which would still save you 2 characters.
dreamlax
strager
+4  A: 

My first code golf. 279 non-whitespace characters, 433 including spaces. I'm sure it could be shorter in Python.

Python

import sys
w = sys.stdout.write
i = raw_input()
d = [111,9,94,91,57,115,119,73,127,123,125,55,102,31,118,116]
p = [' _ ','|','_','|','|','_','|']
j = 0
for r in range(3):
    for c in i.lower():
        for n in range(3 if r else 1):
            s = p[j+n]
            if (1<<6-j-n) & d[ord(c)-(ord('0') if c.isdigit() else ord('a')+6)]:
                w(s)
            else:
                w(' '*len(s))
    j += n+1
    w('\n')
FogleBird
I'm not Python junkie, but can't you combine the `import` and `w =` lines into one? Also, the OP asks for input and output to be handled by the program. It seems you're setting a variable and operating off that.
strager
**@strager** I fixed the input to come from stdin.
Steve Losh
@strager: You could convert import and w= to from sys import sys.stdout.write as w, but i think that ends up more chars than the original, albeit 1 less line.
KFro
sys.stdout is a file object, not a submodule. No, you cannot import sys.stdout.write.
ACoolie
+2  A: 

Java 1.5 - 272 characters with unnecessary whitespace removed

Much shorter than earlier version, using some ideas from JRL. Could be made shorter by using command line arguments, but that goes against the spec.

class D{public static void main(String[]a){char[]q=new java.util.Scanner
(System.in).nextLine().toCharArray();int j=0,k;for(;j<9;j+=3){for(int c:
q){for(k=j;k<j+3;k++){System.out.print(("{H=mNgwI\177o_v3|7\027".charAt(
"0123456789abcdef".indexOf(c|32))&"\0\1\0\2\4\10\20 @".charAt(k))>0?
" _ |_||_|".charAt(k):32);}}System.out.println();}}}
Simon Nickerson
Actually, your code can be reduced down to 291 chars :) if you allow the string to come from the command line and shuffle stuff a bit:class S{public static void main(String[]a){for(int j=0,k;j<9;j+=3){for(int i:a[0].toLowerCase().toCharArray())for(k=j;k<j+3;k++)System.out.print(("{H=mNgwI\177o_v3|7\027".charAt("0123456789abcdef".indexOf(i))System.out.println();}}}
JRL
You can further reduce this to 272 :) as handling uppercase chars wasn't a requirement toLowerCase() may be safely removed (13 chars). toCharArray() may be replaced by the shorter getBytes() (3). And the lengthy System.out.print() may be replaced by string concatenation (3), summing up to 19: class S{public static void main(String[]a){for(int j=0,k;j<9;j+=3){String s="";for(int i:a[0] .getBytes())for(k=j;k<j+3;k++)s+=("{H=mNgwI\177o_v3|7\027".charAt("0123456789abcdef".indexOf(i))System.out.println(s);}}} ... code golf is fun :)
sfussenegger
The spec says hex characters "in both upper and lower case" may be in the input.
hobbs
@hobbs: damn, don't know how I could have missed that - most likely as it was (too?) early in the morning. However, it still works! the reason is that `.indexOf(c|32)` which is equal for lowercase and uppercase characters. See: `groovy -e 'for (int c in "aAbBcCdDeEfF") println Integer.toString(c|32,2)'`
sfussenegger
For the legal input characters, "0123456789abcdef".indexof(c|32) is equivalent to (c%32+9)%25, saving 21 bytes.
meriton
A: 

Compiles with -Wall and can be understood; not very short (~400 chars). In a serious implementation, I'd pull the char->index out of the output part (and store the indices in a buffer rather than the chars). Global r is initialized to 0, which it wouldn't if it were a local in main.

#include <stdio.h>
typedef char const* R;
R _=" _ ",S="   ",I="  |",J=" _|",L="|_ ",U="|_|",N="| |",C="|  ";
R s[][16]={
    {_,S,_,_,S,_,_,_,_,_,_,S,_,S,_,_},
    {N,I,J,J,U,L,L,I,U,U,U,L,C,J,L,L},
    {U,I,L,J,I,J,U,I,U,J,N,U,L,U,L,C}};
int r,c,i;
int main(){
    char b[999];
    scanf("%s",b);
    for(;r<3;++r)
        for(c=0;;) {
            i=b[c++]-'0';
            if (i>16) i-=7;
            if (i>15) i-=32;
            if (i<0||i>15){putchar('\n');break;}
            printf("%s",s[r][i]);
        }
    return 0;
}
wrang-wrang
`gets(b)` is shorter than `scanf("%s",b)`. That can be placed in the first `for`'s initializer. In C89, there's no need to have those `int`'s there. The `return` isn't necessary, either. You can put `c`'s declaration as a parameter to `main` to save a character. Replace `'\n'` with 10, and `'0'` with 48. You can borrow my `ascii2hex` if you want, as it's much shorter than all those `if`'s. =] You can also remove the space after `#include`. The `typedef` isn't necessary because you can put it all together without much loss of characters, and can put `b`'s declaration in that list as well.
strager
Does not compile with GCC.
LiraNuna
Your C89 points are surely right. C99 and C++ require a return and don't allow implicit int return or param type. I was forced to use char const* instead of char * (by C++ or C99, not sure which), so threw in a typedef.
wrang-wrang
It certainly compiles with gcc 3.4.4 -Wall - I just copy/pasted to check.
wrang-wrang
Not on GCC 4.3 which is much more strict.
LiraNuna
Weird, you're right - even after I made R = char const* const! It compiles with g++ 4.3 though :)
wrang-wrang
+2  A: 

C++, 286 bytes

Hehe, everybody has come up with a more concise way to represent the data.

Anyway, so that it wouldn't be a complete waste of time (input from command line):

#include<cstdio>
#define P(x)putchar(x);
int main(int,char**v){int i,j,d[]={0,6947821,0,7209841,7734140,1180575,8257861,
3933037,1442811};char*p,c[]="|_|";for(++v;*v;++v){for(i=0;i<9;i+=3){for(p=*v;*p;
++p){for(j=0;j<3;++j)P(1<<((*p>96?*p-32:*p)-48)&d[i+j]?c[j]:32)P(32)}P(10)}P(10)
}}

And unobfuscated:

#include <cstdio>

void out(const char* s)
{
    int i, j;
    const char* c = "|_|";
    unsigned d[9] = {0, 6947821, 0, 7209841, 7734140, 1180575, 8257861, 3933037, 1442811};
    for (i = 0; i < 9; i += 3) {
        for (const char* p = s; *p; ++p) {
            for (j = 0; j != 3; ++j)
                putchar(1 << ((*p > 96 ? *p - 32 : *p) - '0') & d[j + i] ? c[j] : ' ');
            putchar(' ');
        }
        putchar('\n');
    }
}

int main(int, char** argv)
{
    for (++argv;*argv;) {
        out(*argv++);
        putchar('\n');
    }
}

The magic numbers refer to which characters have a _ or a | at a particular position. E.g if 0, 3, 5, and 'A' all have a | somewhere, the number would be 1 << n('0') | 1 << n('3') | 1 << n('5') | 1 << n('A') - where n(x) means x - '0'. This assumes at least 32-bit integers, as there is a small gap between '9' and 'A' in ASCII chart.

Both codes were edited recently: use one-dimensional array instead of two-dimensional (d), own non-error-proof "toupper" instead of including cctype, moved everything into main, another way of looping over command line arguments, more condensed variable declarations, magic values instead of char constants and a few other minor changes.

UncleBens
There's no need for `const` is there? You can map `d` to a 1D array and perhaps save some characters there. Replace character constants with their decimal values, if you want it to be even shroter. Since you're including `cstdio` anyway, `puts(0)` is shorter than `putchar(10)`. You can write `main`'s `for` loop as: `for(;--argc>0;puts(0))out(*++argv);` (I think). I wonder what those magic numbers are... Care to explain how you got them?
strager
At least c requires const (for warning-free code). I suppose there are couple of places where to lose a few bytes. Numbers explained in main post.
UncleBens
`argv[i]` is of type `char*`, so `out` can accept a `char*` instead of a `const char*`. Also, I missed the explanation on the bottom; woops. ;P
strager
@strager: I made a number of changes to condense the code and updated the post to new version. Looping over command line arguments can also rely on there being a NULL pointer as a sentinel (I seem to remember so). I'd also keep the const char*, since a string literal is const, and the code would otherwise trigger a warning. The explanation of magic numbers was edited into the main post after you asked for them :)
UncleBens
I still took the advice of removing const from const char* and declaring the string as an array instead.
UncleBens
A: 

Mine isn't short, but it was fun to do:

~ dlamblin$ ./7seg 31337aAbcdeF
 _     _  _  _  _  _     _     _  _ 
 _|  | _| _|  ||_||_||_ |   _||_ |_ 
 _|  | _| _|  || || ||_||_ |_||_ |  

#include <stdio.h>
#define P(x) fputs(x,stdout)
#define Z "\33[3D\33[B"
#define a "   " Z
#define s " _ " Z
#define d "  |" Z
#define f " _|" Z
#define g "|  " Z
#define h "| |" Z
#define j "|_ " Z
#define k "|_|" Z
int main(int argc, char** argv){
char m=0,n,*b[]={s h k,a d d,s f j,s f f,a k d,s j f,s j k,s d d,s k k,s k d,
"","","","","","","",
s k h,a j k,s g j,a f k,s j j,s j g};
P("\n\n\n\33[3A");
while (argc>1&&0!=(n=argv[1][m++])){
P(b[n>96?n-80:n-48]);P("\33[3A\33[3C");
}
P("\n\n\n");
}

You can save a couple characters by removing some newlines, but I don't care to. That's 500 characters, or 482 if you take out newlines and argc>1&&

Requires VT100 escape code support and won't do more than 26 characters of output on an 80 column terminal.

PS I'd like to see more people show their output.

dlamblin
The output should be the same for all implementations, so it's not too useful to reproduce it (unless it's bugged =]).
strager
"The output should be the same for all implementations" `<-` Exactly why specs exist.
LiraNuna
It's just for added benefit, it's tough to trudge through reading most of these to verify them; yes I've run a couple; but if all you want to see is that it works, it doesn't take up much space. I might also note that my output is actually a little different from some of them as it contains terminal controls, and I take my input from the arguments; I suppose I didn't read it exactly.
dlamblin
+3  A: 

Python, 188 total chars

I haven't looked too much at the other solutions, but I'm sure there is still a lot of room for improvement.

n=int(raw_input(),16)
O=[""]*3
while n:O=["".join(" |_"[(m>>n%16*3+i&1)*(i%2+1)]for i in[2,1,0])+o
for o,m in zip(O,[0x482092490482,0xd9cdff3b76cd,0x9bef5f3d978f])];n>>=4
print"\n".join(O)
ACoolie
+4  A: 

Ruby: 175 (!!)

d="3yy0nxcoypnk4185nbr3k9ddjlhe".to_i 36
s=gets.chomp
o=''
for i in 0..2
s.each_char{|c|q=d>>c.to_i(16)*3
"|_|".each_char{|z|o<<(q&1>0?z:' ')
q>>=1}}
d>>=48
o<<"\n"
end
puts o

And when a bit more readable...

d="3yy0nxcoypnk4185nbr3k9ddjlhe".to_i 36
s=gets.chomp
o=''
for i in 0..2
  s.each_char { |c|
    q = d >> c.to_i(16) * 3
    "|_|".each_char { |z|
      o << (q & 1 > 0 ? z : ' ')
      q >>= 1
    }
  }
  d >>= 48
  o << "\n"
end
puts o
DigitalRoss
Sigh, 175 was looking good when I posted it...
DigitalRoss
+29  A: 

Perl, 134 characters

All linebreaks may be removed.

@s=unpack"C*",~"P\xdbLI\xc3a`[\@AB\xe0t\xc8df";
$_=<>;for$s(6,3,0){print map$s[hex$&]&1<<$_+$s?$_%2?"_":"|":" ",
0..2while/./g;print$/}

Explanation

@s stores the bits for each segment. The entries are in order from 0 to F (thanks to hex()) and the bits map to segments in this order:

6 7 x
3 4 5
0 1 2

with 0 being the LSB. (Bit 6 is unused). The values are stored packed in the string bit-inverted so there are a lot more printable characters; the ~ operator flips the bits and unpack gives me numbers (perl's bitwise operators are much clumsier when it comes to strings).

With the data in hand I read the input and proceed to loop over it three times; the only difference between the three loops is the bitmask required. For each character of input three characters of output are printed. The character to be printed is

$s[ hex $& ] & (1 << ($_ + $s) )
? ($_ % 2 ? "_" : "|" )
: " "

where @s is the lookup table, $s is the shift in effect depending on the row, and $_ is whether we're printing the 1st, 2nd, or 3rd character in the row. If the right bit in the lookup table entry is false it prints a space; otherwise it prints a "|" on the sides or a "_" in the middle.

hobbs
You do realize that this technically uses `eval()`
Brad Gilbert
You have a point, as much as I wish you didn't. It's not a "bad eval" and I'd love to see the rules revised in my favor ;) nonetheless I'll work on a /e-less one.
hobbs
No more s///e. It got shorter, actually. Made me rethink the code.
hobbs
Even better. ​
Brad Gilbert
I love it that the character `B` is used to encode the representation for `A`.
mobrule
A: 

Python one-liner (322 characters)

print(lambda l:(lambda s:'\n'.join([' '.join(x) for x in zip(*[l[c].split('\n')
for c in s])])))(dict(zip('0123456789abcdef', 'eJxdjrEBwDAIw3au0Ac9iUd0fJM2DTQD'
'g5ExJgkxTOMKYIzPDDUYORlNsZ3zppwuXsqt/pmmjVmZ\nH6M+9BTXZvU8Umg9fd03SOgvPw=='
.decode('base64').decode('zlib').split('\n/\n'))))(__import__('sys').argv[-1]
.lower())

Most of the length due to me being lazy and using zlib and base64 builtins for the lookup table. Run it from the command line like this:

$ python golf.py fedcba9876543210
lost-theory
+5  A: 

Man... can't beat the perl. Here's some py3k at 163 chars.

i=input()
[print(''.join('     | _  _||  | ||_ |_|'[(7&(d>>l))*3:][:3]for d
in[255&0xb4b61fa637bdbbbf89b7b3399b9e09af>>int(x,16)*8 for x in i]))for
l in[6,3,0]]

Explanation. First here's what it looked like completely unoptimized:

# segment positions, easily understandable as octal.  first digit is top row
# last digit is bottom row.  high bit is first column, low bit last.

a=[0o257, 0o011, 0o236, 0o233,
   0o071, 0o263, 0o267, 0o211,
   0o277, 0o273, 0o275, 0o067,
   0o246, 0o037, 0o266, 0o264]

# and the corresponding segments:
#   421    421    421    421    421    421    421    421  
b=['   ', '  |', ' _ ', ' _|', '|  ', '| |', '|_ ', '|_|']

# function to look for the proper segment for a decoded digit:
def lookup(digit, line):
    return b[ 7& (digit>>(6-line*3))]

#function to encode an ascii hex string into coded form suitible for
#above function
def code(i):
    return [a[int(x,16)] for x in i]

def fmt(i):
    return '\n'.join(''.join(lookup(d,l) for d in code(i)) for l in [0,1,2])

i = input()
print(fmt(i))

Then i go about finding ways to pack the data. First I can transform a into a big, long integer, last element first, 8 bits at a time. That produces, in octal: 0o2645541764615736673577046675463463347404657. written in hex, that's 0xb4b61fa637bdbbbf89b7b3399b9e09af, 90 chars shorter than the list of octals. To use it you have to rewrite code, of course. That looks like

def code_a_8(i):
    return [255&a_8>>int(x,16)*8 for x in i]

b can be concatenated, ''.join(b) is 26 chars, including the quotes. the lookup function must also change to support this.

def lookup_b_cat(d, l):
    return b_cat[(7&(d>>6-l*3))*3:][:3]

Next I just eliminate all unneeded syntax by folding functions and constants into the expression, and that's pretty much it.

It's possible to tighten up the printing a bit, too. Instead of joining the lines, just print them immediately. this results in a modified fmt():

def fmt_print(i):
    [print(''.join(lookup(d,l) for d in code(i))) for l in [0,1,2]]
TokenMacGuy
Replace the list l iterates over from [0,1,2] to [6,3,0]. Then "6-l*3" can be replaced with "l".
recursive
+1 for first unreadable python code
J. Random Coder
+1  A: 

C# - 576 characters (w/o linebreaks)

There's probably a better way to do this, but here's my solution:

using C=System.Console;class Z{static void Main(){new Z();}int L=0;Z(){
try{var s=C.ReadLine().ToUpper();C.Clear();foreach(var c in s){int n=(
int)c;var p=(n==48?"_ ;| |;|_|":n==49?";  |;  |":n==50?"_; _|;|_":n==51
?"_; _|; _|":n==52?";|_|;  |":n==53?"_;|_; _|":n==54?"_;|_;|_|":n==55?
"_;  |;  |":n==56?"_;|_|;|_|":n==57?"_;|_|; _|":n==65?"_;|_|;| |":n==66
?";|_;|_|":n==67?"_;|;|_":n==68?"; _|;|_|":n==69?"_;|_;|_":n==70?"_;|_;|"
:";;").Split(';');P(0);C.Write(" "+p[0]);P(1);C.Write(p[1]);P(2);C.Write
(p[2]);L+=4;}C.WriteLine();}catch{}}void P(int t){C.SetCursorPosition(L,t);}}
MiffTheFox
+3  A: 

F#, 294 chars

I did nothing clever, but still get a respectable score.

let rec G s=for r in[" _     _  _     _  _  _  _  _  _     _     _  _ ";"| |  | _| _||_||_ |_   ||_||_||_||_ |   _||_ |_ ";"|_|  ||_  _|  | _||_|  ||_| _|| ||_||_ |_||_ |  "]do Seq.iter(printf"%s"<<S r)s;printfn""
and S a h=a.Substring(3*(int h-if h<'A'then 48 elif h<'a'then 55 else 87),3)

With whitespace for clarity:

let rec G s=
    for r in[" _     _  _     _  _  _  _  _  _     _     _  _ ";
             "| |  | _| _||_||_ |_   ||_||_||_||_ |   _||_ |_ ";
             "|_|  ||_  _|  | _||_|  ||_| _|| ||_||_ |_||_ |  "] do 
        Seq.iter (printf "%s" << S r) s;
        printfn""
and S a h=
    a.Substring(3*(int h - if h<'A'
                           then 48 
                           elif h<'a'
                           then 55 
                           else 87),3)

Sample:

G("abcdefFEDCBA9876543210")

Output:

 _     _     _  _  _  _     _     _  _  _  _  _  _     _  _     _
|_||_ |   _||_ |_ |_ |_  _||  |_ |_||_||_|  ||_ |_ |_| _| _|  || |
| ||_||_ |_||_ |  |  |_ |_||_ |_|| | _||_|  ||_| _|  | _||_   ||_|
Brian
+! that's the approach in my mind too....respectable and very readable especially give F# which is Greek to me!
kenny
+1  A: 

Haskell, 259 chars.

Is Haskell bad for golf or is it me (my first golf)?

import Char
i=iterate
d b=map(`mod`b).i(`div`b)
z _ 0=' '
z a 1=a
r=take 3
main=
  getLine>>=
  putStr.unlines.foldl1(zipWith(++)).
  map(r.map r.i(drop 3).take 9.zipWith z(cycle"|_|").(0:).d 2.
    (d 256(0xddfd91edcd9cd97990f5*2^384+0x2d6df865ecbd*(2^520+2^776))!!).ord)

(split main to lines for readability)

yairchu
There are some puzzling things about code-golf. The results aren't what I initially expected to see.Often, C89 beats the fancy dynamic languages. The (non-Perl) VHLL's in general don't win. C# and Java usually get clobbered. I never win with Ruby but sometimes I get close or lead temporarily. Machine code once clobbered everyone, although I bet it was actually typed in as assembly, so I don't think it should count.I translated one of my Ruby entries to C89, but once it worked I kind of lost the motivation to #define-it into something looking like Perl. :-)
DigitalRoss
+5  A: 

C (170 characters)

i,j;main(c,s){char**r=s,*p=*++r;for(;i<3;)j--?putchar(!p[-1]?p=*r,++i,j=0,10:
"##3#3133X=W.<X/`^_G0?:0@"[i*8+c/2]-33>>c%2*3+j&1?"|_"[j&1]:32):(j=3,c=*p++&31,
c-=c>6?10:1);}

This takes the input string as a command-line argument. Conversion to use stdin would be one more character:

i,j;main(c){char s[99],*p=s;for(gets(s+1);i<3;)j--?putchar(!*p?p=s,++i,j=0,10:
"##3#3133X=W.<X/`^_G0?:0@"[i*8+c/2]-33>>c%2*3+j&1?"|_"[j&1]:32):(j=3,c=*++p&31,
c-=c>6?10:1);}

The stdin version can accept up to 98 input characters. Of course, any more than floor(terminalWidth / 3) will cause confusing line wrap.

The output for each character is treated like a 3x3 grid, where the cells in each row are the segments. A segment is either "on" or "off". If a segment is "on", either a '|' or a '_' is output, depending on position. If it's off, a space is output. The character array is an array of bits that determine whether each segment is on or off. More about that after the code:

i,j; /* Loop variables. As globals, they'll be initialized to zero. */
main(c,s){
    /* The signature for main is
     *
     *     main(int argc, char **argv)
     *
     * Rather than add more characters for properly declaring the parameters,
     * I'm leaving them without type specifiers, allowing them to default to
     * int.  On almost all modern platforms, a pointer is the same size as
     * an int, so we can get away with the next line, which assigns the int
     * value s to the char** variable r.
     */

    char**r=s,*p=*++r;
    /* After coercing the int s to a char** r, offset it by 1 to get the
     * value of argv[1], which is the command-line argument.  (argv[0] would
     * be the name of the executable.)
     */

    for(;i<3;) /* loop until we're done with 3 lines */

        j--?
         /* j is our horizontal loop variable.  If we haven't finished a
          * character, then ... */

            putchar(  /* ...we will output something */
                !p[-1]? /* if the previous char was a terminating null ... */

                    p=*r,++i,j=0,10
                    /* ... reset for the next row.  We need to:
                     *
                     * - reinitialize p to the start of the input
                     * - increment our vertical loop variable, i
                     * - set j to zero, since we're finished with this
                     *   "character" (real characters take 3 iterations of
                     *   the j loop to finish, but we need to short-circuit
                     *   for end-of-string, since we need to output only one
                     *   character, the newline)
                     * - finally, send 10 to putchar to output the newline. */

                    :"##3#3133X=W.<X/`^_G0?:0@"[i*8+c/2]-33>>c%2*3+j&1?
                    /* If we haven't reached the terminating null, then
                     * check whether the current segment should be "on" or
                     * "off".  This bit of voodoo is explained after the
                     * code. */

                        "|_"[j&1]:32
                        /* if the segment is on, output either '|' or '_',
                         * depending on position (value of j), otherwise,
                         * output a space (ASCII 32) */
            )/* end of putchar call */

            :(j=3,c=*p++&31,c-=c>6?10:1);
            /* this is the else condition for j--? above.  If j was zero,
             * then we need to reset for the next character:
             *
             * - set j to 3, since there are three cells across in the grid
             * - increment p to the next input character with p++
             * - convert the next character to a value in the range 0–15.
             *   The characters we're interested in, 0–9, A–F, and a–f, are
             *   unique in the bottom four bits, except the upper- and
             *   lowercase letters, which is what we want.  So after anding
             *   with 15, the digits will be in the range 16–25, and the
             *   letters will be in the range 1–6.  So we subtract 10 if
             *   it's above 6, or 1 otherwise.  Therefore, input letters
             *   'A'–'F', or 'a'–'f' map to values of c between 0 and 5,
             *   and input numbers '0'–'9' map to values of c between
             *   6 and 15.  The fact that this is not the same as the
             *   characters' actual hex values is not important, and I've
             *   simply rearranged the data array to match this order.
             */
}

The character array describes the character grids. Each character in the array describes one horizontal row of the output grid for two input characters. Each cell in the grid is represented by one bit, where 1 means that segment is "on" (so output a '|' or a '_', depending on position), and 0 means that segment is "off".

It takes three characters in the array to describe the entire grid for two input characters. The lowest three bits of each character in the array, bits 0-2, describe one row for the even input character of the two. The next three bits, bits 3-5, describe one row for the odd input character of the two. Bits 6 and 7 are unused. This arrangement, with an offset of +33, allows every character in the array to be printable, without escape codes or non-ASCII characters.

I toyed with several different encodings, including putting the bits for all 7 segments of an input character into one character in the array, but found this one to be the overall shortest. While this scheme requires 24 characters in the array to represent the segments of only 16 input characters, other encodings either required using non-ASCII characters (which unsurprisingly caused problems when I used this in my Morse Code golf answer), a lot of escape codes, and/or complex decoding code. The decoding code for this scheme is surprisingly simple, although it does take full advantage of C's operator precedence to avoid having to add any parentheses.

Let's break it into tiny steps to understand it.

"##3#3133X=W.<X/`^_G0?:0@"

This is the encoded array. Let's grab the appropriate character to decode.

[i*8

The first 8 characters describe the top row of segments, the next 8 describe the middle row of segments, and the last 8 describe the bottom row of segments.

 +c/2]

Remember that, by this point, c contains a value from 0 to 15, which corresponds to an input of ABCDEF0123456789, and that the array encodes two input characters per encoded character. So the first character in the array, '#', holds the bits for the top row of 'A' and of 'B', the second character, also '#', encodes the top row of 'C' and 'D', and so on.

-33

The encoding results in several values that are under 32, which would require escape codes. This offset brings every encoded character into the range of printable, unescaped characters.

>>

The right shift operator has lower precedence than arithmetic operators, so this shift is done to the character after subtracting the offset.

c%2*3

c%2 evaluates to zero for even numbers, and to one for odd numbers, so we'll shift right by three for odd characters, to get at bits 3–5, and not shift at all for even characters, providing access to bits 0–2. While I'd prefer to use c&1 for even/odd check, and that is what I use everywhere else, the & operator has too low precedence to use here without adding parentheses. The % operator has just the right precedence.

+j

Shift by an additional j bits to get at the correct bit for the current output position.

&1

The bitwise and operator has lower precedence than both the arithmetic operators and the shift operators, so this will test whether bit zero is set after shifting has brought the relevant bit into bit zero.

?

If bit zero is set ...

"|_"

... output one of these characters, chosen by ...

[j&1]

... whether our horizontal loop variable is even or odd.

:32

Otherwise (bit zero is not set), output 32 (space character).


I don't think I can trim this down much more, if any, and certainly not enough to beat hobbs's perl entry.

P Daddy
+13  A: 

COM Executable: 102 bytes

Assemble the following using A86 (this is the original, larger version):

dd 0801E8Ah,0BD80C380h,03B50154h,0D789D58Ah,0B20082BEh,077F33B03h,0C048A29h,0149F0420h
dd 020AD431h,088C402C4h,01468BC1h,0F8C1E0D3h,046220Fh,0AA036E8Dh,0DD75CAFEh,04609ED83h
dd 0C583D1EBh,0D0AB809h,075CDFEABh,0AA24B0C3h,021CD09B4h,05F0000C3h,020B7EBh,8EFB7C00h
dd 07C3EF75Fh,0BF7CF9E4h,0B6DE5FA2h
dw 0F47Ch
db 0DFh

Skizz

Edit:

The DosBox issue is probably the way the program assumes register values at start-up. Anyhow, here's the modified source that assembles to 102 bytes and should work with DosBox:

 mov bp,d1
 mov ch,3
 mov dx,ds ; if you want to use dos box, put "mov dx,08000h" here instead, it might fix the problem
 mov di,dx
l4: mov si,082h
l3: mov bl,3
 cmp byte ptr [si],0dh
 je l5
 mov cl,[si]
 cmp cl,40h
 jle l2
 add cl,9
l2: and cl,0fh
l1: mov ax,word ptr [bp+1]
 shl ax,cl
 sar ax,15
 and al,byte ptr [bp]
 add bp,3
 stosb
 dec bl
 jnz l1
 sub bp,9
 inc si
 jmp l3
l5: add bp,9
 mov ax,0d0ah
 stosw
 dec ch
 jnz l4
 mov al,'$'
 stosb
 mov ah,9
 int 21h
d1: ret
 dw 0
 db '_'
 dw 01011011111101011xb
 db ' '
 dw 0
 db '|'
 dw 01000111011111011xb
 db '_'
 dw 00011111011110111xb
 db '|'
 dw 01111100111100100xb
 db '|'
 dw 01010001010111111xb
 db '_'
 dw 01011011011011110xb
 db '|'
 dw 01101111111110100xb

Thanks to ephemient for a couple of tweaks!

Skizz
Does not work in DOSBOX?
LiraNuna
Doesn't work in DOSBOX for me either, but I dug up a Windows machine to try it on and it works there. I'm not sure what the problem is.
ephemient
The new version now runs in DOSBOX, but prints garbage.
ephemient
+12  A: 
ephemient
+6  A: 

Well, it's hard to beat a specialized language like Perl. However, here's Python version at 160 bytes:

i=input().lower()
for x in[' #_ 14bd# ','| 1237d#_ 017c#| 56bcef','| 134579#_ 147af#| 2cef']: print(' '.join(''.join(y[j in y]for y in x.split('#'))for j in i))

The ungolfed version:

input_ = input().lower()

for magic in [' #_ 14bd# ',
          '| 1237d#_ 017c#| 56bcef',
          '| 134579#_ 147af#| 2cef',
         ]:
    # We have three lines, so x iterates over 3 magic strings.
    print(' '.join(
                # This is the cycle iterating over digits.
                ''.join(
                    # For each line and digit we need to print several
                    # letters. To do this, we break a magic string into
                    # 3 chunks. A chunk usually contains digits that
                    # *don't* have an element there. 
                    chunk[digit in chunk]
                    # For example, lower right chunk y="| 2cef". For digits
                    # 2, c, e, f, there should be " ", for all others there
                    # should be "|". So for digits 2, c, e, f, `j in y` returns
                    # False and indexes y[0], for other digits it indexes y[1]. 
                for chunk in magic.split('#'))
          for digit in input_))
ilya n.
A: 

PHP, 232 characacters

$d=str_split('    _ | ||_ |_| _|  ||  124066153155046135134166144145142034173054133137',3);
foreach(str_split(strtolower($s))as$c){
    $c=hexdec($c)+8;$r[0].=$d[$d[$c][0]];$r[1].=$d[$d[$c][1]];$r[2].=$d[$d[$c][2]];i
}
echo implode("\n",$r);

Explanation

# Array containing possible lines
$d=array(
    0 => '   ',
    1 => ' _ ',
    2 => '| |',
    3 => '|_ ',
    4 => '|_|',
    5 => ' _|',
    6 => '  |',
    7 => '|  '
);

# Array mapping characters to 3 lines
$m=array(
    0 => '124', # i.e. The character '0' is represented by $d[1], $d[2] and $d[4]
    1 => '066',
    2 => '153',
    3 => '155',
    4 => '046',
    5 => '135',
    6 => '134',
    7 => '166',
    8 => '144',
    9 => '145',
    a => '142',
    b => '034',
    c => '173',
    d => '054',
    e => '133',
    f => '137',
);

# traverse $s and append to array $r
foreach (str_split(strtolower($s)) as $c) {
    $r[0].=$d[$m[$c][0]];
    $r[1].=$d[$m[$c][1]];
    $r[2].=$d[$m[$c][2]];
}

# echo $r
echo implode("\n",$r);

Optimizations:

  • The arrays are generated dynamically using str_split()
  • Both arrays are joined into one
  • Characters are mapped numerically using hexdec()
soulmerge
+1  A: 

Javascript 333 Characters When Packed

s=" ";
function r(p)
{
if(t[p]=="0")
return s;
return "_|_||_|".split("")[p];
}
function g(w){
a="";b=a;c=a;
z=[];
for(x=0;x<w.length;x++){
t=("000"+parseInt("6F095E5B397377497F7B7D37661F7674".substr(parseInt(w.substr(x,1),16)*2,2),16).toString(2)).slice(-7).split("");
a+=s+r(0)+s+s;
b+=r(1)+r(2)+r(3)+s;
c+=r(4)+r(5)+r(6)+s;
}
return a+"\n"+b+"\n"+c;
}


alert(g("0123456789deadbeef"));
pflunk
+4  A: 

Golfscript - 116 chars

$ echo -n deadbeef | ./golfscript.rb  led.gs 
    _  _        _  _  _ 
 _||_ |_| _||_ |_ |_ |_ 
|_||_ | ||_||_||_ |_ |

Make sure to save without an extra newline on the end or the input string will be printed at the end.

{32:^|}%:
{^' _':$@'14bd'{?~!=}:&~^}%n
{:x' |':|\'1237d'&$x'017c'&|x'56bcef'&}%n
{:x|\'134579'&$x'147af'&|x'2cef'&}%

How it works

Notice that more segments are turned on than off for the range 0-F. List the exceptions (digits that have the segment turned off) for each segment.

#Python version of the algorithm above
s=raw_input().lower()
J=''.join()
print J(' '+'_ '[c in'14bd']+' 'for c in s)
print J('| '[c in'1237d']+'_ '[c in'017c']+'| '[c in'56bcef']for c in s)
print J('| '[c in'134579']+'_ '[c in'147af']+'| '[c in'2cef']for c in s)
gnibbler
+1  A: 

dc - 172 chars

A bit late but a dc entry, almost to spec: it does only accept uppercase numbers, i.e. [0-9A-F] and may print extra 0s at the beginning. Anyway, here it is:

16iD9B4FE55FFBDFFA5BF5BAB774977sK
[   ][ _ ][  |][ _|][|  ][|_ ][| |][|_|]8[1-ds_:al_d0<L]dsLx
?dsNZdsZ
40sr[[1-dlNr10r^/10%80r^lKr/80%lr/8%;aPd0<L]dsLxAP]dsAx
lZ8srlAxlZ1srlAx

A little explanation (line breaks are not required):

The constant 0xD9B4FE55FFBDFFA5BF5BAB774977 encodes each digit in 7 bit, e.g. the number '4' is encoded as 0 111 010, meaning use the string '0' for the top (''), string 7 for the middle ('|_|') and string 2 for the bottom ('|').

The second line defines the strings and stores them in array 'a'

The third lines handles the input and checks how long the number is.

The fourth line does the computation. It created the "subroutine" A and execute it for the first line. The subroutine extracts the appropriate digit (1-dlNr10r^/10), then extracts the 7-bit encoding data (80r^lKr/80%), then takes the appropriate part for the specific line (lr/8%) loads the string and prints it (;aP) then it loops over all the digits (d0<L)

The last line does the same for lines 2 and 3 of the display.

Dan Andreatta
A: 

Scala, 234 bytes

val a=Seq("|_|"," _ ","  |","| |"," _|","|  ","|_ ","   ")
val m=argv(0).toLowerCase.map(_-'0').map(i=>if(i<10)i else i-39)
for(s<-Seq(0,16,32))
println(m.map(i=>a("171171111117171132440662000654660264240204306065"(i+s)-'0')).mkString)

Code is quite straight-forward: all 9 possible segments are packed into array and mapping between hex number and position. Probably, it could be packed better.

The code to calculate magic numbers:

val s = " _     _ *all numbers in one line here* |_||_ |  "
val gl = (0 to s.size / 3-1).map(c => s.substring(c*3, c*3+3 ))
// gl now contains flat list of string of 3 chars each

val arr=gl.toSet.toArray   // remove duplicates

// for each segment string find corresponding packed index
val n = gl.map( arr indexOf _).mkString

as result, n is the magic number, arr is the array of strings

Alexander Kosenkov
+2  A: 

BrainF***, 920 906 885 868 863 860 858 chars for digital clock

I started on this on the (now closed) digital clock code golf, so : is supported as well. Todo: handle lowercase.

-[>+<-----]->----[>,]<[<]>>[[->]<+[-<+]->]<+>-[>]+++[[<]>>[[>]>[>]+[<]<[<]+[>]>[
>]+[-<+]->[[>]>[>]<+[<]<[<]>+[>]+[-<+]->-]->]<[<]>+[-[>]+[-<+]+<[<]>[[>]+[-<+]->
+<[<]>-]>+]+[-->]+[->]-[>-<-----]>+++>-->->----<<<[>>+++>+++++>-[+++<]>]-<+[>]-[
<]>>>+[-<<+[->+]<<-[-[++>->>[>]>>+++++>>+++++>>>>>+++++>>>+++++++>>>>>>+++++>>>+
++++>>+[<+]<[<]<]>[-<++>>>[>]<--->+>+>->++++++>+>-->>>>>>>+++>-->-->>+>+>-->->->
>+++++>+[<++++]<[<]]<]>[-<+>>>[>]<--->++++++>+>-->+>-->+++++>>>>>>>+++>->-->>-->
->>->+>>-->+[<++++]<[<]]<+>-[[>]>[>]<[-]<[<]<[<]>-]>[>]>[>]+<-[-[-[-[-[-[-[-[-[[
<]<<.>...>>[>]<-]>[<+[<]<<.>.<.>.>>[>]<->]<]>[<+[<]<..>>[>]<->]<]>[<+[<]<<<<.>>>
.>>[>]<->]<]>[<+[<]<....>>[>]<->]<]>[<+[<]<<.<.>>..>>[>]<->]<]>[<+[<]<..<.>.>>[>
]<->]<]>[<+[<]<.<<.>.>.>>[>]<->]<]>[<+[<]<<.<.>.>.>>[>]<->]<]>[<+[<]<.<<.>>..>>[
>]<->]<<[[-]<]-[<]>>>+]++++++++++.[>]<[[-]<]+[-<+]-[>]<-]

$ echo 01:23456789DEADBEEF | beef clock.b 
 _         _   _       _   _   _   _   _       _   _           _   _   _  
| |   | .  _|  _| |_| |_  |_    | |_| |_|  _| |_  |_|  _| |_  |_  |_  |_  
|_|   | . |_   _|   |  _| |_|   | |_|  _| |_| |_  | | |_| |_| |_  |_  |   

This depends heavily on the platform being 8-bit.

mvds