Code Golf: Seven Segments

2019-01-30 08:21发布

问题:

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

回答1:

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.



回答2:

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. */
}


回答3:

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!



回答4:

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


回答5:

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]]


回答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_)) 


回答7:

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)


回答8:

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.



回答9:

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


回答10:

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


回答11:

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.



回答12:

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)


回答13:

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:

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


回答14:

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.



回答15:

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();}}}


回答16:

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.



回答17:

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);}}


回答18:

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)



回答19:

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"));


回答20:

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.



回答21:

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



回答22:

Windows PowerShell, 157

$i=[char[]]"$input"
'☺ ☺☺ ☺☺☺☺☺☺ ☺ ☺☺','♠☻♥♥♦♣♣☻♦♦♦♣•♥♣♣','♦☻♣♥☻♥♦☻♦♥♠♦♣♦♣•'|%{$c=$_
""+($i|%{('···0·_·0··|0·_|0|_|0|_·0|·|0|··'-split0)[$c[("0x$_"|iex)]]})}


回答23:

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;
}


回答24:

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


回答25:

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


回答26:

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");

" _     _  _     _  _  _  _  _  _     _     _  _ 
| |  | _| _||_||_ |_   ||_||_||_||_ |   _||_ |_ 
|_|  ||_  _|  | _||_|  ||_| _|| ||_||_ |_||_ |  "