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
JMP divisibility # jump to the beginning of loop
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)
JMP isDivisible # jump to beginning of loop
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
JMP notDivLoop # jump to beginning of loop
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