17
\$\begingroup\$

Growing up, my first console game system was an Atari 2600 and I will always have a love for some of those games I so enjoyed as a child. Many of the graphics are still memorable, perhaps even iconic.

It turns out that these sprites are very simplistic bitmaps, 8 pixels wide with variable height where the binary representation is the arrangement of the pixels.

For example, the hex bytes 0x18, 0x24, 0x18 would draw a crude circle like so:

0x18: 00011000
0x24: 00100100
0x18: 00011000

As 8 pixels wide creates fairly small graphics (even by Atari 2600 standards) it was common to double or quadruple either the height, width or both to create a larger (though more blocky and distorted) version of the same image. They would commonly also be flipped vertically or horizontal for both player sprites and playfields. The game Combat is a good example of this.

The challenge is, to write code to display these sprites as "graphics" in ASCII form including the ability to stretch or flip them vertically, horizontally or both. This must be in the form of either a full program, or callable function.

Input:

  • An array of bytes, each representing the horizontal bits for that line.
  • A non-zero integer value for each direction, horizontal and vertical representing the scaling factor for that dimension.
  • A negative value indicates that the dimension should also be flipped along it's axis.

Output:

  • ASCII representation to STDOUT or a newline-separated string, using a space character for black (0) pixels and any printable, non-space character of your choice for white (1) pixels.

Test data:

bmp1 = [ 0x06, 0x0F, 0xF3, 0xFE, 0x0E, 0x04, 0x04, 0x1E, 0x3F, 0x7F, 0xE3, 0xC3, 0xC3, 0xC7, 0xFF, 0x3C, 0x08, 0x8F, 0xE1, 0x3F ]
bmp2 = [ 0x07, 0xFD, 0xA7 ]
bmp3 = [ 0x00, 0x8E, 0x84, 0xFF, 0xFF, 0x04, 0x0E, 0x00 ]
bmp4 = [ 0x00, 0xFC, 0xFC, 0x38, 0x3F, 0x38, 0xFC, 0xFC]

Note: Above example input arrays of bytes are provided as hex. If your platform does not accept hex literals for byte representation you may convert them to a native byte-equivalent literal.

Example Output:

f( bmp1, 1, 1 ) =>
--------
     XX 
    XXXX
XXXX  XX
XXXXXXX 
    XXX 
     X  
     X  
   XXXX 
  XXXXXX
 XXXXXXX
XXX   XX
XX    XX
XX    XX
XX   XXX
XXXXXXXX
  XXXX  
    X   
X   XXXX
XXX    X
  XXXXXX
--------

f( bmp1, -2, 1 ) =>
----------------
  XXXX          
XXXXXXXX        
XXXX    XXXXXXXX
  XXXXXXXXXXXXXX
  XXXXXX        
    XX          
    XX          
  XXXXXXXX      
XXXXXXXXXXXX    
XXXXXXXXXXXXXX  
XXXX      XXXXXX
XXXX        XXXX
XXXX        XXXX
XXXXXX      XXXX
XXXXXXXXXXXXXXXX
    XXXXXXXX    
      XX        
XXXXXXXX      XX
XX        XXXXXX
XXXXXXXXXXXX    
----------------

f( bmp2, 1, 2 ) =>
--------
     XXX
     XXX
XXXXXX X
XXXXXX X
X X  XXX
X X  XXX
--------

f( bmp2, 2, 1 ) =>
----------------
          XXXXXX
XXXXXXXXXXXX  XX
XX  XX    XXXXXX
----------------

f( bmp2, -2, -2 ) =>
----------------
XXXXXX    XX  XX
XXXXXX    XX  XX
XX  XXXXXXXXXXXX
XX  XXXXXXXXXXXX
XXXXXX          
XXXXXX          
----------------

f( bmp3, 1, -1 ) =>
--------

    XXX 
     X  
XXXXXXXX
XXXXXXXX
X    X  
X   XXX 

--------

f( bmp3, 3, 3 ) =>
------------------------



XXX         XXXXXXXXX   
XXX         XXXXXXXXX   
XXX         XXXXXXXXX   
XXX            XXX      
XXX            XXX      
XXX            XXX      
XXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXX
               XXX      
               XXX      
               XXX      
            XXXXXXXXX   
            XXXXXXXXX   
            XXXXXXXXX   



------------------------

f( bmp4, -1, -1 ) =>
--------
  XXXXXX
  XXXXXX
   XXX  
XXXXXX  
   XXX  
  XXXXXX
  XXXXXX

--------

f( bmp4, 4, 2 ) =>
--------------------------------


XXXXXXXXXXXXXXXXXXXXXXXX        
XXXXXXXXXXXXXXXXXXXXXXXX        
XXXXXXXXXXXXXXXXXXXXXXXX        
XXXXXXXXXXXXXXXXXXXXXXXX        
        XXXXXXXXXXXX            
        XXXXXXXXXXXX            
        XXXXXXXXXXXXXXXXXXXXXXXX
        XXXXXXXXXXXXXXXXXXXXXXXX
        XXXXXXXXXXXX            
        XXXXXXXXXXXX            
XXXXXXXXXXXXXXXXXXXXXXXX        
XXXXXXXXXXXXXXXXXXXXXXXX        
XXXXXXXXXXXXXXXXXXXXXXXX        
XXXXXXXXXXXXXXXXXXXXXXXX        
--------------------------------

Note: the horizontal lines above and below are to show the beginning and end of the output. They are not required in the output, however empty lines (represented by all zeros/spaces) at the beginning and/or end are required, as shown.

Note 2: these test bitmaps were inspired by and re-drawn/coded based on game screenshots tagged as "fair use" on Wikipedia.

Winning Criteria

  • This is , so the shortest code in bytes per language wins.
  • Standard loopholes are forbidden.
\$\endgroup\$
  • 5
    \$\begingroup\$ "Somebody get this freakin' duck away from me!" - Strong Bad \$\endgroup\$ – AdmBorkBork Jan 29 at 14:29
  • 4
    \$\begingroup\$ The irony is that even the cleverest golfing here will probably not be as clever as what programmers for the Atari 2600 actually had to do if they wanted anything more interesting than a Pong clone -- the whole screen was rendered one line at a time and the CPU spent most of its time doing that. With only 128 bytes of RAM, there was no room for a luxury like a screen buffer... The five whole sprites you got were the luxury. \$\endgroup\$ – Jeroen Mostert Jan 29 at 15:16
  • \$\begingroup\$ Can we take the input as a list of 8-bit binary-strings, or similar formats where the bytes are already unpacked into bits? \$\endgroup\$ – Luis Mendo Jan 29 at 16:09
  • \$\begingroup\$ @LuisMendo "If your platform does not accept hex literals for byte representation you may convert them to a native byte-equivalent literal." \$\endgroup\$ – Kevin Cruijssen Jan 29 at 16:09
  • \$\begingroup\$ @KevinCruijssen That's the point, I don't know what is accepted as equivalent. Does that open the door to inputting the bitmap directly? \$\endgroup\$ – Luis Mendo Jan 29 at 16:31
5
\$\begingroup\$

05AB1E, 27 26 bytes

εS²Ä×J³Äи²0‹ií]³0‹iR}˜0ð:»

Takes the input as a list of 8-bit binary-strings, and outputs with 1 as non-space character.

-1 byte thanks to @MagicOctopusUrn.

Try it online or verify all test cases.

Explanation:

ε         # Map the (implicit) input-list to:
 S        #  Convert the binary-String to a list of characters
  ²Ä      #  Take the absolute value of the second input
    ×     #  And repeat each character that many times
     J    #  And then join it back together to a single string again
 ³Ä       #  Take the absolute value of the third input
   и      #  Repeat that string as a list that many times
 ²0‹i     #  If the second input is negative:
     í    #   Reverse each string in the list
]         # Close both the if-statement and (outer) map
³0‹i }    # If the third input is negative:
    R     #  Reverse the list of lists
      ˜   # Flatten the list of lists to a list of strings
0ð:       # Replace all 0s with spaces " "
   »      # And join the strings by newlines (which is output implicitly)
\$\endgroup\$
  • \$\begingroup\$ There has to be a 2-byter for 0‹i... \$\endgroup\$ – Magic Octopus Urn Jan 29 at 16:35
  • \$\begingroup\$ @MagicOctopusUrn There should be a 1-byte for 0‹ indeed.. We do have a 1-byter for >=0, which is d. But we should also have a 1-byter to check for negative imo. Now I just use 0‹ or d_. \$\endgroup\$ – Kevin Cruijssen Jan 29 at 16:41
  • \$\begingroup\$ All I could come up with was: „íR³²‚0‹Ï.V (full code εε²Ä×}J³Äи0ð:}„íR³²‚0‹Ï.V˜») which isn't an improvement, but does get rid of one of those negative checks. \$\endgroup\$ – Magic Octopus Urn Jan 29 at 16:49
  • 1
    \$\begingroup\$ Also, pretty sure εS²Ä×J³Äи²0‹ií]³0‹iR}˜0ð:» saves a byte. If you can take in a 2D array, you can remove the S entirely for 25 bytes. \$\endgroup\$ – Magic Octopus Urn Jan 29 at 17:12
  • \$\begingroup\$ @MagicOctopusUrn Ah of course, S²Ä× instead of ε²Ä×}. Thanks! Hmm, if we are allowed to take the binary-inputs as a list of 0s and 1s an additional byte could be saved by omitting the S. Will ask OP if this is allowed. I like your „íR³²‚0‹Ï.V in your other comment as well. :) \$\endgroup\$ – Kevin Cruijssen Jan 29 at 17:19
4
\$\begingroup\$

Python 2, 117 bytes

def f(m,w,h):
 for r in m[::cmp(h,0)]:print(''.join(' X'[1<<i&r>0]*abs(w)for i in range(8)[::cmp(0,w)])+'\n')*abs(h),

Try it online!

\$\endgroup\$
3
\$\begingroup\$

MATL, 24 19 bytes

B,!i|1&Y"2M0<?XP]Zc

Inputs are an array of decimal numbers, horizontal scale, vertical scale.

Try it online!

Explanation

B        % Implicit input: array of numbers. Convert to binary. Gives a zero-one
         % matrix, each row containing the binary expansion of a number
,        % Do twice
  !      %   Transpose
  i      %   Input: number
  |      %   Absolute value
  1&Y"   %   Repeat each row that many times
  2M     %   Push the latest input again
  0<     %   Is it negative?
  ?      %   If so:
    XP   %     Flip vertically
  ]      %   End
  Zc     %   Convert each nonzero into '#'. Zeros are displayed as space
         % Implicit end. Implicit display
\$\endgroup\$
2
\$\begingroup\$

Dyalog APL, 46 42 bytes

f←{r←⍉⍵⊤⍨8/2⋄' #'[r⊣{r⊢←⌽⍣(0>⍵)⊢(|⍵)/⍉r}¨⍺]}

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Charcoal, 28 bytes

FθE↔ζ⭆⮌↨ι²×§ Xμ↔ηF›η⁰‖F‹ζ⁰‖↓

Try it online! Link is to verbose version of code. Explanation:

Fθ

Loop over the list of bytes.

E↔ζ

Map over the vertical scaling factor, thus multiplying the output lines.

⭆⮌↨ι²×§ Xμ↔η

Convert the input to base 2, reverse it, map the digits to space and X, then multiply each character by the horizontal scaling factor.

F›η⁰‖

If the horizontal scaling factor was positive, reflect to get the image the correct way around again.

F‹ζ⁰‖↓

Reflect vertically if the vertical scaling factor was negative.

\$\endgroup\$
  • \$\begingroup\$ Not that it would save any bytes, but I'm just curious: why did you use F (For) instead of ¿ (If) for the checks? \$\endgroup\$ – Kevin Cruijssen Jan 29 at 16:59
  • 1
    \$\begingroup\$ @KevinCruijssen In Charcoal succinct mode the else is implied so the only time I can use if is if it's the last statement in the block. \$\endgroup\$ – Neil Jan 29 at 18:53
  • \$\begingroup\$ Ah ok, didn't knew about that. So using two If here would actually be an If ... Else If ... instead of two loose If. Hmm, good to know. \$\endgroup\$ – Kevin Cruijssen Jan 29 at 19:00
1
\$\begingroup\$

Perl 5, 105 bytes

($_,$h,$v)=@F;say for map{$_=reverse if$h<0;y/0/ /;s/./$&x abs$h/eg;($_)x abs$v}$v<0?reverse/\d+/g:/\d+/g

TIO

If input must be hex

126 bytes

\$\endgroup\$
1
\$\begingroup\$

Jelly, 21 bytes

⁴²+BḊ€⁸m€Ṡ}¥xA}ZʋƒYo⁶

Try it online!

Assumes there is at most one command-line argument.

\$\endgroup\$
0
\$\begingroup\$

C (clang), 126 bytes

k,w,l,_;f(int*o,z,x,y){for(w=z*y;w;)for(k=w>0?z*y-w--:++w,_=l=8*x;_;putchar(_?o[k/y]>>(l>0?--l/x:7-++l/x)&1?88:46:10))_=l?:0;}

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Saved 2 bytes thanks to ceilingcat \$\endgroup\$ – AZTECCO Jan 31 at 22:51

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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