Koch Snowflake - codegolf

Python, 650 612 594 574 characters

S='_a/G\F I\n'
A=dict(zip(S,('III','   ','__/','  G','\  ','F__','   ','III','')))
B=dict(zip(S,('III','   ','\  ',' aF','/a ','  G','   ','III','')))
C=dict(zip(S,('___','aaa','/  ','GII','II\\','  F','   ','III','')))
def T(s):
 for k in s:
    if k=='I':a=a[:-3]+('II\\'if'a '==d[1:3]else'GII'if' a'==d[:2]else 3*k)
    if k==n:d=c.replace('____','__/F').replace('aaaa','aa  ').replace('/  a','/a  ').replace('a  F','  aF');r+=a+n+b+n+d+n;a=b=c=''
 return r
print T(T(T('__\n\G\n'))).translate({97:95,71:47,73:32,70:92})

This works by expanding the triangle by a factor of 3 each time. To do that, we need to keep track of whether each symbol is a left or right boundary (e.g. how / is expanded depends on which side of the / is the inside). We use different symbols for the two possible cases, as follows:

_: _, outside on the top
a: _, outside on the bottom
/: /, outside on the left
G: /, outside on the right
\: \, outside on the left
F: \, outside on the right
<space>: inside
I: outside

The d variable handles the special case where the expansion of an a needs to extend into the 3x3 in the next row.

MS-DOS 16 bit machine code: 199 bytes

Decode using this site, save as 'koch.com' file and execute from WinXP command prompt.



Here's an easy-to-read assembler version:

  ; L-System Description
  ; Alphabet : F
  ; Constants : +, -
  ; Axiom : F++F++F
  ; Production rules: F -> F-F++F-F 
  ; Register usage:
  ;                             _        _
  ; bp = direction: 0 = ->, 1 = /|, 2 = |\, 3 = <-, 4 = |/_, 5 = _\|
  ; cl = min y, ch = max y
  ; bl = x (unsigned)
  ; bh = y (signed)
  ; si = max level

  ; clear data
  mov al,20h
  add dh,al
  mov ds,dx
  mov es,dx
  mov cx,di
  rep stosb
  mov ax,'__'
  mov dx,'/\'

  ; initialise variables
  mov bp,Direction0
  xor bx,bx
  mov si,4

  call MoveForward
  call TurnRight
  call TurnRight
  call MoveForward
  call TurnRight
  call TurnRight
  call MoveForward

  mov dh,cl
  xor dl,dl
  mov bl,79
  mov bh,dh
  mov w [bx],0a0dh
  mov b [bx+2],24h
  mov ah,9
  int 21h
  inc dh
  cmp dh,ch
  jle OutputLoop  

  dw MoveRight
  dw MoveUpRight
  dw MoveUpLeft
  dw MoveLeft
  dw MoveDownLeft
  dw MoveDownRight

  mov w [bx],ax
  add bl,2

  mov b [bx],dh
  inc bl
  jmp DecBHCheckY

  dec bl
  mov b [bx],dl
  dec bh
  jmp CheckY

  dec bl  
  cmp b [bx],20h
  jne MoveLeftAgain
  mov [bx],al
  dec bl  
  mov [bx],al

  add bx,255
  mov b [bx],dh
  jmp CheckY

  inc bh
  mov b [bx],dl
  inc bl

  cmp bh,ch
  jle NoMaxChange
  mov ch,bh
  cmp bh,cl
  jge NoMinChange
  mov cl,bh

  add bp,8

  add bp,2

  cmp bp,Direction6
  jb ret
  sub bp,12

  dec si
  push [bp]
  jz DontRecurse
  pop di
  call MoveForward
  call TurnLeft
  call MoveForward
  call TurnRight
  call TurnRight
  call MoveForward
  call TurnLeft
  call MoveForward
  inc si

Perl, 176 175 bytes

Posting this as a separate answer because it uses a binary source file, which is perhaps a bit cheaty. But considering that it’s still Perl source code, I think it’s remarkable that it beats the MS-DOS machine code solution!

Source as base64-encoded


Somewhat more readable

Replace all instances of /<[0-9a-f]+>/ with the relevant binary data:

# Raw data!

# Decode left half of the snowflake (without newlines)
s^.^$x=ord$&;$"x($x>>3).qw(\ /_ __/ \/ __ _\ / __/\__)[$x&7]^eg;

# Reconstruct the right half and the newlines

In this version, the snowflake is encoded in the following way:

  • The 8 bits in each byte are divided like this:

    |      5 bits       |   3 bits  |
              R               C
  • R encodes a run of spaces. The longest run is 27 characters, so all runs fit into 5 bits.

  • C encodes a sequence of characters which are simply looked up in the literal array. (I used to have slightly crazier encodings here where the array contained only / \ _, but the Perl code necessary to decode it was longer...)

  • I am lucky that the binary data does not contain any "/' or \ that would need escaping. I didn’t plan for this. But even if it did, I probably could have just changed the order of the items in the array to fix that.

  • It is amazing how simple this solution is compared to the tens of other solutions I went through before I came up with this. I experimented with many different bitwise encodings more complex than this one, and it never occurred to me that a simpler one might be worth it simply because the Perl code to decode it would be shorter. I also tried to compress repetitions in the data using variable interpolation (see the other answer), but with the newest version that doesn’t gain any characters anymore.