This was immensely fun. Thank you for posting this challenge.
Full disclosure: The language (Hexagony) did not exist at the time this challenge was posted. However, I did not invent it, and the language was not designed for this challenge (or any other specific challenge).
){_2"_{\"{{""}"{'2//_.\><*\"\/_><[\]/3\'\_;|#__/(\2\'3_'}(#:|{$#{>_\//(#={/;01*&"\\_|[##={|}$_#></)]$_##|){*_.>.(/?#//~-="{}<_"=#/\}.>"%<.{#{x\"<#_/=&{./1#_#>__<_'\/"#|@_|/{=/'|\"".{/>}]#]>(_<\'{\&#|>=&{{(\=/\{*'"]<$_
Laid out hexagonally:
) { _ 2 " _ { \ "
{ { " " } " { ' 2 /
/ _ . \ > < * \ " \ /
_ > < [ \ ] / 3 \ ' \ _
; | # _ _ / ( \ 2 \ ' 3 _
' } ( # : | { $ # { > _ \ /
/ ( # = { / ; 0 1 * & " \ \ _
| [ # # = { | } $ _ # > < / ) ]
$ _ # # | ) { * _ . > . ( / ? # /
/ ~ - = " { } < _ " = # / \ } .
> " % < . { # { x \ " < # _ /
= & { . / 1 # _ # > _ _ < _
' \ / " # | @ _ | / { = /
' | \ " " . { / > } ] #
] > ( _ < \ ' { \ & #
| > = & { { ( \ = /
\ { * ' " ] < $ _
The program does not actually use the #
instruction, so I used that character to show which cells are genuinely unused.
How does this program work? That depends. Do you want the short version, or the long?
Short explanation
To illustrate what I mean by “line” and “segment” in the following explanation, consider this dissection of the intended output:
segments →
│ │ │ │ │ │x lines
─┼───┼─┼─────────┼─┼───┼─ ↓
│ │ │ │ │xxx│
─┼───┼─┼─────────┼─┼───┘
│ │ │ │x│
─┼───┼─┼─────────┼─┘
│ │ │xxxxxxxxx│
─┼───┼─┼─────────┘
│ │x│
─┼───┼─┘
│xxx│
─┼───┘
x│
With that explained, the program corresponds to the following pseudocode:
n = get integer from stdin
# Calculate the number of lines we need to output.
line = pow(2, n+1)
while line > 0:
line = line - 1
# For all segments except the last, the character to use is spaces.
ch = ' ' (space, ASCII 32)
# The number of segments in each line is
# equal to the line number, counting down.
seg = line
while seg > 0:
seg = seg - 1
# For the last segment, use x’s.
if seg = 0:
ch = 'x' (ASCII 120)
# Calculate the actual segment number, where the leftmost is 1
n = line - seg
# Output the segment
i = pow(3, number of times n can be divided by 2)
i times: output ch
output '\n' (newline, ASCII 10)
end program
Long explanation
Please refer to this color-coded code path diagram.
Execution starts in the top left corner. The sequence of instructions ){2'"''3''"2}?)
is executed (plus a few redundant cancelations, like "{
etc.) by pursuing a fairly convoluted path. We start with Instruction Pointer #0, highlighted in crimson. Halfway through, we switch to #1, starting in the top-right corner and painted in forest green. When IP #2 starts out in cornflower blue (middle right), the memory layout is this:
Throughout the entire program, the edges labeled 2a and 2b will always have the value 2
(we use them to calculate 2ⁿ⁺¹ and to divide by 2, respectively) and the edge labeled 3 will always be 3
(we use that to calculate 3ⁱ).
We get to business as we enter our first loop, highlighted in cornflower blue. This loop executes the instructions (}*{=&}{=
to calculate the value 2ⁿ⁺¹. When the loop exits, the saddle brown path is taken, which takes us to Instruction Pointer #3. This IP merely dabbles along the bottom edge westwards in goldenrod yellow and soon passes control to IP #4.
The fuchsia path indicates how IP #4, starting in the bottom left, proceeds swiftly to decrement line, set ch to 32
(the space character) and seg to (the new value of) line. It is due to the early decrement that we actually start with 2ⁿ⁺¹−1 and eventually experience a last iteration with the value 0. We then enter the first nested loop.
We turn our attention to the branching indigo, where, after a brief decrement of seg, we see ch updated to x
only if seg is now zero. Afterwards, n is set to line − seg to determine the actual number of the segment we’re in. Immediately we enter another loop, this time in the fair color of tomato.
Here, we figure out how many times n (the current segment number) can be divided by 2. For as long as the modulo gives us zero, we increment i and divide n by 2. When we are satisfied n is no longer thusly divisible, we branch into the slate gray, which contains two loops: first it raises 3 to the power of the i we calculated, and then it outputs ch that many times. Observe that the first of these loops contains a [
instruction, which switches control to IP #3 — the one that was only taking baby steps along the bottom edge earlier. The body of the loop (multiplying by 3 and decrementing) is executed by a lonely IP #3, imprisoned in an endless dark olive green cycle along the bottom edge of the code. Similarly, the second of these slate gray loops contains a ]
instruction, which activates IP #5 to output ch and decrement, shown here in dark Indian red. In both cases, those Instruction Pointers trapped in servitude obediently execute one iteration at a time and yield control back to IP #4, only to bide the moment for their service to be called upon once again. The slate gray, meanwhile, rejoins its fuchsia and indigo brethren.
As seg inevitably reaches zero, the indigo loop exits into the lawn green path, which merely outputs the newline character and promptly merges back into the fuchsia to continue the line loop. Beyond the final iteration of the line loop lies the short sable ebon path of ultimate program termination.
Inside the power loop
(,],~3^#@~.)@]
instead of(1,[:,1,"0~3*])
saves 1 byte. And if you're ok with!
as output charu:32+
instead of' #'{~
saves another one. – randomra – 2015-02-18T18:26:40.080#\
instead ofi.@#
and you overtake APL! :) – randomra – 2015-02-18T19:06:17.677Your second solution doesn't work because a cap would be needed, but I found another way to beat APL. – FUZxxl – 2015-02-18T20:25:26.047
The new output is the staircase for the
n-1
not forn
. – randomra – 2015-02-18T22:11:05.627@randomra Ah… that's shitty. Let me see if it's fixable. – FUZxxl – 2015-02-18T22:13:31.700
@randomra Not fixable. Let's see if beating APL is possible by other means. – FUZxxl – 2015-02-18T22:18:55.897
Very interesting approach. You can extract the factors of 2 from a number
– Level River St – 2015-02-19T01:50:51.363m
withm&-m
(C or Ruby syntax). See my answer http://codegolf.stackexchange.com/a/46861/15599 I dont' know if it will be useful in any way.@steveverrill That's a bit tricky to get right. Let me check… – FUZxxl – 2015-02-19T09:10:43.587
@randomra Now we are on par with APL. – FUZxxl – 2015-02-19T09:22:58.447