Nickolas's blog

By Nickolas, 7 years ago, translation, In English,

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 
 
 
 
 
  • Vote: I like it
  • +38
  • Vote: I do not like it