# Collatz sequence on a two counter machine

## 18 instructions

I was a bit disappointed that I arrived late on scene, as the minimalistic nature of the problem and the language make there (seemingly) only one general approach for a good answer. I got a 19-instruction answer fairly quickly, but I didn't feel like it brought enough to the table to post it. But after much head scratching, my hacky z80 assembly experience came through and I found a way to save an instruction by reusing a block of code for a purpose it wasn't meant for!

# Let N be the previous number in the Collatz sequence.

# Print N, and if N==1, halt.
# X=N, Y=0
Main:           PRINTX          # Print N.
DJZX Done       # X=N-1 (N shouldn't be zero, so this never jumps)
DJZX Done       # If N-1==0, halt. Otherwise, X=N-2.

# Find the parity of N and jump to the proper code to generate the next N.
# X=N-2, Y=0
FindParity:     INCY
DJZX EvenNext   # If N%2==0, go to EvenNext with X=0, Y=N-1.
INCY
DJZX OddNext    # If N%2==1, go to OddNext with X=0, Y=N-1.
JMP FindParity

# Find the next N, given that the previous N is even.
# X=0, Y=N-1
EvenNext:       INCX
DJZY Main       # Y=Y-1 (Y should be odd, so this never jumps)
DJZY Main       # If Y==0, go to Main with X=(Y+1)/2=N/2, Y=0.
JMP EvenNext

# Find the next N, given that the previous N is odd.
# X=0, Y=N-1
OddNext:        INCX
INCX
INCX
DJZY EvenNext   # If Y==0, go to EvenNext with X=(Y+1)*3=N*3, Y=0.
JMP OddNext     # ^ Abuses EvenNext to do the final INCX so X=N*3+1.

# Halt.
Done:           HALT


SCORE: 21

Here is my attempt:

main: prints X and jumps to finish (if X==1).

divisibility: makes a distinction if X%2==0 or X%2==1. Also copies X to Y and makes X==0. Jumps to either isDivisible (if X%2==0) or isNotDivisible (if X%2==1).

isDivisible: loop used when Y%2==0. For each decrease of Y by 2, it increases X by 1. When Y==0, jumps to main.

isNotDivisible: used when Y%2==1. It increases X by 1.

notDivLoop: loop used when Y%2==1. For each decrease of Y by 1, it increases X by 3. When Y==0, jumps to main.

finish: halts

main:           PRINTX              # print X
DJZX main           # here X is always >0 and jump never fires (it is just for decreasing)
DJZX finish         # if initially X==1 this jumps to finish
INCX                # establish the previous state of X
INCX
# continue with X>1

divisibility:   DJZX isDivisible    # if X%2==0, then this will fire (when jumping Y=X)
INCY
DJZX isNotDivisible # if X%2==1, this fires (when jumping Y=X)
INCY

isDivisible:    DJZY main           # this jumps to the main loop with X=X/2
DJZY main           # this jump will never fire, because X%2==0
INCX                # for every partition 2 of Y, increase X (making X=Y/2)

isNotDivisible: INCX                # X=0, increase for 1
notDivLoop:     DJZY main           # in each iteration, increase X for 3 (when Y==0, X=3Y+1)
INCX
INCX
INCX

finish:         HALT                # finally halt


Supplied with 3 (using the interpreter supplied by @orlp), the produced result is:

3
10
5
16
8
4
2
1


### 19 instructions

I wrote my own interpreter because I'm fancy like that. Here is my solution for my own interpreter:

MP
XE
XE
HY
XV
XO
JH
WX
VYM
JW
LYM
X
X
OX
X
X
X
JL
EH


And here is what it looks like with syntax compatible to the other interpreter:

# x = n, y = 0
main:    printx
djzx   end
djzx   end

# x = n - 2, y = 0 on fallthrough
half:    incy
djzx   even
djzx   odd
jmp    half

evloop:  incx
# x = 0, y = n / 2  on jump to even
even:    djzy   main
jmp    evloop

oddloop: djzy   main
incx
incx
# x = 0, y = (n + 1) / 2 on jump to even
odd:     incx
incx
incx
incx
jmp    oddloop

end:     halt