Onion Programming

22

1

Using only printable ASCII (hex codes 20 to 7E), write a square N×N core program without comments that is surrounded by 4 more layers, creating a (N+8)×(N+8) square program (N > 0). For N = 3 the layout (to be replaced by actual code) looks like this:

44444444444
43333333334
43222222234
43211111234
4321CCC1234
4321CCC1234
4321CCC1234
43211111234
43222222234
43333333334
44444444444
  • The C's represent the core 3×3 program.
  • The 1`s represent the first layer, the 2's represent the second layer, etc.

The program always takes a string of space separated integers such as 0 -1 31 -1 2 2 2 via stdin or similar (it should just be the plain numbers, no quotes or brackets or anything). The output depends on what parts of the layout were run.

There are five ways to run the program (newlines are included in the run). Each does something different to the list:

  1. Run just the core:

    CCC
    CCC
    CCC
    

    This computes the maximum of the absolute values of the input list elements, and prints CORE on a new line that many times. If the max is 0 nothing is output (a newline is fine).

    • The output for 0 -1 31 -1 2 2 2 would be

      CORE
      CORE
      ...
      

      31 times.

  2. Run the core with layer 1:

    11111
    1CCC1
    1CCC1
    1CCC1
    11111
    

    This outputs the average (arithmetic mean) of the list values to standard floating point precision.

    • The output for 0 -1 31 -1 2 2 2 would be 35 / 7 = 5 (5.0 is fine).
  3. Run the core with layers 1 and 2:

    2222222
    2111112
    21CCC12
    21CCC12
    21CCC12
    2111112
    2222222
    

    This outputs a space separated list of the input list reversed.

    • The output for 0 -1 31 -1 2 2 2 would be 2 2 2 -1 31 -1 0.
  4. Run the core with layers 1, 2, and 3 (the pattern should be obvious).
    This outputs a space separated list of the sorted input list.

    • The output for 0 -1 31 -1 2 2 2 would be -1 -1 0 2 2 2 31.
  5. Run the core with layers 1, 2, 3, and 4.
    This outputs a space separated list of the input list with duplicates removed, the ordering doesn't matter.

    • The output for 0 -1 31 -1 2 2 2 could be -1 0 2 31.

All output is to stdout or a similar alternative.

Only these 5 layout combinations have specified behavior.

Notes

  • Comments are not allowed in the core or layers or combinations thereof. Code that is a no-op or does nothing constructive does not count as a comment.
  • Remember that the core can have any (positive) N×N dimensions, but the layers are only one character thick.
  • You may assume the input has no leading or trailing spaces and exactly one space between numbers. It will always contain at least one number. (The output lists should be formatted like this too.)
  • You may assume the list and calculations necessary for output won't have values that overflow (or underflow) your integers (as long as their max is something reasonable like 216).

Scoring

Writing this program normally would be easy. Writing it with a small core is hard.

The program with the smallest core size (the smallest N) wins. In case of ties the winner is the full program (the (N+8)×(N+8) square) with the fewest distinct characters (not counting newlines).

Please report your N value at the top of your answer.

Calvin's Hobbies

Posted 2014-10-30T07:38:10.407

Reputation: 84 000

Completion of this challenge requires that I update my language spec. There is no way to sort a stack using only stack operations without access to a second stack. Time to squeeze in another codepoint for List commands! (I got up through layer 3, but 4 and 5 aren't possible). – Draco18s no longer trusts SE – 2018-12-05T03:11:34.413

1I thought that this would also be another one of those new types – Optimizer – 2014-10-30T07:42:26.657

Can I use a language that ignores everything after a newline? – isaacg – 2014-10-30T07:43:39.150

1@isaacg Yes (as long as newline isn't considered the comment character, which would be weird). – Calvin's Hobbies – 2014-10-30T07:46:03.813

3@Optimizer Don't tempt me... "Each answer adds a new layer to the code onion so it does something new with the list..." – Calvin's Hobbies – 2014-10-30T07:49:16.807

Can the input require to be surrounded with [ ] ? – feersum – 2014-10-30T08:09:13.933

@feersum No. It should be the plain space separated list that you get in as a string. – Calvin's Hobbies – 2014-10-30T08:12:05.150

Can my output for the last 3 layers be like [1 2 3] instead of 1 2 3 ? – Optimizer – 2014-10-30T08:13:17.313

1@Optimizer No. (I know these i/o rules are kinda harsh but that's to keep things consistent across languages.) – Calvin's Hobbies – 2014-10-30T08:14:52.390

Are comments only forbidden in the core, or is it also forbidden to render part of the core as comment by the surrounding onion layers? Or are comments generally forbidden everywhere? – M.Herzkamp – 2014-10-30T14:53:58.060

@M.Herzkamp Forbidden everywhere. – Calvin's Hobbies – 2014-10-30T20:38:54.157

Answers

10

CJam, N = 5, 27 (26) unique characters

It's 26 characters if I don't count the spaces. The program could actually be converted to one that doesn't use spaces, by just filling up all empty spaces with no-ops (e.g. _; which duplicates the top stack element and then discards, or by sorting the array again and again), but it would just distract from the actual code.

l~]_|S*      
{l~]$S*      
 {l~]W%S*    
  {l~]_,\    
   {l~]{z    
    }%$W=    
    "CORE    
    "*       
         }   
   ;:+d\/ }  
  ;        } 
 ;          }
;            

Test it here.

The core is

l~]{z
}%$W=
"CORE
"*

(Plus an empty line.)

I'm fairly sure that N = 4 can't be done in CJam (and I'm sure Dennis will convince me otherwise :D). The above has 17 characters, and while it might be possible to get it down to 16 (e.g. if CJam didn't have a bug to choke on :z, which requires {z}%, or by using ARGV), I don't think you can fit it in the layout without introducing a line break within CORE.

All of the implementations are very straightforward solutions to the given tasks. All of them start with l~] which reads STDIN, evaluates it, and puts it in an array.

The previous layer is always surrounded in {...}, which makes it a block that isn't automatically executed. And instead of executing it, I just discard it from the stack with ;, so no layer depends on code in the previous layer. In the Layer 1, the code didn't fit into the first line, so I continued it after discarding the core block.

Now for the actual programs:

  • Core:

    {z}%$W="CORE
    "*
    

    Map abs onto the list, sort it, take the last element, repeat CORE (and a line break) that many times.

  • Layer 1:

    _,\:+d\/
    

    Duplicate the list, take the length, swap the stack elements, get the sum, cast to double, swap the stack elements, divide. I think this can be shorter, but there's no incentive to do so.

  • Layer 2:

    W%S*
    

    Reverse the array, riffle with spaces.

  • Layer 3:

    $S*
    

    Sort the array, riffle with spaces.

  • Layer 4:

    Duplicate, take set union, riffle with spaces.

Some other optimisations are also possible, like reusing the ; and *S of Layer 2, but again, but it doesn't affect the score.

Martin Ender

Posted 2014-10-30T07:38:10.407

Reputation: 184 808

17

Python 2 - N = 17, 53 characters

Oh I love source-layout challenges with Python...

i=4                     ;
ii=3                    ;
iii=2                   ;
iiii=1                  ;
iiiii=0;R=raw_input     ;
iiiii;w=R().split()     ;
iiiii;n=map(int,w)      ;
iiiii;S=set(n);M=max    ;
iiiii;s=sorted(n)       ;
iiiii;J="\n".join       ;
iiiii;j=" ".join        ;
iiiii;k=M(map(abs,n))   ;
iiiii;A=J(["CORE"]*k)   ;
iiiii;B=sum(n)/len(n)   ;
iiiii;C=j(w[::-1])      ;
iiiii;D=j(map(str,s))   ;
iiiii;E=j(map(str,S))   ;
iiiii;P=A,B,C,D,E       ;
iiiii;print P[i]        ;
iiiii;" /__----__\  "   ;
iiiii;"|/ (')(') \| "   ;
iiii;"  \   __   /  "   ;
iii;"   ,'--__--'.   "  ;
ii;"   /    :|    \   " ;
i;"   (_)   :|   (_)   ";

There's still some unused whitespace, though.

I could still improve the unique character count, but I'll stick with better readability - if there is any at all.

Edit: Oh, it's Stan again!

Falko

Posted 2014-10-30T07:38:10.407

Reputation: 5 307

You probably can save some lines by aliasing print instead of the i=* trick – M.Herzkamp – 2014-10-30T14:26:50.623

@M.Herzkamp: Aliasing print is not possible with Python 2. But sure, there's probably room for improvement - maybe using Python 3. – Falko – 2014-10-30T14:30:42.597

I don't know Python, but isn't this missing absolute value in the core code output - c*max(n) – nutki – 2014-10-30T14:45:37.967

@nutki: You're right! I didn't read carefully. But I was able to fix it. – Falko – 2014-10-30T15:15:35.153

6

Python 3: N=11, 40 distinct characters

if 1:              
 if 1:             
  if 1:            
   if 1:           
    p=print;R=0    
    a=input()      
    b=a.split()    
    m=map;a=abs    
    E=max;l=len    
    n=m(int,b);    
    C=['CORE']     
   "R=E(m(a,n))"   
   OO=C*R;s=sum    
   "x='\n'.join"   
   "p(x(O))    "   
  "p(s(n)/l(b)) "  
 "p(*b[::-1])    " 
"p(*sorted(n))    "
p(*set(n))         

Thanks to @Falko for being my muse. This works, because Python does not create a new scope for each if statement, so the variables persist in the outer print statements. One annoying thing was that a map object (in our case n) can be used only once. So it was necessary to string out the R=E(...) line, but then R was not defined. Therefore I was lucky that there were four spaces left in the first line!

The output can be solved by providing multiple elements *b[::-1] instead of the list. The alternative ' '.join(...) would have been too long.

M.Herzkamp

Posted 2014-10-30T07:38:10.407

Reputation: 1 227

Beautiful! Nice to see an alternative approach to deal with variable line beginnings in python. Just some short if-statements and all those spaces are just fine. :) – Falko – 2014-10-31T12:35:53.823

@Falko: The downside is: there is no space for Stan :( – M.Herzkamp – 2014-10-31T12:39:29.163

2

C (gcc), N = 15, 47 unique characters

Assumes sizeof(int) == 4 and sizeof(int*) >= sizeof(int).

;                     ;
 ;                   ; 
  ;                 ;  
   ;           float   
    s;c(a,b)int*a,*    
    b;{b=*b-*a;}i,n    
    ,*f;*q,*R,C,E ;    
    main(a){for(;0<    
    scanf("%i",&a);    
    i=i<abs(a)?a:i,    
    s+=f[n-!0]=a)f=    
    realloc(f,++n*4    
    );qsort(f,n*C,4    
    ,c);for(i=q?R?n    
    :!0:i;i--;a=f[i    
    ])!E|n-i<2|a!=f    
    [i]&&printf(q?R    
    ?R:q:"CORE\n",!    
    q+R?f[i]:s/n);}    
   ;*q="%f";       ;   
  ;*R="%.0f ";      ;  
 ;C=!0;              ; 
;E=!0;                ;

4 Layers

3 layers

2 layers

1 layer

Core

gastropner

Posted 2014-10-30T07:38:10.407

Reputation: 3 264

0

Runic Enchantments, N=9 N=8, 38 Characters

/ o/\  \     \S\
" //RiU\      \}
@            q "
"        }+1\r @
}   ^U \    {q "
     \{\?)}\+  }
  o\/'|A:{:/R' S
 //r/Ril2=?\?R :
   ,A~/"OC"/=? {
   @| \"RE"\3= =
 D$\' /rqka/l2S?
    i \*@   il\/
   'R1i     Ui ~
 R$/Rak      U \
 ?!D  Rlril1-{=
R   R: }S:{=?\~

Try it online!

Turns out I was wrong, I forgot I had an explicit sort command already, due to having encountered the "sort a list" problem before. This does, however, limit the size of inputs that the final program can take (8 values) due to the internal costs of the sort command. A slight tweak can increase the input size to 13 at the cost of 1 unique character or to 19 for two unique characters (all additional characters are on Layer 1 and added at the same time, but the increased capacity of the IP's stack isn't needed until Layer 3, as C, L1, and L2 can perform their calculations without holding the entire input in memory).

Core: Try it online!

Layer 1: Try it online!

Layer 2: Try it online!

Layer 3: Try it online!

Layer 4: Try it online!

Further compression is highly unlikely, due to the smaller space necessitating an increase in the number of flow control characters. I found an arrangement that gave 9 empty spaces in the core program, but that is not enough, as we need (a correctly arranged) 15.

Explaining how any of these programs work is difficult without a visual map of the path the IP takes, which is cumbersome and time consuming to construct. The initial entry point is the upper left corner of the Core program (^) which allows for consistent flow control as new layers are added, as each layer has an opportunity to intercept on the newly added line at the top or the bottom.

Layers 1 and 2 intercept at the bottom (so that the top line remains empty for future layers) and then perform their operations along the right hand edge (a loop arranged vertically). Layer 1 is slightly too long and takes 3 characters along the top edge as well, but the diagonal reflector (\) in the top right realigns the IP with the next loop iteration.

Layer 3 intercepts along the top edge in order to grab the first input value before redirecting to the bottom edge (Layer 4 leaves a NOP in this column on its bottom line) and reads the full input using the bottom edge loop, redirecting on the Down command (D) in the lower left. From there the IP bounces a few times before ending up in an output ($) loop in the lower left in order to space-separate the values.

Layer 4 utilizes all of the functionality of layer 3 (hence the blank space), but intercepts on its own new top edge (upper left) in order to perform its own functionality at the end of Layer 3's processing. The top left corner inserts a string "@" which is used to denote the end of the array before entering the processing loop along the bottom. If a duplicate value is found, it's popped (~, lower right corner) otherwise the branch is taken that consumes the new right hand edge. This side branch checks to see if the end of the array has been reached, and if so, break out and head to the same space-separated output loop from Layer 3. Otherwise use the blank space on Layer 3 to return back to the main loop.

Draco18s no longer trusts SE

Posted 2014-10-30T07:38:10.407

Reputation: 3 053