Roco is a very simple language. On one hand, it's esoteric, so everything you need to know about it fits in two pages. On the other hand, it's powerful enough to enable writing rather complicated programs in it without racking one's brains over each elementary operation (like in Brainfuck). Being able to refer variables using names (numerical, but names nevertheless) is already a gift to the programmer :-)
188A - Hexagonal Numbers
As usual, the first problem tests the competitor's understanding of basic arithmetic operations. The solution is very similar to the example.
iin [0]
mul [1] [0] 2
dec [1]
mul [1] [1] [0]
iout [1]
ac
Starting with the second one, the problems need learning the main feature of the language — the coroutines. A coroutine is a named piece of code executed in an endless loop. Something in between a procedure (without input parameters and variables, but with a name used for calling it) and a loop (without loop counter or break conditions, but with a chance to break it at any moment), coroutines allow to implement both these concepts.
188B - A + Reverse B
The key element of this problem is reversing a number (we knew how to add the second number to it ever since the example). There are two ways to do this: either read the number as a sequence of characters and accumulate its reverse simultaneously, or read the number as, well, a number and calculate its reverse using math. I used the second approach, so I needed a single loop-like coroutine which stopped when the number being reversed turned zero.
/* coroutine to reverse a number [1] into [2] */
co reverse {
eq [3] [1] 0
if [3]
ac
/* find current number modulo 10 */
mod [3] [1] 10
/* add it to reversed number */
mul [2] [2] 10
add [2] [2] [3]
/* and divide current number */
div [1] [1] 10
}
iin [0]
iin [1]
ca reverse
add [2] [2] [0]
iout [2]
ac
188C - LCM
In other languages the easiest way to get LCM of two numbers is recursive. The specifics of Roco is that one can't write a recursion with a single coroutine calling itself — after the second call of the coroutine its execution continues from the place where it stopped last time. So it was easier to rewrite the standard algorithm as a loop.
/* [0] - a
[1] - b
*/
co gcd {
eq [2] [0] 0
if [2]
ac
set [2] [1]
set [1] [0]
mod [0] [2] [1]
}
iin [0]
iin [1]
set [3] [0]
set [4] [1]
ca gcd
/* now [1] is gcd */
div [5] [3] [1]
mul [5] [5] [4]
iout [5]
ac
188D - Asterisks
Ok, let's move on to a more complicated task: here we need a nested loop, the outer one running through the lines and the inner one — through the asterisks in the line. The inner loop will be called several times (from each iteration of the outer one), so here one could meet the mentioned feature of the coroutines for the first time.
/* [0] - * quantity
[1] - current line
[2] - current * in line
*/
co one_row {
lt [3] [1] [2]
if [3]
ac
cout 42
inc [2]
}
co print_all {
lt [3] [0] [1]
if [3]
ac
set [2] 1
ca one_row
cout 10
inc [1]
}
iin [0]
set [1] 1
ca print_all
ac
188E - HQ9+
This time one can't solve this problem in one line using regular expressions, since this language has none. So one has to do it in an old-fashioned way, reading characters one-by-one and checking them for equality with each of the required ones. The code is rather long — there are three characters to compare each one to, and the answer has to be printed char-by-char, but overall there is nothing complex here.
/* [0] - YES or NO
[1] - current char
*/
co one_char {
cin [1]
eq [2] [1] 10
if [2]
ac
/* H */
eq [2] [1] 72
or [0] [0] [2]
/* Q */
eq [2] [1] 81
or [0] [0] [2]
/* 9 */
eq [2] [1] 57
or [0] [0] [2]
}
co say_yes {
cout 89
cout 69
cout 83
cout 10
ac
}
co say_no {
cout 78
cout 79
cout 10
ac
}
set [0] 0
ca one_char
/* output the result stored in [0] */
if [0]
ca say_yes
dec [0]
if [0]
ca say_no
ac
Three last problems are almost real programming :-) They use the fact that in Roco variables are referenced not by names but by their indices in the heap, and the index can in turn be the contents of a variable. One can do more interesting things with arrays, but the programs become longer and more error-prone.
188F - Binary Notation
The bad thing about the binary notation is that the digits are calculated right-to-left but have to be printed left-to-right. This can be done without arrays (reverse the number in binary using the math trick from 188B - A + Reverse B and then print its digits in the same order as they are calculated), but I used an array solution which first calculates the digits and writes them into an array and then goes through the array from the end to the start and prints the digits.
/* [0] - current number
[1] - index of last digit calculated
[2]..[9] - service cells
[10]..[[1]] - the actual digits of the number
*/
co calculate_digits {
/* stop when the number is 0 */
eq [3] [0] 0
if [3]
ac
/* get next digit - put it to its location directly */
mod [[1]] [0] 2
/* update the number and its notation length */
div [0] [0] 2
inc [1]
}
co output_digits {
/* stop when the next digit printed will be outside of the number */
eq [3] [1] 9
if [3]
ac
/* output the digit at [1] */
iout [[1]]
dec [1]
}
iin [0]
set [1] 10
ca calculate_digits
dec [1]
ca output_digits
ac
188G - Array Sorting
Unlike Befunge, in Roco the memory size limit is technical, not conceptual, so one could coed counting sort as well as comparison-based sort. Here is a code for the former one.
/* [1]..[100] - the counts of numbers
[101] - quantity of numbers
[102] - current number
[103] - current index
*/
iin [101]
set [103] 1
co init {
eq [104] [103] 101
if [104]
ac
set [[103]] 0
inc [103]
}
ca init
set [103] 0
co read {
eq [104] [103] [101]
if [104]
ac
iin [102]
inc [[102]]
inc [103]
}
ca read
set [102] 1
co print_sorted {
eq [104] [102] 101
if [104]
ac
set [103] 1
co print_one_number {
gt [104] [103] [[102]]
if [104]
ac
iout [102]
cout 32
inc [103]
}
/* due to coroutine property (!), call only for positive quantity of numbers*/
gt [104] [[102]] 0
if [104]
ca print_one_number
inc [102]
}
ca print_sorted
ac
188H - Stack
This problem required to simulate a stack using an array — nothing too complicated, especially since the data never overflows and never attempts to do invalid calculations.
/* [0] - current character
[1] - current stack size
[2]..[9] - service
[10]... - stack
*/
co plus {
dec [1]
add [3] [1] 9
add [4] [3] 1
add [[3]] [[3]] [[4]]
ac
}
co multiply {
dec [1]
add [3] [1] 9
add [4] [3] 1
mul [[3]] [[3]] [[4]]
ac
}
co number {
sub [2] [0] 48
inc [1]
add [3] [1] 9
set [[3]] [2]
ac
}
co process {
cin [0]
/* end of line */
eq [2] [0] 10
if [2]
ac
/* digit - push on the stack */
gt [2] [0] 47
if [2]
ca number
eq [2] [0] 43
if [2]
ca plus
eq [2] [0] 42
if [2]
ca multiply
}
set [2] 0
ca process
/* output the topmost element of the stack */
add [2] [1] 9
iout [[2]]
ac