62
\$\begingroup\$

In 2014, demoscener Jakub 'Ilmenit' Debski released a 250-byte(1) procedural graphics demo for the Atari XL called Mona. It's drawing the following picture(2):

mona

Your task is to generate the exact same picture, using the language of your choice.


(1) Breakdown: 136 bytes of data + 114 bytes of code.
(2) The original picture is 128x96. The above version was magnified to 256x192. A few pixels differ from the original, but this is the expected output with the pseudo-code described in this challenge.

How?

This is . Although you're authorized to use any method, best results will most probably be achieved by using the original algorithm which is described below.

NB: This paragraph is not a specification but rather a general description. Please refer to the pseudo-code and the reference implementation for the details of the algorithm.

The image is made of 64 pseudo-random brush strokes (see this video), cycling through the following colors (in RRGGBB hexadecimal format):

COLOR = [ 0xFFE289, 0xE99E45, 0xA55A00, 0x000000 ]

The background is initially filled with the 4th color (black). Each stroke is shorter than the previous one.

The pseudo-random generator is using a Linear-Feedback Shift Register (LFSR) on a 32-bit integer initially set to 0x7EC80000 and XOR'ed with 0x04C11DB7.

Each stroke is initialized with a 16-bit value which overwrites the least significant bytes of the seed:

BRUSH = [
  0x030A, 0x37BE, 0x2F9B, 0x072B, 0x0E3C, 0xF59B, 0x8A91, 0x1B0B,
  0x0EBD, 0x9378, 0xB83E, 0xB05A, 0x70B5, 0x0280, 0xD0B1, 0x9CD2,
  0x2093, 0x209C, 0x3D11, 0x26D6, 0xDF19, 0x97F5, 0x90A3, 0xA347,
  0x8AF7, 0x0859, 0x29AD, 0xA32C, 0x7DFC, 0x0D7D, 0xD57A, 0x3051,
  0xD431, 0x542B, 0xB242, 0xB114, 0x8A96, 0x2914, 0xB0F1, 0x532C,
  0x0413, 0x0A09, 0x3EBB, 0xE916, 0x1877, 0xB8E2, 0xAC72, 0x80C7,
  0x5240, 0x8D3C, 0x3EAF, 0xAD63, 0x1E14, 0xB23D, 0x238F, 0xC07B,
  0xAF9D, 0x312E, 0x96CE, 0x25A7, 0x9E37, 0x2C44, 0x2BB9, 0x2139
];

These values are also used to set the new position (bx, by) of the brush at the beginning of the stroke: bx is given by the least significant byte and by is given by the most significant byte.

The direction of the stroke is given by bits #1 and #7 of the seed. (See the SWITCH statement in the pseudo-code.)

Pseudo-code

Below is the algorithm in pseudo-code, assuming 0-indexed arrays, where AND, OR and XOR mean bitwise operations.

seed = 0x7EC80000
dir = 0x00

FOR part = 0 TO 63
  word = BRUSH[part]
  seed = (seed AND 0xFFFF0000) OR word
  bx = word AND 0xFF
  by = (word >> 8) AND 0xFF

  FOR len = 0 TO (64 - part) * 32 - 1
    carry = seed AND 0x80000000
    seed = (seed << 1) AND 0xFFFFFFFF

    IF carry
      seed = seed XOR 0x04C11DB7
      dir = seed AND 0xFF
    ENDIF

    SWITCH dir AND 0x82
      CASE 0x00:
        by = (by + 1) AND 0x7F
      ENDCASE
      CASE 0x02:
        bx = (bx + 1) AND 0x7F
      ENDCASE
      CASE 0x80:
        by = (by - 1) AND 0x7F
      ENDCASE
      CASE 0x82:
        bx = (bx - 1) AND 0x7F
      ENDCASE
    ENDSWITCH

    drawPixel(bx, by, COLOR[part AND 3])
  ENDFOR
ENDFOR

Reference implementation

Below is an ungolfed reference implementation in JavaScript.

const SEED    = 0x7EC80000,
      XOR_MSK = 0x04C11DB7,
      COLOR   = [
        '#FFE289', '#E99E45', '#A55A00', '#000000'
      ],
      BRUSH   = [
        0x030A, 0x37BE, 0x2F9B, 0x072B, 0x0E3C, 0xF59B, 0x8A91, 0x1B0B,
        0x0EBD, 0x9378, 0xB83E, 0xB05A, 0x70B5, 0x0280, 0xD0B1, 0x9CD2,
        0x2093, 0x209C, 0x3D11, 0x26D6, 0xDF19, 0x97F5, 0x90A3, 0xA347,
        0x8AF7, 0x0859, 0x29AD, 0xA32C, 0x7DFC, 0x0D7D, 0xD57A, 0x3051,
        0xD431, 0x542B, 0xB242, 0xB114, 0x8A96, 0x2914, 0xB0F1, 0x532C,
        0x0413, 0x0A09, 0x3EBB, 0xE916, 0x1877, 0xB8E2, 0xAC72, 0x80C7,
        0x5240, 0x8D3C, 0x3EAF, 0xAD63, 0x1E14, 0xB23D, 0x238F, 0xC07B,
        0xAF9D, 0x312E, 0x96CE, 0x25A7, 0x9E37, 0x2C44, 0x2BB9, 0x2139
      ];

var ctx = document.getElementById('output').getContext('2d'),
    seed = SEED,
    bx, by, word, len, carry,
    dir = 0,
    part;

ctx.fillStyle = COLOR[3];
ctx.fillRect(0, 0, 128 * 2, 128 * 2);

for(part = 0; part < 64; part++) {
  word = BRUSH[part];
  seed = (seed & 0xffff0000) | word;
  bx = word & 0xff;
  by = (word >> 8) & 0xff;

  ctx.fillStyle = COLOR[part & 3];

  for(len = 0; len < (64 - part) * 32; len++) {
    carry = seed & 0x80000000;
    seed <<= 1;

    if(carry) {
      seed ^= XOR_MSK;
      dir = seed & 0xff;
    }

    switch(dir & 0x82) {
      case 0x00:
        by = (by + 1) & 0x7f;
        break;

      case 0x02:
        bx = (bx + 1) & 0x7f;
        break;

      case 0x80:
        by = (by - 1) & 0x7f;
        break;

      case 0x82:
        bx = (bx - 1) & 0x7f;
        break;
    }
    ctx.fillRect(bx * 2, by * 2, 2, 2);
  }
}
<canvas id="output" width=256 height=192></canvas>

You can also see an animated version here.

Clarification and rules

  • The output must be cropped to 128x96, even though the algorithm draws outside this area.
  • If your language/platform is not able to output the exact colors described above, you must use colors that are as close as possible.
  • Should you decide to use an alternate method, you still must generate the exact same output.
  • Just in case: submitting the original 6502 assembly code or any slightly edited version is not allowed.
  • Can you beat 250 bytes? Happy drawing!
\$\endgroup\$
19
  • 1
    \$\begingroup\$ The "you're encouraged to" is problematic, because if doing so conflicts with saving bytes, should someone do so or not? It should either be "you must" (do it even if it costs bytes), or "you may" (do it only if it saves bytes). \$\endgroup\$
    – user62131
    Jun 16, 2017 at 2:19
  • 1
    \$\begingroup\$ Usually I think it's better to split the ascii option into another challenge, otherwise there are kind of 2 sub-challenges anyway. \$\endgroup\$ Jun 16, 2017 at 2:33
  • 1
    \$\begingroup\$ @FryAmTheEggman It was originally meant to be graphical output only and adding this ASCII option was probably a bad idea indeed. Hopefully I'm not ruining some WIP answer by removing it... \$\endgroup\$
    – Arnauld
    Jun 16, 2017 at 2:47
  • 5
    \$\begingroup\$ @ATaco Paint Starry Nigh was a code-challenge with a different goal (reproducing the image as closely as possible). This one is plain kolmogorov-complexity with a constant output. \$\endgroup\$
    – Arnauld
    Jun 16, 2017 at 2:57
  • 1
    \$\begingroup\$ That's understandable, Redacting my close vote. \$\endgroup\$
    – ATaco
    Jun 16, 2017 at 3:10

18 Answers 18

32
\$\begingroup\$

Excel VBA 32-Bit, 1011 407 + 128 = 535 Bytes

Revision 69; ΔScore= \$-476\$ Bytes

A full VBA subroutine, and helper file ,"B", that takes no input and outputs the Mona Lisa to the ActiveSheet object on the range [A1:DX96]. This solution starts from the Previous Approach, shown below, and uses old school file relies VBA's Open and Get commands to pull the raw 16-bit values from the file, byte by byte.

This approach of using Open, Put, Get, and Close for handling raw data I/O dates back at least as far as 1981 with IBM's Disk Basic and Advanced Basic, authored by Microsoft. It is possible that this style of IO goes back even earlier, possibly being defined in 'ANSI X3.60-1978 "For Minimal BASIC"', however this standard is not freely available to the public.

This style of handling raw data I/O was inherited by many Microsoft authored BASIC dialects, including GW-BASIC, QuickBASIC, Visual Basic, and Visual Basic for Applications. Notably, this style of raw data I/O was not included in VB.Net.

Note: This solution has been restricted to 32-Bit versions of Excel VBA as ^ is the LongLong type literal in 64-Bit versions

The Code, 408 bytes

DefByte X-Y
Sub M
Cells.RowHeight=48
Cells.Interior.Color=0
s=4057*2^19
Open 1As#1Len=1
For p=1To 64
Get#1,,y
Get#1,,x
s=x+y*256&Or-4^8And s
For l=1To(65-p)*32
c=s<0
s=-c*79764919XOr(s And 2^30-1Or-(2^30And s))*2
d=130And IIf(c,s,d)
e=2And d
f=(-1)^(d>2)
x=255-e^7And.5*e*f+x
y=IIf(e,y,127And y+f)
Cells(y+1,x+1).Interior.Color=Array(0,9036543,4562665,23205)(3And p)*-(y\96+x\128=0)
Next l,p
Close#1
End Sub
Commented
DefByte X-Y             ' Define all variables starting w. `x` or `y` to be bytes
Sub M                   ' Begin a subroutine
Cells.RowHeight=48      ' Make all cells square
Cells.Interior.Color=0  ' Fill all squares with black
s=4057*2^19             ' define the seed
Open 1As#1Len=1         ' open file '1' from the current directory
For p=1To 64            ' iter over over all parts
Get#1,,y                ' grab high byte of word from file '1'; assign as seed for y 
Get#1,,x                ' grab low byte of word from file '1'; assign as seed for x
s=x+y*256&Or-4^8And s   ' update overall seed, including read seed data
For l=1To(65-p)*32      ' Iterate 32 x (65-p) times 
c=s<0                   ' Check if negative (if one of two highest bits is high)
                        ' bit shift lowest 30 bits to the left, include carry
s=-c*79764919XOr(s And 2^30-1Or-(2^30And s))*2
d=130And IIf(c,s,d)     ' extract direction bits
e=2And d                ' get lowest bit
f=(-1)^(d>2)            ' multiplier - negative if 8th lowest bit not set
x=255-e^7And.5*e*f+x    ' find x value to be updated
y=IIf(e,y,127And y+f)   ' find y value to be updated
                        ' update the found cell, if and only if x<129 and y<97
Cells(y+1,x+1).Interior.Color=Array(0,9036543,4562665,23205)(3And p)*-(y\96+x\128=0)
Next l,p                ' close both loops
Close#1                 ' close file '1'
End Sub                 ' end subroutine

The Helper File, 128 bytes

The read-only helper file, 1 used by this code it the raw bytes of the BRUSH array defined in the problem statement saved to a file. A hex dump and the VBA code used to generate the file are included below for completeness

Hex Dump
  Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 	
00000000: 03 0A 37 BE 2F 9B 07 2B 0E 3C F5 9B 8A 91 1B 0B    ..7>/..+.<u.....
00000010: 0E BD 93 78 B8 3E B0 5A 70 B5 02 80 D0 B1 9C D2    .=.x8>0Zp5..P1.R
00000020: 20 93 20 9C 3D 11 26 D6 DF 19 97 F5 90 A3 A3 47    ....=.&V_..u.##G
00000030: 8A F7 08 59 29 AD A3 2C 7D FC 0D 7D D5 7A 30 51    .w.Y)-#,}|.}Uz0Q
00000040: D4 31 54 2B B2 42 B1 14 8A 96 29 14 B0 F1 53 2C    T1T+2B1...).0qS,
00000050: 04 13 0A 09 3E BB E9 16 18 77 B8 E2 AC 72 80 C7    ....>;i..w8b,r.G
00000060: 52 40 8D 3C 3E AF AD 63 1E 14 B2 3D 23 8F C0 7B    R@.<>/-c..2=#.@{
00000070: AF 9D 31 2E 96 CE 25 A7 9E 37 2C 44 2B B9 21 39    /.1..N%'.7,D+9!9
Generation Code
Private Sub PutBytes()

    ' Declare vars to be used in making the helper file
    Dim Brush               As Variant, _
        HighByte            As Byte, _
        LowByte             As Byte, _
        iter                As Byte
    
    ' open helper file "B" in the current directory
    '   current directory may be queried using `?CurDir`
    Open "B" For Binary As #1
       
    ' store the array of unsigned 2-byte ints from the original
    Let Brush = Array( _
            778, 14270, 12187, 1835, 3644, 62875, 35473, 6923, _
            3773, 37752, 47166, 45146, 28853, 640, 53425, 40146, _
            8339, 8348, 15633, 9942, 57113, 38901, 37027, 41799, _
            35575, 2137, 10669, 41772, 32252, 3453, 54650, 12369, _
            54321, 21547, 45634, 45332, 35478, 10516, 45297, 21292, _
            1043, 2569, 16059, 59670, 6263, 47330, 44146, 32967, _
            21056, 36156, 16047, 44387, 7700, 45629, 9103, 49275, _
            44957, 12590, 38606, 9639, 40503, 11332, 11193, 8505)
    
    ' iterate across the Brush values
    For iter = 0 To 63
        
        ' Split the 16-Bit Brush val into its high and low byte
        Let HighByte = Brush(iter) \ 256
        Let LowByte = Brush(iter) Mod 256
         
        ' Put the bytes into the the open file
        Put #1, , HighByte
        Put #1, , LowByte
        
    Next iter
    
    'close the file
    Close #1
    
End Sub

Output

Gif showing output to the ActiveSheet when M is called in the VBE immediate window. Note that due to file size limitations this gif has fewer frames than actually produced. Mona

Previous Approach, 618 Bytes

An immediate window function that takes no input and outputs the Mona Lisa to the ActiveSheet object on the range [A1:DX96].

There was a lot of black magic involved in golfing this down to its current state - some of the tricks involved are pixel art prep, bit shifting colors implicit type conversion, and base64 compression compressing bytes as a String.

Cells.RowHeight=48:Cells.Interior.Color=0:s=4057*2^19:k=256:For p=1To 64:w=k*Asc(Mid(";3ùŽ—¼´tÔ $$A*ã›”§Ž-§Ù4ØX¶µŽ-´WBí¼°„V‘B±""¶'ij5š)¢0/%",p))+Asc(Mid("»˜(9˜Žºu;W²}®Ï™Óò DôVª)ùzwN.(?“î)¸tßoÄ=9¬`:Œxš+ˤ4A¶6",p))-1021:s=s And-4^8Or w:x=w mod k:y=w\k mod k:For l=1To(65-p)*32:c=s And-2^31:s=2*(s And 2^30-1Or-(2^30And s)):s=IIf(c,79764919Xor s,s):d=IIf(c,s mod k,d)And 130:e=2And d:f=(-1)^(d>2):x=255-e^7And.5*e*f+x:y=IIf(e,y,127And y+f):Cells(y+1,x+1).Interior.Color=Array(0,9036543,4562665,23205)(3And p)*-(y\96+x\128=0):Next l,p

Slightly More Readably Formatted

Line continuation characters (:) are replaced with newline literals for readability

Cells.RowHeight=48
Cells.Interior.Color=0
s=4057*2^19
k=256
For p=1To 64
w=k*Asc(Mid(";3ùŽ—¼´tÔ $$A*ã›”§Ž-§Ù4ØX¶µŽ-´WBí¼°„V‘B±""¶'ij5š)¢0/%",p))+Asc(Mid("»˜(9˜Žºu;W²}®Ï™Óò DôVª)ùzwN.(?“î)¸tßoÄ=9¬`:Œxš+ˤ4A¶6",p))-1021
s=s And-4^8Or w
x=w mod k
y=w\k mod k
For l=1To(65-p)*32
c=s And-2^31
s=2*(s And 2^30-1Or-(2^30And s))
s=IIf(c,79764919Xor s,s)
d=IIf(c,s mod k,d)And 130
e=2And d
f=(-1)^(d>2)
x=255-e^7And.5*e*f+x
y=IIf(e,y,127And y+f)
Cells(y+1,x+1).Interior.Color=Array(0,9036543,4562665,23205)(3And p)*-(y\96+x\128=0)
Next l,p

Ungolfed

Ungolfed full subroutine that takes no input and produces the mona lisa using the method described above on the ActiveSheet object

Option Private Module
Option Compare Text
Option Explicit
Option Base 0


Public Sub MonaLisa()
    On Error GoTo 0

    Dim part As Integer, _
        length As Integer, _
        M As Long, _
        seed As Long, _
        dir As Long, _
        word As Long, _
        carry As Long, _
        bx As Byte, _
        by As Byte, _
        BRUSH, _
        COLOR

    Let COLOR = Array(&H89E2FF, &H459EE9, &H5AA5, 0)
    Let BRUSH = Array( _
            778, 14270, 12187, 1835, 3644, 62875, 35473, 6923, _
            3773, 37752, 47166, 45146, 28853, 640, 53425, 40146, _
            8339, 8348, 15633, 9942, 57113, 38901, 37027, 41799, _
            35575, 2137, 10669, 41772, 32252, 3453, 54650, 12369, _
            54321, 21547, 45634, 45332, 35478, 10516, 45297, 21292, _
            1043, 2569, 16059, 59670, 6263, 47330, 44146, 32967, _
            21056, 36156, 16047, 44387, 7700, 45629, 9103, 49275, _
            44957, 12590, 38606, 9639, 40503, 11332, 11193, 8505)

    Let dir = 0
    Let carry = 0
    Let seed = &H7EC80000

    Let Cells.Interior.Color = 0
    Let Cells.ColumnWidth = 2
    
    Call Range("A1:DX96").Select
    Let ActiveWindow.Zoom = True
    Call Range("A1").Select

    For part = 0 To 63 Step 1

        Call VBA.DoEvents

        Let word = BRUSH(part)
        Let seed = (seed And &HFFFF0000) Or word

        Let bx = word And 255
        Let by = Int(word / (2 ^ 8)) And 255

        For length = 0 To (64 - part) * 32 - 1 Step 1

            Let carry = seed And &H80000000
            Let M = seed And &H40000000
            Let seed = (seed And &H3FFFFFFF) * 2
            If M <> 0 Then Let seed = seed Or &H80000000

            Let seed = seed And &HFFFFFFFF

            If carry Then
                Let seed = seed Xor 79764919
                Let dir = Int(seed And 255)
            End If

            Select Case dir And 130
                Case 0:   Let by = Int(by + 1) And 127
                Case 2:   Let bx = Int(bx + 1) And 127
                Case 128: Let by = Int(by - 1) And 127
                Case 130: Let bx = Int(bx - 1) And 127
            End Select

            If bx<128 And by<96 Then
                Let Cells(by + 1, bx + 1).Interior.Color = COLOR(part And 3)
            End If
        Next length
    Next part
    
End Sub
\$\endgroup\$
4
  • 6
    \$\begingroup\$ Hey, nice animation! \$\endgroup\$
    – Arnauld
    Jun 20, 2017 at 20:27
  • \$\begingroup\$ @Arnauld, thanks :P it took more work than it should have \$\endgroup\$ Jun 21, 2017 at 0:45
  • 1
    \$\begingroup\$ You MUST be kidding me oh my god \$\endgroup\$
    – Razetime
    Aug 24, 2020 at 17:11
  • 3
    \$\begingroup\$ Reminds me of the old Pipes screensaver from XP \$\endgroup\$
    – EasyasPi
    Jun 8, 2021 at 17:24
26
\$\begingroup\$

8086 Assembly - NASM (MBR) - 248 245 bytes

[org 0x7C00]
[bits 16]
    push 0xA000
    pop es
    mov si, $brush
    xor cx, cx

    mov ax, 0x0013
    int 0x10

    mov ebx, 0x7EC80000

part_loop:
    lodsw
    mov bx, ax
    
    mov bp, 64
    sub bp, cx
    shl bp, 5
    mov sp, bp
    
len_loop:
    shl ebx, 1
    jnc not_carry
    xor ebx, 0x04C11DB7
    mov dh, bl
    not_carry:

    and dh, 0x82
    je dir_00
    jpe dir_82
    js dir_80
    dir_02:
        inc al  
        jmp dir_end
    dir_82:
        dec al
        jmp dir_end
    dir_00:
        inc ah
        jmp dir_end 
    dir_80:
        dec ah  
    dir_end:
    
    and ax, 0x7F7F
    cmp ah, 96
    jae skip

    movzx di, ah
    movzx bp, al
    imul di, 320
    add di, bp
    mov bp, cx
    and bp, 3
    mov dl, byte[bp + color]
    mov [es:di], dl
    skip:
    
    dec sp
    jnz len_loop
    inc cx
    cmp cx, 64
    jl part_loop    
    jmp $
  
color:
    db 0x43, 0x42, 0x06, 0x00
    
brush:
    dw  0x030A, 0x37BE, 0x2F9B, 0x072B, 0x0E3C, 0xF59B, 0x8A91, 0x1B0B
    dw  0x0EBD, 0x9378, 0xB83E, 0xB05A, 0x70B5, 0x0280, 0xD0B1, 0x9CD2
    dw  0x2093, 0x209C, 0x3D11, 0x26D6, 0xDF19, 0x97F5, 0x90A3, 0xA347
    dw  0x8AF7, 0x0859, 0x29AD, 0xA32C, 0x7DFC, 0x0D7D, 0xD57A, 0x3051
    dw  0xD431, 0x542B, 0xB242, 0xB114, 0x8A96, 0x2914, 0xB0F1, 0x532C
    dw  0x0413, 0x0A09, 0x3EBB, 0xE916, 0x1877, 0xB8E2, 0xAC72, 0x80C7
    dw  0x5240, 0x8D3C, 0x3EAF, 0xAD63, 0x1E14, 0xB23D, 0x238F, 0xC07B
    dw  0xAF9D, 0x312E, 0x96CE, 0x25A7, 0x9E37, 0x2C44, 0x2BB9, 0x2139
    

times 510 - ($-$$) db 0
DB 0x55
DB 0xAA

mona.jpg

\$\endgroup\$
7
  • 6
    \$\begingroup\$ Oh hey, an answer that beats the score in the question (assuming an accurate byte count). Nice! \$\endgroup\$ Jun 21, 2017 at 20:48
  • \$\begingroup\$ Welcome to PPCG! wonderful first post. \$\endgroup\$ Jun 22, 2017 at 18:50
  • \$\begingroup\$ You can reduce to 245 bytes if you remove useless xor dx,dx and change mov bp,0xa000; mov es,bp to push 0xa000; pop es. \$\endgroup\$
    – Ruslan
    Jun 23, 2017 at 12:43
  • 1
    \$\begingroup\$ It's not 8086. 8086 has no PUSH imm \$\endgroup\$
    – l4m2
    Dec 28, 2017 at 16:15
  • 1
    \$\begingroup\$ Basic optimize to 240 paste.ubuntu.com/26272648 \$\endgroup\$
    – l4m2
    Dec 28, 2017 at 16:52
17
\$\begingroup\$

x86 opcode, 227 224 223 Bytes

0000h: 68 20 A8 07 B8 13 00 CD 10 66 BF 40 00 C8 7E 5A
0010h: 89 FD BE 5B 01 AD 89 C7 89 E9 C1 E1 05 66 D1 E7
0020h: 73 09 66 81 F7 B7 1D C1 04 89 FA 80 E2 82 74 09
0030h: 7A 04 78 08 40 40 05 7F 7F 80 C4 02 FE CC 25 7F
0040h: 7F 89 EB 83 E3 03 8A B7 DB 01 88 E3 6B DB 40 01
0050h: C3 26 88 37 E2 C7 4D 75 BC EB FE 0A 03 BE 37 9B
0060h: 2F 2B 07 3C 0E 9B F5 91 8A 0B 1B BD 0E 78 93 3E
0070h: B8 5A B0 B5 70 80 02 B1 D0 D2 9C 93 20 9C 20 11
0080h: 3D D6 26 19 DF F5 97 A3 90 47 A3 F7 8A 59 08 AD
0090h: 29 2C A3 FC 7D 7D 0D 7A D5 51 30 31 D4 2B 54 42
00a0h: B2 14 B1 96 8A 14 29 F1 B0 2C 53 13 04 09 0A BB
00b0h: 3E 16 E9 77 18 E2 B8 72 AC C7 80 40 52 3C 8D AF
00c0h: 3E 63 AD 14 1E 3D B2 8F 23 7B C0 9D AF 2E 31 CE
00d0h: 96 A7 25 37 9E 44 2C B9 2B 39 21 43 00 06 42   

  0100 6820A8                 push A820
  0103 07                     pop es
  0104 B81300                 mov ax, 0013
  0107 CD10                   int 10
  0109 66BF4000C87E           mov edi, 7EC80040
  010F 5A                     pop dx
  0110 89FD                   mov bp, di
  0112 BE5B01                 mov si, 015B
  0115 AD                     lodsw
  0116 89C7                   mov di, ax
  0118 89E9                   mov cx, bp
  011A C1E105                 shl cx, 05
  011D 66D1E7                 shl edi, 01
  0120 7309                   jnb 012B
  0122 6681F7B71DC104         xor edi, 04C11DB7
  0129 89FA                   mov dx, di
  012B 80E282                 and dl, 82
  012E 7409                   je 0139
  0130 7A04                   jpe 0136
  0132 7808                   js 013C
  0134 40                     inc ax
  0135 40                     inc ax
  0136 057F7F                 add ax, 7F7F
  0139 80C402                 add ah, 02
  013C FECC                   dec ah
  013E 257F7F                 and ax, 7F7F
  0141 89EB                   mov bx, bp
  0143 83E303                 and bx, 0003
  0146 8AB7DB01               mov dh, [bx+01DB]
  014A 88E3                   mov bl , ah
  014C 6BDB40                 imul bx, 0040
  014F 01C3                   add bx, ax
  0151 268837                 mov es:[bx], dh
  0154 E2C7                   loop 011D
  0156 4D                     dec bp
  0157 75BC                   jne 0115
  0159 EBFE                   jmp 0159
  015B 0A03BE37...3921        brush_dw
  01DB 43000642               color_db

Image: enter image description here

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Would you mind including an explanation of this and a screenshot of the output - as it is it is not self explanatory \$\endgroup\$ Dec 28, 2017 at 19:42
  • \$\begingroup\$ Should I use a camera or a virtual environment, as I run it on full screen and no screen capture anymore \$\endgroup\$
    – l4m2
    Dec 29, 2017 at 3:20
  • 1
    \$\begingroup\$ Image: imagebin.ca/v/3mJb1bFSlGRY \$\endgroup\$
    – l4m2
    Dec 29, 2017 at 3:34
  • 1
    \$\begingroup\$ 0122 6681F7B71DC104 xor edi, 00001DB7 bug found in W32Dasm \$\endgroup\$
    – l4m2
    Mar 9, 2018 at 17:03
14
\$\begingroup\$

HTML + CSS + JavaScript (ES6), 499 bytes

  • HTML: 33 bytes
  • CSS: 17 bytes
  • JS: 678 ... 478 475 473 465 459 455 451 447 449 bytes

It's nowhere near 250 bytes, but I'll definitely settle for under 500 bytes! Huge thanks to @Arnauld and @Firefly for helping me golf this monster down.

with(C.getContext`2d`)for(s=4057<<19,d=n=65;n--;)for(w=n-44?` ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,s=s&~65535|w,x=w&255,y=w>>8,fillStyle="#"+["FFE289","000","A55A00","E99E45"][n&3],k=32*n;k--;fillRect(x,y,1,1))(s*=2)/2>>31&&(d=s^=79764919),D=d&128?-1:1,d&2?x=x+D&127:y=y+D&127
*{background:#000
<canvas id=C width=128 height=96>

For a bigger scale, replace the CSS with the following:

canvas { background: #000; image-rendering: pixelated; zoom: 3 }

Annotated History!

I had a blast golfing Arnauld's reference code, and you can get some of that here. Enjoy!

// One pass through Closure Compiler ADVANCED mode
// Added with statement, golfed switch statement, golfed color array
// I was surprised Closure Compiler didn't touch the switch statement, like, at least convert it into a bunch of conditional statements.
with(C.getContext("2d")){e=2127036416;m=0;fillRect(0,0,256,256);for(n=0;n<64;n++)for(h=[778,14270,12187,1835,3644,62875,35473,6923,3773,37752,47166,45146,28853,640,53425,40146,8339,8348,15633,9942,57113,38901,37027,41799,35575,2137,10669,41772,32252,3453,54650,12369,54321,21547,45634,45332,35478,10516,45297,21292,1043,2569,16059,59670,6263,47330,44146,32967,21056,36156,16047,44387,7700,45629,9103,49275,44957,12590,38606,9639,40503,11332,11193,8505][n],e=e&4294901760|h,p=[h>>8&255,,h&255],fillStyle="#"+["FFE289","E99E45","A55A00","000"][n&3],k=0;k<32*(64-n);k++)l=e&2147483648,e<<=1,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?p[i]--:p[i]++,fillRect(p[2]&127,p[0]&127,1,1);}

// Background moved to CSS
with(C.getContext("2d")){e=2127036416;m=0;for(n=0;n<64;n++)for(h=[778,14270,12187,1835,3644,62875,35473,6923,3773,37752,47166,45146,28853,640,53425,40146,8339,8348,15633,9942,57113,38901,37027,41799,35575,2137,10669,41772,32252,3453,54650,12369,54321,21547,45634,45332,35478,10516,45297,21292,1043,2569,16059,59670,6263,47330,44146,32967,21056,36156,16047,44387,7700,45629,9103,49275,44957,12590,38606,9639,40503,11332,11193,8505][n],e=e&4294901760|h,p=[h>>8&255,,h&255],fillStyle="#"+["FFE289","E99E45","A55A00","000"][n&3],k=0;k<32*(64-n);k++)l=e&2147483648,e<<=1,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?p[i]--:p[i]++,fillRect(p[2]&127,p[0]&127,1,1);}

// e<<=1 same as e*=2
with(C.getContext("2d")){e=2127036416;m=0;for(n=0;n<64;n++)for(h=[778,14270,12187,1835,3644,62875,35473,6923,3773,37752,47166,45146,28853,640,53425,40146,8339,8348,15633,9942,57113,38901,37027,41799,35575,2137,10669,41772,32252,3453,54650,12369,54321,21547,45634,45332,35478,10516,45297,21292,1043,2569,16059,59670,6263,47330,44146,32967,21056,36156,16047,44387,7700,45629,9103,49275,44957,12590,38606,9639,40503,11332,11193,8505][n],e=e&4294901760|h,p=[h>>8&255,,h&255],fillStyle="#"+["FFE289","E99E45","A55A00","000"][n&3],k=0;k<32*(64-n);k++)l=e&2147483648,e*=2,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?p[i]--:p[i]++,fillRect(p[2]&127,p[0]&127,1,1);}

// Semicolon
with(C.getContext("2d")){e=2127036416;m=0;for(n=0;n<64;n++)for(h=[778,14270,12187,1835,3644,62875,35473,6923,3773,37752,47166,45146,28853,640,53425,40146,8339,8348,15633,9942,57113,38901,37027,41799,35575,2137,10669,41772,32252,3453,54650,12369,54321,21547,45634,45332,35478,10516,45297,21292,1043,2569,16059,59670,6263,47330,44146,32967,21056,36156,16047,44387,7700,45629,9103,49275,44957,12590,38606,9639,40503,11332,11193,8505][n],e=e&4294901760|h,p=[h>>8&255,,h&255],fillStyle="#"+["FFE289","E99E45","A55A00","000"][n&3],k=0;k<32*(64-n);k++)l=e&2147483648,e*=2,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?p[i]--:p[i]++,fillRect(p[2]&127,p[0]&127,1,1)}

// Failed to golf those colors down ;-;
a=n=>["FFE289","E99E45","A55A00","000"][n&3]
b=n=>"FFE289E99E45A55A00000".substr(n%4*6,6)
c=n=>btoa`Q6óÑ=ôN9@ÓM4ÓM4`.substr(n%4*6,6)  // <-- there are 2 unprintables somewhere in there
A = [0,1,2,3]
console.log(A.map(n=>a(n)))
console.log(A.map(n=>b(n)))
console.log(A.map(n=>c(n)))
d=n=>`FFE289 E99E45 A55A00 000`.split` `[n&3]


// Let's compress that hunky array of numbers...
[778,14270,12187,1835,3644,62875,35473,6923,3773,37752,47166,45146,28853,640,53425,40146,8339,8348,15633,9942,57113,38901,37027,41799,35575,2137,10669,41772,32252,3453,54650,12369,54321,21547,45634,45332,35478,10516,45297,21292,1043,2569,16059,59670,6263,47330,44146,32967,21056,36156,16047,44387,7700,45629,9103,49275,44957,12590,38606,9639,40503,11332,11193,8505].map(n=>String.fromCharCode(n)).join``
`̊㞾⾛ܫ฼誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖�韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ`.charCodeAt(n)

// And the char at n=20 became 65533 instead of 57113??
with(C.getContext("2d")){e=2127036416;m=0;for(n=0;n<64;n++)for(h=`̊㞾⾛ܫ฼誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖�韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ`.charCodeAt(n),e=e&4294901760|h,p=[h>>8&255,,h&255],fillStyle="#"+["FFE289","E99E45","A55A00","000"][n&3],k=0;k<32*(64-n);k++)l=e&2147483648,e*=2,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?p[i]--:p[i]++,fillRect(p[2]&127,p[0]&127,1,1)}

// Maybe try replacing that char with \u000? (Nope, editor doesn't like that at all)
with(C.getContext("2d")){e=2127036416;m=0;for(n=0;n<64;n++)for(h=`̊㞾⾛ܫ฼誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ`.charCodeAt(n)||57113,e=e&4294901760|h,p=[h>>8&255,,h&255],fillStyle="#"+["FFE289","E99E45","A55A00","000"][n&3],k=0;k<32*(64-n);k++)l=e&2147483648,e*=2,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?p[i]--:p[i]++,fillRect(p[2]&127,p[0]&127,1,1)}

// Okay, just check if n is 20 and make an exception.
with(C.getContext("2d")){e=2127036416;m=0;for(n=0;n<64;n++)for(h=n-20?`̊㞾⾛ܫ฼誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖�韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ`.charCodeAt(n):57113,e=e&4294901760|h,p=[h>>8&255,,h&255],fillStyle="#"+["FFE289","E99E45","A55A00","000"][n&3],k=0;k<32*(64-n);k++)l=e&2147483648,e*=2,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?p[i]--:p[i]++,fillRect(p[2]&127,p[0]&127,1,1)}

// Also, this could work too... (how to do byte comparison properly? Not so knowledgeable about encodings)
+('0x'+btoa`Ó}ß°DØ_AÓ½ÐMÂAðuÔÐ@C÷~üÍÄN@ï@yÓo4@uô öÛOwÛOBÜ=uÛ ú]}÷±y÷@7~;ð{ÓÎ}ÛÐ}ì1BÐ>ÃÀßNuõçn6]xðzÛÝxAuç}ÓwÐ
=Ü@AÝz×ÎûÁ6.öó@»çn4ð=ÂÜ@>·ÔMxmÃÛNÁ_Cß]÷ Û;ôMûØ.8Ø}Û]ý`.substr(n*4,4))


// ES6 template string syntax, easy-peasy.
with(C.getContext`2d`){e=2127036416;m=0;for(n=0;n<64;n++)for(h=n-20?`̊㞾⾛ܫ฼誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖�韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ`.charCodeAt(n):57113,e=e&4294901760|h,p=[h>>8&255,,h&255],fillStyle="#"+["FFE289","E99E45","A55A00","000"][n&3],k=0;k<32*(64-n);k++)l=e&2147483648,e*=2,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?p[i]--:p[i]++,fillRect(p[2]&127,p[0]&127,1,1)}

// 0x80000000 = 2147483648 = 2**31 = 1<<31
// 0x4C11DB7 = 2127036416 = 4057<<19
// 0xffff0000 = 4294901760
with(C.getContext`2d`){e=4057<<19;m=0;for(n=0;n<64;n++)for(h=n-20?`̊㞾⾛ܫ฼誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖�韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ`.charCodeAt(n):57113,e=e&0xffff0000|h,p=[h>>8&255,,h&255],fillStyle="#"+["FFE289","E99E45","A55A00","000"][n&3],k=0;k<32*(64-n);k++)l=e&1<<31,e*=2,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?p[i]--:p[i]++,fillRect(p[2]&127,p[0]&127,1,1)}

// Turns out individual x and y vars were better
with(C.getContext`2d`){e=4057<<19;m=0;for(n=0;n<64;n++)for(h=n-20?`̊㞾⾛ܫ฼誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖�韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ`.charCodeAt(n):57113,e=e&0xffff0000|h,x=h&255,y=h>>8&255,fillStyle="#"+["FFE289","E99E45","A55A00","000"][n&3],k=0;k<32*(64-n);k++)l=e&1<<31,e*=2,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?i?x--:y--:i?x++:y++,fillRect(x&127,y&127,1,1)}

// Reverse loop k
with(C.getContext`2d`){e=4057<<19;m=0;for(n=0;n<64;n++)for(h=n-20?`̊㞾⾛ܫ฼誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖�韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ`.charCodeAt(n):57113,e=e&0xffff0000|h,x=h&255,y=h>>8&255,fillStyle="#"+["FFE289","E99E45","A55A00","000"][n&3],k=32*(64-n);k--;)l=e&1<<31,e*=2,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?i?x--:y--:i?x++:y++,fillRect(x&127,y&127,1,1)}

// Reverse loop n (wow!)
// Side effects: string is reversed, 20th character becomes 43rd character, color array is reversed, and we must add 1 to n when used in the k loop
with(C.getContext`2d`){e=4057<<19;m=0;for(n=64;n--;)for(h=n-43?`ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,e=e&0xffff0000|h,x=h&255,y=h>>8&255,fillStyle="#"+["000","A55A00","E99E45","FFE289"][n&3],k=32*-~n;k--;)l=e&1<<31,e*=2,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?i?x--:y--:i?x++:y++,fillRect(x&127,y&127,1,1)}

// Everything in the for loop to eliminate "with" brackets
with(C.getContext`2d`)for(e=4057<<19,m=0,n=64;n--;)for(h=n-43?`ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,e=e&0xffff0000|h,x=h&255,y=h>>8&255,fillStyle="#"+["000","A55A00","E99E45","FFE289"][n&3],k=32*-~n;k--;)l=e&1<<31,e*=2,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?i?x--:y--:i?x++:y++,fillRect(x&127,y&127,1,1)

// Use third argument of "for"
with(C.getContext`2d`)for(e=4057<<19,m=0,n=64;n--;)for(h=n-43?`ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,e=e&0xffff0000|h,x=h&255,y=h>>8&255,fillStyle="#"+["000","A55A00","E99E45","FFE289"][n&3],k=32*-~n;k--;fillRect(x&127,y&127,1,1))l=e&1<<31,e*=2,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?i?x--:y--:i?x++:y++

// Iterate 65 to 1 instead of 64 to 0; removes need to add 1 with -~ at cost of increasing string length by 1
with(C.getContext`2d`)for(e=4057<<19,m=0,n=65;n--;)for(h=n-44?` ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,e=e&0xffff0000|h,x=h&255,y=h>>8&255,fillStyle="#"+["FFE289","000","A55A00","E99E45"][n&3],k=32*n;k--;fillRect(x&127,y&127,1,1))l=e&1<<31,e*=2,l&&(e^=79764919,m=e&255),j=m&130,i=j%4,2<j?i?x--:y--:i?x++:y++

// Rename variables to be more aligned with original names
// s = seed, d = direction, w = word
with(C.getContext`2d`)for(s=4057<<19,d=0,n=65;n--;)for(w=n-44?` ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,s=s&0xffff0000|w,x=w&255,y=w>>8&255,fillStyle="#"+["FFE289","000","A55A00","E99E45"][n&3],k=32*n;k--;fillRect(x&127,y&127,1,1))l=s&1<<31,s*=2,l&&(s^=79764919,d=s&255),j=d&130,i=j%4,2<j?i?x--:y--:i?x++:y++

// s&0xffff0000|w same as s>>16<<16|w (@Arnauld)
with(C.getContext`2d`)for(s=4057<<19,d=0,n=65;n--;)for(w=n-44?` ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,s=s>>16<<16|w,x=w&255,y=w>>8&255,fillStyle="#"+["FFE289","000","A55A00","E99E45"][n&3],k=32*n;k--;fillRect(x&127,y&127,1,1))l=s&1<<31,s*=2,l&&(s^=79764919,d=s&255),j=d&130,i=j%4,2<j?i?x--:y--:i?x++:y++

// d can be initialized to 65 (@Arnauld)
with(C.getContext`2d`)for(s=4057<<19,d=n=65;n--;)for(w=n-44?` ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,s=s>>16<<16|w,x=w&255,y=w>>8&255,fillStyle="#"+["FFE289","000","A55A00","E99E45"][n&3],k=32*n;k--;fillRect(x&127,y&127,1,1))l=s&1<<31,s*=2,l&&(s^=79764919,d=s&255),j=d&130,i=j%4,2<j?i?x--:y--:i?x++:y++

// Much shorter way to calculate direction (@Arnauld)
// j=d&130,i=j%4,2<j?i?x--:y--:i?x++:y++
// d&128?d&2?x--:y--:d&2?x++:y++
with(C.getContext`2d`)for(s=4057<<19,d=n=65;n--;)for(w=n-44?` ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,s=s>>16<<16|w,x=w&255,y=w>>8&255,fillStyle="#"+["FFE289","000","A55A00","E99E45"][n&3],k=32*n;k--;fillRect(x&127,y&127,1,1))l=s&1<<31,s*=2,l&&(s^=79764919,d=s&255),d&128?d&2?x--:y--:d&2?x++:y++

// "&255" of "d=s&255" isn't necessary now (@Arnauld)
// s^=79764919,d=s&255
// d=s^=79764919
with(C.getContext`2d`)for(s=4057<<19,d=n=65;n--;)for(w=n-44?` ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,s=s>>16<<16|w,x=w&255,y=w>>8&255,fillStyle="#"+["FFE289","000","A55A00","E99E45"][n&3],k=32*n;k--;fillRect(x&127,y&127,1,1))l=s&1<<31,s*=2,l&&(d=s^=79764919),d&128?d&2?x--:y--:d&2?x++:y++

// s>>16<<16 same as s&~65535 (@FireFly)
with(C.getContext`2d`)for(s=4057<<19,d=n=65;n--;)for(w=n-44?` ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,s=s&~65535|w,x=w&255,y=w>>8&255,fillStyle="#"+["FFE289","000","A55A00","E99E45"][n&3],k=32*n;k--;fillRect(x&127,y&127,1,1))l=s&1<<31,s*=2,l&&(d=s^=79764919),d&128?d&2?x--:y--:d&2?x++:y++

// Even shorter way to calculate direction (@FireFly)
// d&128?d&2?x--:y--:d&2?x++:y++
// D=d&128?-1:1,d&2?x+=D:y+=D
with(C.getContext`2d`)for(s=4057<<19,d=n=65;n--;)for(w=n-44?` ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,s=s&~65535|w,x=w&255,y=w>>8&255,fillStyle="#"+["FFE289","000","A55A00","E99E45"][n&3],k=32*n;k--;fillRect(x&127,y&127,1,1))l=s&1<<31,s*=2,l&&(d=s^=79764919),D=d&128?-1:1,d&2?x+=D:y+=D

// Get rid of l (@Arnauld)
// l=s&1<<31,s*=2,l&&
// (s*=2)/2>>31&&
with(C.getContext`2d`)for(s=4057<<19,d=n=65;n--;)for(w=n-44?` ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,s=s&~65535|w,x=w&255,y=w>>8&255,fillStyle="#"+["FFE289","000","A55A00","E99E45"][n&3],k=32*n;k--;fillRect(x&127,y&127,1,1))(s*=2)/2>>31&&(d=s^=79764919),D=d&128?-1:1,d&2?x+=D:y+=D

// "&255" isn't necessary y=w>>8&255 (@Arnauld)
with(C.getContext`2d`)for(s=4057<<19,d=n=65;n--;)for(w=n-44?` ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,s=s&~65535|w,x=w&255,y=w>>8,fillStyle="#"+["FFE289","000","A55A00","E99E45"][n&3],k=32*n;k--;fillRect(x&127,y&127,1,1))(s*=2)/2>>31&&(d=s^=79764919),D=d&128?-1:1,d&2?x+=D:y+=D

// x and y are constrained within 255 only upon direction change, making the drawing invalid; constraining every time fixes it. (@Arnauld)
// x+=D:y+=D
// fillRect(x&127,y&127,1,1)
// x=x+D&127:y=y+D&127
// fillRect(x,y,1,1)
with(C.getContext`2d`)for(s=4057<<19,d=n=65;n--;)for(w=n-44?` ℹ⮹ⱄ鸷▧雎ㄮ꾝쁻⎏눽Ḕ굣㺯贼剀胇걲룢ᡷ㺻ਉГ匬냱⤔誖넔뉂含퐱け핺ൽ緼ꌬ⦭࡙諷ꍇ那韵�⛖㴑ₜₓ鳒킱ʀ炵끚렾鍸ຽᬋ誑฼ܫ⾛㞾̊`.charCodeAt(n):57113,s=s&~65535|w,x=w&255,y=w>>8,fillStyle="#"+["FFE289","000","A55A00","E99E45"][n&3],k=32*n;k--;fillRect(x,y,1,1))(s*=2)/2>>31&&(d=s^=79764919),D=d&128?-1:1,d&2?x=x+D&127:y=y+D&127

\$\endgroup\$
3
  • 3
    \$\begingroup\$ Nice job and thanks for sharing the history! \$\endgroup\$
    – Arnauld
    Jun 16, 2017 at 14:49
  • \$\begingroup\$ @Arnauld Glad to take the challenge! \$\endgroup\$
    – darrylyeo
    Jun 16, 2017 at 15:56
  • 1
    \$\begingroup\$ +1 for Annotated History! \$\endgroup\$
    – Eran W
    Mar 1, 2023 at 10:06
13
\$\begingroup\$

БК 0010, 254 bytes

16-bit DEC PDP-11 compatible Soviet home computer from 1984

БК 0010

; Mona Lisa 254-byte intro for BK 0010
; Download link: https://www.pouet.net/prod.php?which=86820
; Watch it using RGB->GreyScale adapter!

; Compile this source with PDPy11: https://github.com/imachug/PDPy11
; Manwe/SandS 2020

    BX = BXBY       ; low byte in word
    BY = BXBY+1     ; hight byte in word

        EMT 14              ; clear screen
        MOV #233,R0         ; set 256x256 mode
        EMT 16

        MOV #77310,R5       ; seed high word 0x7EC8, R4 = seed low word

        MOV #BRUSH+128.,R4
LOOP1:
        MOV R4,R0
        ASR R0
        COM R0
        BIC #177774,R0
        ADD #221,R0         ; set color ascii code
        EMT 16              ; set color

        MOV -(R4),R0        ; word, seed = (seed AND 0xFFFF0000) OR word
        MOV (R4),BXBY       ; bx in low byte, by in high byte

LEN:    MOV #2048.,R3       ; (64 - part) * 32
        SUB #32.,LEN+2
        BEQ LEN+2           ; end of data? go to HALT

LOOP2:  ASL R0
        ROL R5              ; seed = (seed << 1) AND 0xFFFFFFFF

        BCC NOCAR           ; carry = unmodified seed AND 0x80000000
            MOV #2301,R2    ; high word of XOR 0x04C1
            XOR R2,R5       ; seed = seed XOR 0x04C11DB7
            MOV #16667,R2   ; low word of XOR  0x1DB7
            XOR R2,R0
DIR:        MOV R0,#0       ; direction

NOCAR:  MOV #BY,R1          ; pointer to coordinate to modify
        BITB #2,DIR+2       ; dir AND 0x2
        BEQ 1
            DEC R1          ; point to bx
1:      TSTB DIR+2          ; dir AND 0x80
        BMI 2
            INCB (R1)
            BR 3
2:      DECB (R1)

3:      BIC #100200,BXBY    ; bx AND 0x7F, by AND 0x7F

        MOVB BX,R1
        MOVB BY,R2

        ADD #64.,R1
        ADD #144.,R2

        EMT 30              ; draw pixel R1,R2

        SOB R3,LOOP2

        BR LOOP1

BRUSH:  ; reversed array
.WORD  8505., 11193., 11332., 40503., 9639., 38606., 12590., 44957.
.WORD  49275., 9103., 45629., 7700., 44387., 16047., 36156., 21056.
.WORD  32967., 44146., 47330., 6263., 59670., 16059., 2569., 1043.
.WORD  21292., 45297., 10516., 35478., 45332., 45634., 21547., 54321.
.WORD  12369., 54650., 3453., 32252., 41772., 10669., 2137., 35575.
.WORD  41799., 37027., 38901., 57113., 9942., 15633., 8348., 8339.
.WORD  40146., 53425., 640., 28853., 45146., 47166., 37752., 3773.
.WORD  6923., 35473., 62875., 3644., 1835., 12187., 14270., 778.

BXBY:   ; low word, hight word 0x7EC8

.END
```
\$\endgroup\$
1
  • 3
    \$\begingroup\$ That might be the most specific system requirements I've ever seen on here. \$\endgroup\$ Dec 3, 2020 at 6:15
11
\$\begingroup\$

Python 3, 544 536 523 519 518 bytes

from tkinter import*
s=32456<<16
d=0
b="#000"
a=Canvas(Tk(),w=128,he=96,bg=b)
a.pack()
for p in range(64):w=ord("̊㞾⾛ܫ฼誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖\udf19韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ"[p]);s=s&~65535|w;*e,=divmod(w,256)[::-1];exec("c=s&8<<28>0;s=s*2^79764919*c;d=[d,s&255][c];e[d&2<1]=e[d&2<1]+(d&128<1)*2-1&127;a.create_line(*e,e[0]+1,e[1]+1,f=['#FFE289','#E99E45','#A55A00',b][p&3]);"*(64-p)*32)

This is a further golfed-down version of CCB60's Python translation of the reference implementation. I originally used a large hex number to represent the brush of the algorithm, but I later realized that my unfounded assumption that a Unicode string representation wouldn't work in Python was false.

I originally thought my byte count was significantly lower, but as ASCII-only pointed out, I didn't remember to count the Unicode characters as more than one byte.

Output (128 x 96)

Mona Lisa in tk window

Identical to CCB60's output.

\$\endgroup\$
2
  • \$\begingroup\$ This is 518 bytes, not 396 \$\endgroup\$
    – ASCII-only
    Jun 20, 2017 at 2:17
  • \$\begingroup\$ @ASCII-only I forgot to count the Unicode characters as more than one byte. Oof. No wonder the byte count seemed so low... \$\endgroup\$
    – notjagan
    Jun 20, 2017 at 2:43
11
\$\begingroup\$

Befunge, 1131 1052 bytes

"Dq~"1+00g"Ot"0"-R"0003"7/"727*"E1"5*\2*39*27*"\1"3*\2*:8-"ph"2*2\"N"2*"  =&~a"v
v  *83+"k~>"*524\*2"XS)"*2"E"-1:*2"YT"*2"j0"\+94*3"G}"+"%~)"8*2\+"%~E"-7:+" w"+<
>"V\"2*\2*"@"2*"R/"3*">~/"+56*"Y"2*"##`"2*\5*"1K"2*"%O"2*",+!_"2*25*\"{ "+"+<{"v
v"/~Y"+"y~G"+"%~"*5"1"*55*2"k"+98+9:*3"1"*2\*3";i"*2\+"7~@Z>x"*3"?"+92+" q"+" "<
>+",~"2*"}zQ1+BK"2*45*\45*",~s"+\25*9+9"~="+29+2*"wq"2*"r~I"+"@<c#"5*\45*"=o "+v
  v_,#!>#:<"P3 128 96 127"_v`+" k":p39+1:%*:*82<<0<<<\*5"%9D7"+")~"*2"g."+" }{"<
#@_:63p"@d~"03p2*13p043p0v^_:"@"%\"@"/p93g28*:*+^>\04>1-:93p3g2*+:v>g+\%:v>:"_"`
_3*55+,:2g.:1+2g.2+2g.1+v_:48**\1-:83p\83g:1g:23p\0v |:g39`\0p3g39<3v4\+4<^+1$$<
`v0:\%4g++77/"@"\%"@":::<^!:$$_\73g>0#p0#33g#:`#g^#< _$!#v_28*:*::0^>/2%8*-03p:v
">\:88vv%"@":\g38:\<_\73p\1-:!^v4:%\+g32::p31-*2+%2\*"@"% 4/"@":\++"C~":%\+g31:<
~v<\%*<>\"@"/77++p\0^!-g36:%**<>5*9++\:4/8%4*\2%+2*-23p:3 3g+\%:"="3*+\033g`28*v
">88*/7v>g!+53g2-!-153g2%+28*8^v2`\0\%2/2+*:*82:g34+*:*82<p34p33:-*2++%8\*+88< 8
^-1p++7<^35+*:*28\%**8*28-%2g35>#<*#2+#-:#!5#-3#3p#g1#3-#5!#+< >8+#:/#\4#*%#*^#<

There are a number of issues that make this a challenging problem in Befunge:

  1. Befunge has only got 2000 bytes of memory to work with (and that includes the source code), so there is no way we can render the entire image into memory before outputting it. The way I work around this is by repeatedly running the algorithm 96 times, once for each line. Each run stores just the pixels that are needed for the current line, which are then output at the end of the run. This allows us to get by with a pixel buffer of just 128 bytes.

  2. Befunge has no bit operations whatsoever. Many of the AND operations can simply be emulated with a modulo operator (e.g. a AND 0x7F can be replaced with a % 0x80). However, the XOR requires some rather complicated bit manipulation, which we have to deal with one byte at a time, using a set of custom formulas hardcoded to handle the four bytes we need. For example, to calculate a XOR 0xC1, we use the formula: a + 0xC1 - (a/64%4*64 + a%2)*2

  3. While not a limitation of Befunge per se, the interface on TIO is incapable of handling extended ASCII characters in the source, which would have been the easiest way to store the brush and colour tables. I work around this by generating those tables as a list of numbers on the stack, then have a little initialisation loop that copies the values from the stack to memory. A significant chunk of my time was spent golfing this table, which takes up the first five and half lines of code.

Unfortunately, despite all my effort to make the code compatible with TIO, and my choice of a file format that could be extracted from TIO (PPM), it's just too slow to complete within the 60 second time limit (running the algorithm 96 times probably doesn't help). But since it generates the image line by line, you should still get enough of the output to recover nearly half the image.

Try it online!

If you don't have a local PPM file viewer, you can easily convert to another format using one of the many online converters. One example being Convertio.

\$\endgroup\$
1
  • \$\begingroup\$ Probably not the easiest tool for the job. Well done! \$\endgroup\$
    – Arnauld
    Dec 25, 2017 at 11:56
9
\$\begingroup\$

Java 7, 681 677 675 626 612 610 bytes

Object l(){BufferedImage g=new BufferedImage(128,96,1);String b="̊㞾⾛
ܫ\u0E3C\uF59B誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖\udf19韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ
㺻\uE916ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝\u312E雎▧鸷ⱄ\u2BB9ℹ";for(int 
s=0x7EC80000,d=0,x,y,z=130,u,w,c,r=255,t=127,p=0,o;p<64;p++)
{w=b.charAt(p);s=s&0xFFFF0000|w;x=w&r;y=(w>>8)&r;for(o=0;o<(64-p)*32;o++)
{c=s&0x80000000;s<<=1;if(c!=0){s^=0x4C11DB7;d=s&r;}x=(u=d&z)==2?x+1&t:u==z?
x-1&t:x;y=u==0?y+1&t:u==128?y-1&t:y;if(x<128&y<96)g.setRGB(x,y,new int[]
{0xFFE289,0xE99E45,0xA55A00,0}[p&3]);}}return g;}

Outputs the following image in resolution 128x96:
enter image description here

I know it is not even near 250 bytes but hey it's java

-2 bytes thanks to Zacharý

\$\endgroup\$
9
  • \$\begingroup\$ Well hey, it appears that no-one has welcomed you to PPCG, so hi, and welcome to PPCG! Nice post, I only have two formatting recommendations for you 1, the header is, for the sake of having leaderboards supposed to be of the format #Language, <s>Old Scores</s> Score Bytes and 2 you can make your code a bit more readable by adding in a language flag of the form <!-- language-all: lang-java --> before your code block \$\endgroup\$ Jun 18, 2017 at 15:14
  • 1
    \$\begingroup\$ Hey thank you for welcoming me :)! I've now edited my post with the improvements you proposed. \$\endgroup\$
    – Twometer
    Jun 18, 2017 at 15:17
  • 2
    \$\begingroup\$ No problem @Twometer,it looks great and hope you enjoy your golfing \$\endgroup\$ Jun 18, 2017 at 15:18
  • 1
    \$\begingroup\$ Two bytes can be shaved: 0x04C11DB7 => 0x4C11DB7 and 0x7f => 127. Don't go hex crazy when it's not needed. \$\endgroup\$
    – Adalynn
    Jul 18, 2017 at 13:02
  • 2
    \$\begingroup\$ I love that “but hey it’s Java” \$\endgroup\$
    – Jack
    Sep 15, 2017 at 18:35
6
\$\begingroup\$

C#, 960 850 bytes

using System.Drawing;_=>{var m = new Bitmap(128,96);Graphics.FromImage(m).FillRectangle(Brushes.Black,0,0,128,96);for(int s=0x7EC80000,d=0,p=0,w,x,y,l,c,t,n=127;p<64;++p){w=new[]{778,14270,12187,1835,3644,62875,35473,6923,3773,37752,47166,45146,28853,640,53425,40146,8339,8348,15633,9942,57113,38901,37027,41799,35575,2137,10669,41772,32252,3453,54650,12369,54321,21547,45634,45332,35478,10516,45297,21292,1043,2569,16059,59670,6263,47330,44146,32967,21056,36156,16047,44387,7700,45629,9103,49275,44957,12590,38606,9639,40503,11332,11193,8505}[p];s=s>>16<<16|w;x=w&255;y=w>>8&255;for(l=0;l++<(64-p)*32;){c=(int)(s&0x80000000);s*=2;if(c!=0){s^=79764919;d=s&255;}t=d&130;x=t==2?(x+1)&n:t==130?(x-1)&n:x;y=t<1?(y+1)&n:t==128?(y-1)&n:y;if(x<=n&y<96)m.SetPixel(x,y,Color.FromArgb((int)new[]{0xFFFFE289,0xFFE99E45,0xFFA55A00,0xFF000000}[p&3]));}}return m;}

A straight forward copy of the pseudo code with some golfing added in. There is still a lot that can be golfed but I wanted to post my answer to get the ball rolling.

Full/Formatted version:

using System.Drawing;

class P
{
    static void Main()
    {
        System.Func<object, Bitmap> f = _ =>
        {
            var m = new Bitmap(128, 96);
            Graphics.FromImage(m).FillRectangle(Brushes.Black, 0, 0, 128, 96);

            for (int s = 0x7EC80000, d = 0, p = 0, w, x, y, l, c, t, n = 127; p < 64; ++p)
            {
                w = new[] { 778, 14270, 12187, 1835, 3644, 62875, 35473, 6923, 3773, 37752, 47166, 45146, 28853, 640, 53425, 40146, 8339, 8348, 15633, 9942, 57113, 38901, 37027, 41799, 35575, 2137, 10669, 41772, 32252, 3453, 54650, 12369, 54321, 21547, 45634, 45332, 35478, 10516, 45297, 21292, 1043, 2569, 16059, 59670, 6263, 47330, 44146, 32967, 21056, 36156, 16047, 44387, 7700, 45629, 9103, 49275, 44957, 12590, 38606, 9639, 40503, 11332, 11193, 8505 }[p];
                s = s >> 16 << 16 | w;
                x = w & 255;
                y = w >> 8 & 255;

                for (l = 0; l++ < (64 - p) * 32;)
                {
                    c = (int)(s & 0x80000000);
                    s *= 2;

                    if (c != 0)
                    {
                        s ^= 79764919;
                        d = s & 255;
                    }

                    t = d & 130;

                    x = t == 2 ? (x + 1) & n : t == 130 ? (x - 1) & n : x;
                    y = t < 1 ? (y + 1) & n : t == 128 ? (y - 1) & n : y;

                    if (x <= n & y < 96)
                        m.SetPixel(x, y, Color.FromArgb((int)new[] { 0xFFFFE289, 0xFFE99E45, 0xFFA55A00, 0xFF000000 }[p & 3]));
                }
            }

            return m;
        };

        f(null).Save("monaLisa.jpg");
    }
}
\$\endgroup\$
3
  • 3
    \$\begingroup\$ var m = new Bitmap(128,96) AE IU WAUGH WY \$\endgroup\$ Jun 18, 2017 at 2:59
  • 2
    \$\begingroup\$ @CalculatorFeline I don't understand? \$\endgroup\$ Jun 18, 2017 at 14:38
  • 9
    \$\begingroup\$ Possible because of the unneeded spaces? \$\endgroup\$
    – Adalynn
    Jun 18, 2017 at 19:30
5
\$\begingroup\$

Python 2.7; 880 876 bytes total (including data)

-4 bytes to 876 thanks to ZacharyT. (My python interpreter did not like his suggestion to drop the spaces between the 80s and else).

Taylor Scott's suggestion to put the brush into Base 10 is excellent, but notjagan (in a comment) took his suggestion one step further, using python's extended integer format in hex. notjagan's answer is in Python 3, and is such an improvement from what I did that he deserves credit. I hope he'll post it as a separate answer.

Output into a Tkinter window. Without scaling, the image is very small, but scaling adds about a dozen bytes to the count.

B=[0x030A,0x37BE,0x2F9B,0x072B,0x0E3C,0xF59B,0x8A91,0x1B0B,
0x0EBD,0x9378,0xB83E,0xB05A,0x70B5,0x0280,0xD0B1,0x9CD2,
0x2093,0x209C,0x3D11,0x26D6,0xDF19,0x97F5,0x90A3,0xA347,
0x8AF7,0x0859,0x29AD,0xA32C,0x7DFC,0x0D7D,0xD57A,0x3051,
0xD431,0x542B,0xB242,0xB114,0x8A96,0x2914,0xB0F1,0x532C,
0x0413,0x0A09,0x3EBB,0xE916,0x1877,0xB8E2,0xAC72,0x80C7,
0x5240,0x8D3C,0x3EAF,0xAD63,0x1E14,0xB23D,0x238F,0xC07B,
0xAF9D,0x312E,0x96CE,0x25A7,0x9E37,0x2C44,0x2BB9,0x2139]
s=0x7EC80000
d=0x00
from Tkinter import *
m=Tk()
a=Canvas(m,w=128,he=96,bg='black')
a.pack()
for p in range(64):
 w=B[p];s=(s&0xFFFF0000)|w;x=w%256;y=w/256
 for t in range((64-p)*32):
  c=s&0x80000000;s=(s<<1)&0xFFFFFFFF;
  if c:s=s^0x04C11DB7;d=s&0xFF
  if d&2:x=(x+(-1if d&0x80 else 1))&0x7f
  else:y=(y+(-1if d&0x80 else 1))&0x7f
  a.create_line(x,y,x+1,y+1,f=['#FFE289','#E99E45','#A55A00','#000000'][p&3])
mainloop()

There's not much going on here except translation into Python and some basic golfing. Sometimes bit-wise manipulations are shorter, sometimes integer math. I could not find a way to pack more of the logic into lists or arrays. The basic algorithm is already pretty dense.

Mona Lisa as Tkinter output

\$\endgroup\$
16
  • 2
    \$\begingroup\$ You don't need the space between the 80's and the else's. And, 0x00 is the same as 0, right? \$\endgroup\$
    – Adalynn
    Jun 18, 2017 at 19:26
  • 2
    \$\begingroup\$ And you can remove the space between import and *. \$\endgroup\$
    – Adalynn
    Jun 18, 2017 at 19:30
  • 1
    \$\begingroup\$ Got it down to 641 bytes by switching to Python 3 and making some general golfing improvements. \$\endgroup\$
    – notjagan
    Jun 18, 2017 at 21:57
  • 2
    \$\begingroup\$ Don't know about Python /Tkinter but in Tckl/Tk you can abbreviate #000000 to #000. May be it is possible here also? \$\endgroup\$
    – sergiol
    Jul 15, 2017 at 1:27
  • 2
    \$\begingroup\$ You could save some bytes by further golfing the data portion; exec"B=[%s]"%",".join("0x"+"030A37BE2F9B072B0E3CF59B8A911B0B0EBD9378B83EB05A70B50280D0B19CD22093209C3D1126D6DF1997F590A3A3478AF7085929ADA32C7DFC0D7DD57A3051D431542BB242B1148A962914B0F1532C04130A093EBBE9161877B8E2AC7280C752408D3C3EAFAD631E14B23D238FC07BAF9D312E96CE25A79E372C442BB92139"[_*4:_*4+4]for _ in range(64)) for example (315 bytes instead of 451; 136 bytes saved). \$\endgroup\$ Sep 4, 2017 at 3:50
4
\$\begingroup\$

Julia 1.0, 627 608 605 597 581 bytes

using Images
g(i=RGB.(zeros(999,999)),S=0x7EC80000,Z=0x0,F=127,X=255,D=Z)=(1:64 .|>P->(W=Int["̊㞾⾛ܫ\ue3c\uf59b誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖\udf19韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻\ue916ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ"...][P];S=S&0xFFFF0000|W;x=W&X;y=W>>8&X;0:32(65-P)-1 .|>_->(C=S&2^31;S=2S&~-2^32;C!=Z&&(S⊻=0x04C11DB7;D=S&X);(A=D&130)<1 ? (y=-~y&F) : A<3 ? (x=-~x&F) : A<129 ? (y=~-y&F) : (x=~-x&F);i[y+1,x+1]=reinterpret(RGB24,[Z,0xFFE289,0xE99E45,0xA55A00][1+P&3])));save("m.png",i[1:96,1:128]))
g()

-24 MarcMush

Pretty straightforward version of the reference implementation. It writes m.png which looks like this

enter image description here

Ungolfed version

using Images
function m(i=RGB.(zeros(999,999)),S=0x7EC80000,Z=0x0,F=127,X=255,D=Z)
  for P=0:63
    W=codepoint.(["̊㞾⾛ܫ\ue3c\uf59b誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖\udf19韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻\ue916ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ"...])[P+1]
    S=(S&0xFFFF0000)|W
    x=W&X
    y=(W>>8)&X
    for _=0:(64-P)*32-1
      C=S&0x80000000
      S=(S<<1)&0xFFFFFFFF
      C!=Z&&(S⊻=0x04C11DB7;D=S&X)
      (A=D&130)==Z ? (y=(y+1)&F) : A==2 ? (x=(x+1)&F) : A==128 ? (y=(y-1)&F) : (x=(x-1)&F)
      i[y+1,x+1]=reinterpret.(RGB24,[0xFFE289,0xE99E45,0xA55A00,Z])[1+(P&3)]
    end
  end
  save("m.png",i[1:96,1:128])
end
m()
\$\endgroup\$
0
3
\$\begingroup\$

Tcl/Tk, 805

808 815 816 819 826 839 840 843

Mona

Still the loser, but I had to do it! may be I can golf it more later!

Not the loser now!

rename set S
rename expr E
pack [canvas .c -w 130 -he 98 -bg #000]
.c cr i 67 51 -i [S I [image c photo -w 128 -h 96]]
S s 0x7EC80000
S d 0
time {S s [E $s&0xFFFF0000|[S w 0x[lindex {. 30A 37BE 2F9B 72B E3C F59B 8A91 1B0B
EBD 9378 B83E B05A 70B5 280 D0B1 9CD2
2093 209C 3D11 26D6 DF19 97F5 90A3 A347
8AF7 859 29AD A32C 7DFC D7D D57A 3051
D431 542B B242 B114 8A96 2914 B0F1 532C
413 A09 3EBB E916 1877 B8E2 AC72 80C7
5240 8D3C 3EAF AD63 1E14 B23D 238F C07B
AF9D 312E 96CE 25A7 9E37 2C44 2BB9 2139} [incr p]]]]
S x [E $w&255]
S y [E $w>>8]
time {S c [E $s&1<<31]
S s [E $s<<1]
if \$c {S s [E $s^79764919]
S d [E $s&255]}
switch [E $d&130] {0 {S y [E $y+[S h 1&127]]}
2 {S x [E $x+$h]}
128 {S y [E $y-$h]}
130 {S x [E $x-$h]}}
$I p #[lindex {FFE289 E99E45 A55A00 000} [E $p-1&3]] -t $x $y} [E (65-$p)*32]} 64

Tcl/Tk, 1370

Very ungolfed transliteration of the Pseudo-code before the golfing spree began! The update line makes possible to view the drawing being done progressively!

pack [canvas .c -w 130 -he 98 -bg #000]
.c create i 67 51 -i [set p [image create photo -w 128 -h 96]]

set COLOR {FFE289 E99E45 A55A00 000000}

set BRUSH {
 0x030A 0x37BE 0x2F9B 0x072B 0x0E3C 0xF59B 0x8A91 0x1B0B 
 0x0EBD 0x9378 0xB83E 0xB05A 0x70B5 0x0280 0xD0B1 0x9CD2 
 0x2093 0x209C 0x3D11 0x26D6 0xDF19 0x97F5 0x90A3 0xA347 
 0x8AF7 0x0859 0x29AD 0xA32C 0x7DFC 0x0D7D 0xD57A 0x3051 
 0xD431 0x542B 0xB242 0xB114 0x8A96 0x2914 0xB0F1 0x532C 
 0x0413 0x0A09 0x3EBB 0xE916 0x1877 0xB8E2 0xAC72 0x80C7 
 0x5240 0x8D3C 0x3EAF 0xAD63 0x1E14 0xB23D 0x238F 0xC07B 
 0xAF9D 0x312E 0x96CE 0x25A7 0x9E37 0x2C44 0x2BB9 0x2139}

set seed 0x7EC80000
set dir 0x00

set part 0
while {$part<64} {
  set word [lindex $BRUSH $part]
  set seed [expr ($seed&0xFFFF0000)|$word]
  set bx [expr $word&0xFF]
  set by [expr $word>>8]

  set len 0
  while {$len<[expr (64-$part)*32]} {
    set carry [expr $seed&0x80000000]
    set seed [expr $seed<<1]

    if \$carry {
      set seed [expr $seed^0x04C11DB7]
      set dir [expr $seed&0xFF]
    }

    switch [expr $dir&0x82] {
      0 {
        set by [expr $by+1&0x7F]
      }
      2 {
        set bx [expr $bx+1&0x7F]
      }
      128 {
        set by [expr $by-1&0x7F]
      }
      130 {
        set bx [expr $bx-1&0x7F]
      }
    }

    $p put #[lindex $COLOR [expr $part&3]] -to $bx $by
    incr len
    update
  }
  incr part
}
\$\endgroup\$
3
\$\begingroup\$

SmileBASIC, 454 447 444 bytes

DIM C[5]C[1]=-7543C[2]=-1466811C[3]=-5940736S=32456<<16FOR P=-63TO.W=ASC("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"[-P])S=W+(S>>16<<16)C[4]=127AND W
C[0]=W>>8FOR L=0TO 31-P*32O=S
S=S<<1IF O<0THEN S=79764919XOR S:D=130AND S
T=D*2AND 4C[T]=127AND C[T]-(D>>6)+1GPSET C[4],C[0]+144,C[3AND P]NEXT
NEXT

The string of "x"s had some invalid unicode characters, so I'm not able to post it here. Here are the character codes in decimal (just the BRUSH array in reverse): 8505, 11193, 11332, 40503, 9639, 38606, 12590, 44957, 49275, 9103, 45629, 7700, 44387, 16047, 36156, 21056, 32967, 44146, 47330, 6263, 59670, 16059, 2569, 1043, 21292, 45297, 10516, 35478, 45332, 45634, 21547, 54321, 12369, 54650, 3453, 32252, 41772, 10669, 2137, 35575, 41799, 37027, 38901, 57113, 9942, 15633, 8348, 8339, 40146, 53425, 640, 28853, 45146, 47166, 37752, 3773, 6923, 35473, 62875, 3644, 1835, 12187, 14270, 778

enter image description here

\$\endgroup\$
4
  • \$\begingroup\$ You posted an image of the output, would it be possible to post an image of the contents of the "xx...xx" string? \$\endgroup\$ Mar 16, 2018 at 3:41
  • 1
    \$\begingroup\$ Most of the characters don't actually have symbols defined for them, so it really does look like "xxxxxxxxxxx". I can get a list of the character codes though. \$\endgroup\$
    – 12Me21
    Mar 16, 2018 at 15:19
  • \$\begingroup\$ That actually might be helpful - I think I may be able to use a similar method with yabasic - oh, you already did it - coolio \$\endgroup\$ Mar 16, 2018 at 17:07
  • \$\begingroup\$ It's not very space efficient since (at least in UTF-8) most of those are saved as 3 or more bytes. But it's still better than hex and you might be able to shorten the decoder too. \$\endgroup\$
    – 12Me21
    Mar 16, 2018 at 17:19
2
\$\begingroup\$

Python 3 + matplotlib, 541

from pylab import*
B='ਃ븷鬯⬇㰎鯵醊ଛ봎碓㺸媰땰耂뇐튜錠鰠ᄽ혦᧟ꎐ䞣夈괩ⲣﱽ納竕儰㇔⭔䊲ᒱ隊ᐩⱓጄऊ묾ᛩ眘犬잀䁒㲍꼾掭ᐞ㶲輣節鶯⸱캖ꜥ㞞䐬뤫㤡'
s=32456<<16
d=0
i=zeros((256,256),'I')
for p in range(64):
    W=ord(B[p]);w=W>>8|W%256<<8;s=s&65535<<16|w;x=[w>>8,w&255]
    for l in range((64-p)*32):
        s*=2
        if s>>32:s=d=s^4374732215
        a=(-1)**(d>>7);b=d>>1&1;x[b]=x[b]+a&127;i[(*x,)]=[9036543,4562665,23205,0][p&3]
imsave('i',i.view('4B')[:96,:128,:3])

This saves the image as a png file "i". To display the image you can replace the imsave with an imshow and a show for 545 bytes.

enter image description here

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

:r4, 764 bytes

the source for run in :r4 github

enter image description here

^r4/lib/gui.txt
#c $FFE289 $E99E45 $A55A00 $000000
#b $37BE030A $072B2F9B $F59B0E3C $1B0B8A91 $93780EBD $B05AB83E $028070B5 $9CD2D0B1 $209C2093 $26D63D11 $97F5DF19 $A34790A3 $08598AF7 $A32C29AD $0D7D7DFC $3051D57A $542BD431 $B114B242 $29148A96 $532CB0F1 $0A090413 $E9163EBB $B8E21877 $80C7AC72 $8D3C5240 $AD633EAF $B23D1E14 $C07B238F $312EAF9D $25A796CE $2C449E37 $21392BB9
:m pick2 + $7f7f and dup $7f and over 8 >> 96 >? ( 2drop ; ) setxy ink@ a! ;
:s +? ( 2* ; ) 2* $4c11db7 xor rot drop dup 24 << 31 >> 1 or over $2 and 2 << 8 xor << rot rot ;
:d $100 $7ec80000 0 ( 64 <? )( dup $3 and 2 << 'c + @ ink dup >r 2* 'b + w@ $ffff and swap $ffff0000 and over or 64 r@ - 5 << ( 1? )( >r s swap m swap r> 1- ) drop nip r> 1+ ) 3drop ;
: cls d show 'exit >esc< ;

I use a trick for make the move without conditionals, transform bit 8 in sign and shift with bit 2. version with stack names:

| PHREDA 2017
| https://codegolf.stackexchange.com/questions/126738/lets-draw-mona-lisa
^r4/lib/gui.txt

#color  $FFE289 $E99E45 $A55A00 $000000
#brush [
$030A $37BE $2F9B $072B $0E3C $F59B $8A91 $1B0B
$0EBD $9378 $B83E $B05A $70B5 $0280 $D0B1 $9CD2
$2093 $209C $3D11 $26D6 $DF19 $97F5 $90A3 $A347
$8AF7 $0859 $29AD $A32C $7DFC $0D7D $D57A $3051
$D431 $542B $B242 $B114 $8A96 $2914 $B0F1 $532C
$0413 $0A09 $3EBB $E916 $1877 $B8E2 $AC72 $80C7
$5240 $8D3C $3EAF $AD63 $1E14 $B23D $238F $C07B
$AF9D $312E $96CE $25A7 $9E37 $2C44 $2BB9 $2139 ]

:movxy | dir seed bxy -- dir seed bxy 
  pick2 + $7f7f and
  dup $7f and
  over 8 >>
  96 >? ( 2drop ; )
  setxy ink@ a! ;

:step | dir bxy seed -- dir bxy seed
  +? ( 2* ; )
  2* $4c11db7 xor
  rot drop
  dup 24 << 31 >> 1 or
  over $2 and 2 << 8 xor <<
  rot rot ;

:draw
  $100      | dir
  $7ec80000 | seed
  0 ( 64 <? )(
    dup $3 and 2 << 'color + @ ink
    dup >r 2* 'brush + w@ $ffff and | dir seed brush
    swap $ffff0000 and over or      | dir bxy seed
    64 r@ - 5 << ( 1? )( >r         | dir bxy seed
        step swap movxy swap r> 1- ) drop nip
    r> 1+ ) 3drop ;

: cls draw show 'exit >esc< ;
\$\endgroup\$
2
\$\begingroup\$

PICO-8, 343 bytes

z=128s=32456g=0cls()for p=64,1,-1do
x=ord("9み𝘥7せウ.え{◆=⁘cに<@んrヌw◀め\t⁙,ヨ⁘∧⁘𝘣+1𝘲z}ュ,と𝘺ワ𝘨こレ」サ■う⧗キね█ふ𝘻>xやᵇ➡️い<+いゆ\n",p)y=ord("!+,お%∧1にら#の゛と>♪𝘳█てま「ホ>\n⁴𝘴ぬ)⌂ねの𝘵ケ0コ\r}こ)⁸⌂こ…❎ト&=  うオ²pぬま⧗ᵉ•⌂レᵉ⁷/7³",p)s=s\1|x>>16|y>>8for l=1,p*32do
c=s>>>15s*=2if(c>1)s^^=0x4c1.1db7g=s<<16
n=g>>6&2if(g&2>0)x-=n-1else y-=n-1
if(y%z<96)pset(x%z,y%z,ord("⁴\tᶠ",p-1&3))end
end

(Note: StackExchange seems to clobber the tabs, so wherever there is a \t sequence, replace it with a literal tab. It still works either way, but using literal tab characters makes the program shorter.)

Output of mona.p8

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

Yabasic, 790 779 bytes

An answer that takes no input and outputs to a new graphics window.

Open Window 128,96
j=127
s=4057*2^19
k=255
For p=-1To 63
Color Mid$("255,226,137233,158,069165,090,0000,0,0",1+And(3,p)*11,11)
If p<0Then Fill Circle 0,0,k:p=0Fi
w=Dec(Mid$("30A37BE2F9B072B0E3CF59B8A911B0B0EBD9378B83EB05A70B50280D0B19CD22093209C3D1126D6DF1997F590A3A3478AF7085929ADA32C7DFC0D7DD57A3051D431542BB242B1148A962914B0F1532C04130A093EBBE9161877B8E2AC7280C752408D3C3EAFAD631E14B23D238FC07BAF9D312E96CE25A79E372C442BB92139",4*p,4),16)
s=Or(And(-4^8,s),w)
x=And(k,w)
y=And(w/2^8,k)
For l=1To(64-p)*32
c=And(-2^31,s)
z=And(2^30,s)
s=And(1073741823,s)*2
t=s
If z Then t=Or(s,-2^31)Fi
s=And(-1,t)
If c Then s=Xor(79764919,s):d=And(k,s)Fi
d=And(130,d)
If d=0Then y=And(y+1,j)Fi
If d=2Then x=And(x+1,j)Fi
If d=128Then y=And(y-1,j)Fi
If d=130Then x=And(x-1,j)Fi
Dot x,y
Next
Next

Output

The below is scaled by a factor of 8

Mona.yab

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

Java 11, 518 Bytes

var i=new BufferedImage(128,96,1);for(int s=4057<<19,d=0,x,z=130,u,y,c,r=255,t=127,p=-1,o;++p<64;){y="̊㞾⾛ܫ฼誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖\udf19韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ".charAt(p);s=s&~65535|y;x=y&r;y>>=8;for(o=0;o++<(64-p)*32;){c=s>>63;s+=s;d=c!=0?s^=79764919:d;x=(u=d&z)==2?x+1&t:u<z?x:x-1&t;y=u==0?y+1&t:u==128?y-1&t:y;if(x<=t&y<96)i.setRGB(x,y,new int[]{~7542,0xE99E45,0xA55A00,0}[p&3]);}}

Readable version:

var i = new BufferedImage(128, 96, 1);
for (int s = 4057 << 19, d = 0, x, z = 130, u, y, c, r = 255, t = 127, p = -1, o; ++p < 64;) {
    y = "̊㞾⾛ܫ฼誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖\udf19韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ".charAt(p);
    s = s & ~65535 | y;
    x = y & r;
    y >>= 8;
    for (o = 0; o++ < (64 - p) * 32;) {
        c = s >> 63;
        s += s;
        d = c != 0 ? s ^= 79764919 : d;
        x = (u = d & z) == 2 ? x + 1 & t : u < z ? x : x - 1 & t;
        y = u == 0 ? y + 1 & t : u == 128 ? y - 1 & t : y;
        if (x <= t & y < 96)
            i.setRGB(x, y, new int[] { ~7542, 0xE99E45, 0xA55A00, 0 }[p & 3]);
    }
}

(scaled by a factor of 4): correct version output

Out of competition: 497 Bytes

Note: As it's not pixel perfect anymore, this one fails the rule:

  • Should you decide to use an alternate method, you still must generate the exact same output

It's a nice result anyway, so I'd like to share the code.

var i=new BufferedImage(128,96,1);for(int s=4057<<19,d=0,x,y,z=130,u,c,t=127,p=-1,o;++p<64;){x="̊㞾⾛ܫ฼誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖\udf19韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ".charAt(p);s=s&~65535|x;y=x>>8;for(o=0;o++<64-p<<5;){c=s>>63;s+=s;d=c!=0?s^=79764919:d;x=x+((u=d&z)==2?1:u<z?0:-1)&t;y=y+(u<1?1:u==128?-1:0)&t;if(y<96)i.setRGB(x,y,new int[]{~7542,0xE99E45,0xA55A00,0}[p&3]);}}

readable version:

var i = new BufferedImage(128, 96, 1);
for (int s = 4057 << 19, d = 0, x, y, z = 130, u, c, t = 127, p = -1, o; ++p < 64;) {
    x = "̊㞾⾛ܫ฼誑ᬋຽ鍸렾끚炵ʀ킱鳒ₓₜ㴑⛖\udf19韵那ꍇ諷࡙⦭ꌬ緼ൽ핺け퐱含뉂넔誖⤔냱匬Гਉ㺻ᡷ룢걲胇剀贼㺯굣Ḕ눽⎏쁻꾝ㄮ雎▧鸷ⱄ⮹ℹ".charAt(p);
    s = s & ~65535 | x;
    y = x >> 8;
    for (o = 0; o++ < 64 - p << 5;) {
        c = s >> 63;
        s += s;
        d = c != 0 ? s ^= 79764919 : d;
        x = x + ((u = d & z) == 2 ? 1 : u < z ? 0 : -1) & t;
        y = y + (u < 1 ? 1 : u == 128 ? -1 : 0) & t;
        if (y < 96)
            i.setRGB(x, y, new int[] { ~7542, 0xE99E45, 0xA55A00, 0 }[p & 3]);
    }
}

Out of competition output (scaled by a factor of 4): out of competition output

\$\endgroup\$
2
  • \$\begingroup\$ And BTW: welcome to Code Golf! \$\endgroup\$
    – Arnauld
    Jun 5, 2021 at 14:26
  • \$\begingroup\$ I've given both versions \$\endgroup\$
    – Brixomatic
    Jun 10, 2021 at 9:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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