Code Golf: Seven Segments
Asked Answered
P

26

36

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).

Pains answered 10/9, 2009 at 20:3 Comment(9)
If exec() is forbidden, is system() forbidden?Hemichordate
@Lutz, I would assume that whole family of functions would be disallowed.Devise
hex character in [a-z,A-Z]? Something is wrong.Haematic
There must be a perl one-liner to do this :-)Customary
@rangerchris, I'm surprised one hasn't popped up yet.Devise
No one-liner, but my Perl solution is now twitter-sized.Blow
what bad behavior exactly does eval allow that is worth disallowing?Awaken
Part of the problem is reading in a hex number without a 0x prefix, I don't want to encourage automatic conversions and encourage lookup tables, as most of the answers did.Pains
heh i was going to propose this same code golf soonEmelyemelyne
B
30

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.

Blow answered 10/9, 2009 at 20:3 Comment(4)
You do realize that this technically uses eval()Algor
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.Blow
No more s///e. It got shorter, actually. Made me rethink the code.Blow
I love it that the character B is used to encode the representation for A.Vinyl
D
30

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. */
}
Devise answered 10/9, 2009 at 20:3 Comment(4)
You can save 4 more characters in the args version if you remove the explicit int for r in the main declaration.Elbert
@dreamlax, Sadly, no; that is not possible. If one argument is typed in C89, all must be.Devise
@strager: You could do main(r,v)char**v;{..... which would still save you 2 characters.Elbert
@dreamlax, Ah, thanks! I'll keep that optimization in mind in the future. (I completely forgot about that "feature" of old K&R C.)Devise
P
14

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

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!

Palais answered 10/9, 2009 at 20:3 Comment(2)
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.Propagandize
The new version now runs in DOSBOX, but prints garbage.Propagandize
P
13

x86 (146 bytes; args)

Inspired by Jonas Gulle over in the Code Golf: The wave. Usually I'd be writing a 32-bit Linux ELF, but a 16-bit DOS COM is much smaller (shorter instructions, zero overhead).

46 instructions and 24 non-executed words. (They do stand out quite obviously at the end there, don't they?) Nothing tricky like reusing code as data; it probably wouldn't save more than 10 bytes anyhow.

C:\>od -xAn ss.com
 c930 82be ac00 0d3c 3e74 403c 027e 0904
 0f24 0198 bbc8 0162 c301 0eb4 078a 0424
 0474 7cb0 02eb 20b0 10cd 078a 0224 0474
 5fb0 02eb 20b0 10cd 078a 0124 0474 7cb0
 02eb 20b0 10cd bdeb f980 7420 b014 cd0d
 b010 cd0a 6610 c381 0010 0000 c180 eb10
 c3a1 0002 0202 0200 0202 0202 0002 0002
 0202 0105 0303 0607 0106 0707 0607 0304
 0606 0107 0306 0301 0107 0307 0705 0706
 0406
C:\>ss deadbeef
    _  _        _  _  _ 
 _||_ |_| _||_ |_ |_ |_ 
|_||_ | ||_||_||_ |_ |  

This just goes letter-by-letter printing the first line, then the second line, etc. using 3 bytes to store the 9 bits of data for each character. Obviously there's room left for enhancement… (This is my first time using NASM syntax; I'm more accustomed to gas but I couldn't convince it to output a raw binary.)

org 0x100

; initialize registers
    xor cl,cl

; reset ds:[si] to start of arguments
start:
    mov si,0x82

; load one character of arguments
read:
    lodsb
    cmp al,0xd
    je next

; transform [0-9A-Fa-f] to [\x00-\x0f]
    cmp al,0x40
    jle skipa
    add al,0x9
skipa:
    and al,0xf

; load font definition
    cbw
    add ax,cx
    mov bx,letters
    add bx,ax
    mov ah,0xe

; print first char
    mov al,[bx]
    and al,0x4
    jz s1
    mov al,0x7c
    jmp short w1
s1:
    mov al,0x20
w1:
    int 0x10

; print second char
    mov al,[bx]
    and al,0x2
    jz s2
    mov al,0x5f
    jmp short w2
s2:
    mov al,0x20
w2:
    int 0x10

; print third char
    mov al,[bx]
    and al,0x1
    jz s3
    mov al,0x7c
    jmp short w3
s3:
    mov al,0x20
w3:
    int 0x10

; next character
    jmp short read

; print newline
next:
    cmp cl,0x20
    je end
    mov al,0xd
    int 0x10
    mov al,0xa
    int 0x10
    add ebx,0x10
    add cl,0x10
    jmp short start

end:
    ret

letters:
    db 2,0,2,2,0,2,2,2,2,2,2,0,2,0,2,2
    db 5,1,3,3,7,6,6,1,7,7,7,6,4,3,6,6
    db 7,1,6,3,1,3,7,1,7,3,5,7,6,7,6,4
Propagandize answered 10/9, 2009 at 20:3 Comment(0)
M
6

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_)) 
Marathon answered 10/9, 2009 at 20:3 Comment(1)
Could this be shortened by using "input().lower()" in place of the third to last character?Nyala
P
6

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]]
Parka answered 10/9, 2009 at 20:3 Comment(1)
Replace the list l iterates over from [0,1,2] to [6,3,0]. Then "6-l*3" can be replaced with "l".Ferule
L
5

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)
Limen answered 10/9, 2009 at 20:3 Comment(0)
L
5

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.

Lochia answered 10/9, 2009 at 20:3 Comment(0)
R
5

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
Rebut answered 10/9, 2009 at 20:3 Comment(0)
M
4

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.

Milieu answered 10/9, 2009 at 20:3 Comment(0)
M
4

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')
Mertiemerton answered 10/9, 2009 at 20:3 Comment(4)
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.Devise
@strager I fixed the input to come from stdin.Cephalonia
@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.Servitor
sys.stdout is a file object, not a submodule. No, you cannot import sys.stdout.write.Simferopol
G
3

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:

 _     _     _  _  _  _     _     _  _  _  _  _  _     _  _     _
|_||_ |   _||_ |_ |_ |_  _||  |_ |_||_||_|  ||_ |_ |_| _| _|  || |
| ||_||_ |_||_ |  |  |_ |_||_ |_|| | _||_|  ||_| _|  | _||_   ||_|
Gambrell answered 10/9, 2009 at 20:3 Comment(1)
+! that's the approach in my mind too....respectable and very readable especially give F# which is Greek to me!Hiddenite
S
3

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)
Simferopol answered 10/9, 2009 at 20:3 Comment(0)
S
2

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.

Sportive answered 10/9, 2009 at 20:3 Comment(5)
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?Devise
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.Sportive
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. ;PDevise
@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 :)Sportive
I still took the advice of removing const from const char* and declaring the string as an array instead.Sportive
E
2

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();}}}
Ethnology answered 10/9, 2009 at 20:3 Comment(6)
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))&1<<"808123456".charAt(k)-48)!=0?" _ |_||_|".charAt(k): 32);System.out.println();}}}Scheel
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))&1<<"808123456" .charAt(k)-48)!=0?" _ |_||_|".charAt(k):32;System.out.println(s);}}} ... code golf is fun :)Symposiarch
The spec says hex characters "in both upper and lower case" may be in the input.Blow
@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)'Symposiarch
For the legal input characters, "0123456789abcdef".indexof(c|32) is equivalent to (c%32+9)%25, saving 21 bytes.Antrim
would public static int main be legal, saving one character?Shanty
M
1

Windows PowerShell, 157

$i=[char[]]"$input"
'☺ ☺☺ ☺☺☺☺☺☺ ☺ ☺☺','♠☻♥♥♦♣♣☻♦♦♦♣•♥♣♣','♦☻♣♥☻♥♦☻♦♥♠♦♣♦♣•'|%{$c=$_
""+($i|%{('···0·_·0··|0·_|0|_|0|_·0|·|0|··'-split0)[$c[("0x$_"|iex)]]})}
Map answered 10/9, 2009 at 20:3 Comment(0)
B
1

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

Braid answered 10/9, 2009 at 20:3 Comment(0)
A
1

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.

Artemisia answered 10/9, 2009 at 20:3 Comment(0)
C
1

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"));
Curator answered 10/9, 2009 at 20:3 Comment(0)
K
1

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)

Kristoforo answered 10/9, 2009 at 20:3 Comment(1)
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. :-)Rebut
B
1

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);}}
Bocage answered 10/9, 2009 at 20:3 Comment(2)
You can get rid of the =0, the cast to (int) and the variable n (just use c directly). You can move the C.Write into P and change all the n==<number>?...:... into two arrays. That way I get it down to 472 characters:Malacostracan
using C=System.Console;class Z{static void Main(){new Z();}int L;Z(){var s=C.ReadLine().ToUpper();C.Clear();foreach(var c in s){var p=(c<65?new[]{"_ ;| |;|_|","; |; |","_; _|;|_","_; _|; _|",";|_|; |","_;|_; _|","_;|_;|_|","_; |; |","_;|_|;|_|","_;|_|; _|"}[c-48]:new[]{"_;|_|;| |",";|_;|_|","_;|;|_","; _|;|_|","_;|_;|_","_;|_;|"}[c-65]).Split(';');P(0," "+p[0]);P(1,p[1]);P(2,p[2]);L+=4;}C.WriteLine();}void P(int t,string s){C.SetCursorPosition(L,t);C.Write(s);}}Malacostracan
H
1

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.

Headrest answered 10/9, 2009 at 20:3 Comment(3)
The output should be the same for all implementations, so it's not too useful to reproduce it (unless it's bugged =]).Devise
"The output should be the same for all implementations" <- Exactly why specs exist.Pains
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.Headrest
A
0

JavaScript, 309 304 bytes.

Requires that no prototype functions are added to String/Array.

function d(b){r="";o='î$º¶tÖÞ¤þöü^Ê>ÚØ'.split("");p=b.split(r);a=[" 0 ",
"132","465"];c="036";for(k in a){for(var e in p)for(var f in a[k])r+=(U=
a[k][f])==" "?" ":(y=((Q=o[parseInt(p[e],16)].charCodeAt().toString(2))
.length==6?"00"+Q:Q.length==7?0+Q:Q)[U])==0?" ":c.indexOf(U)>-1?"_":"|";
r+="\n"}return r}

Test case (formatting due to first " marking a String:

d("0123456789abcdef");

" _     _  _     _  _  _  _  _  _     _     _  _ 
| |  | _| _||_||_ |_   ||_||_||_||_ |   _||_ |_ 
|_|  ||_  _|  | _||_|  ||_| _|| ||_||_ |_||_ |  "
Acanthous answered 10/9, 2009 at 20:3 Comment(0)
D
0

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()
Decasyllable answered 10/9, 2009 at 20:3 Comment(0)
C
0

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
Candlemas answered 10/9, 2009 at 20:3 Comment(0)
B
0

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;
}
Bother answered 10/9, 2009 at 20:3 Comment(5)
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.Devise
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.Bother
It certainly compiles with gcc 3.4.4 -Wall - I just copy/pasted to check.Bother
Not on GCC 4.3 which is much more strict.Pains
Weird, you're right - even after I made R = char const* const! It compiles with g++ 4.3 though :)Bother

© 2022 - 2024 — McMap. All rights reserved.