31
\$\begingroup\$

There are 95 printable ASCII characters:

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

In the Consolas font (the Stack Exchange code block default), some of the characters have mirrors around a vertical axis of symmetry:

  • These pairs of characters are mirrors of each other: () [] {} <> /\
  • These characters are mirrors of themselves: ! "'*+-.8:=AHIMOTUVWXY^_ovwx| (Note that space is one.)
  • These do not have mirrors: #$%&,012345679;?@BCDEFGJKLNPQRSZ`abcdefghijklmnpqrstuyz~

(i, l, 0, #, and probably other characters are their own mirrors in some fonts but we'll stick to the Consolas shapes.)

A string is said to be a mirror of itself if it is made with only the 39 mirror characters, arranged such that the string has a central vertical line of symmetry. So ](A--A)[ is a mirror of itself but ](A--A(] is not.

Write a one-line even-length program that is a mirror of itself. When N copies of its left half have been prepended to it and N copies of its right half have been appended to it, it should output N+1. N is a non-negative integer.

For example, if the program was ](A--A)[ (left half: ](A-, right half: -A)[), then:

  • Running ](A--A)[ should output 1. (N = 0)
  • Running ](A-](A--A)[-A)[ should output 2. (N = 1)
  • Running ](A-](A-](A--A)[-A)[-A)[ should output 3. (N = 2)
  • Running ](A-](A-](A-](A--A)[-A)[-A)[-A)[ should output 4. (N = 3)
  • . . .
  • Running ](A-](A-](A-](A-](A-](A-](A-](A-](A-](A--A)[-A)[-A)[-A)[-A)[-A)[-A)[-A)[-A)[-A)[ should output 10. (N = 9)
  • etc.

Rules

  • Output to stdout or your language's closest alternative. There may be an optional trailing newline. No input should be taken.
  • The process should theoretically work for N up to 215-1 or beyond, given enough memory and computing power.
  • A full program is required, not just a REPL command.

The shortest initial program (N = 0 case) in bytes wins.

\$\endgroup\$
  • \$\begingroup\$ In some fonts, # is its own relection as well, but, you're right, not in consolas. \$\endgroup\$ – SuperJedi224 May 28 '15 at 17:53
  • 1
    \$\begingroup\$ Using repls is allowed? In other words: should we write a complete valid program or an expression is enough? I'm thinking about the Haskell entry which would work when running in ghci but isn't a valid complete program. \$\endgroup\$ – Bakuriu May 29 '15 at 4:34
  • \$\begingroup\$ @Bakuriu A full program is required. The Haskell answer is invalid. \$\endgroup\$ – Calvin's Hobbies May 29 '15 at 9:20
  • 4
    \$\begingroup\$ Why aren't 'b' and 'd' mirrors of eachother? It makes my plan impossible :P \$\endgroup\$ – Thijs ter Haar May 29 '15 at 11:21
  • 1
    \$\begingroup\$ @ThijsterHaar I actually didn't consider that but it looks like their Consolas shapes are just slightly different, sorry :P \$\endgroup\$ – Calvin's Hobbies May 29 '15 at 11:25
20
\$\begingroup\$

Pip, 12 8 4 bytes

Now with 66% fewer bytes!

x++x
  • x is a variable, preinitialized to "". In numeric context, this becomes 0.
  • The first half, sans the final +, makes an expression of the form x+x+...+x. This is a valid statement that does nothing.
  • The second half, including the final + from the first half, makes an expression of the form ++x+x+...+x. ++x increments x to 1, and the rest adds it to itself N times. Because expressions are evaluated left-to-right in Pip, the increment is guaranteed to happen first, and the result is equal to the number of mirror levels.
  • At the end, the value of this expression is auto-printed.

Unfortunately, Pip doesn't do well processing huge expressions: this solution causes a maximum recursion depth exceeded error for N above 500 or so. Here's a previous solution that doesn't, for 8 bytes:

x++oo++x

More on Pip

\$\endgroup\$
  • \$\begingroup\$ OK, unless somebody posts a 2 byte answer, looks like you got this one in the bag. By the way, I don't know if you've run it with N = 32767, but the actual output is Fatal error: maximum recursion depth exceeded while calling a Python object. \$\endgroup\$ – Dennis May 30 '15 at 18:19
  • \$\begingroup\$ @Dennis Yeah, I ran into that actually--it starts quite early, around 600 if not before. The reason is that expressions are evaluated by recursively evaluating all subexpressions first, so x+x+...+x generates O(N) depth of recursion. Maybe that invalidates this answer. I'll add a note. \$\endgroup\$ – DLosc May 30 '15 at 18:40
  • \$\begingroup\$ The recursion limit is easily adjustable in Python, isn't it? \$\endgroup\$ – Dennis May 30 '15 at 18:56
  • \$\begingroup\$ @Dennis Yes, although there are dire warnings about what it'll do to your system if you set it too high, so I've never tried it. ;) Besides, configuring it isn't possible from within Pip, so it seems kinda like cheating to me. If you want to try it, though, I'd be interested in hearing the results. \$\endgroup\$ – DLosc May 30 '15 at 19:05
  • \$\begingroup\$ I've tried. On my machine, increasing the recursion limit to 20000 already segfaults. But since the answer has to work only given enough memory and computing power, that shouldn't be a problem. \$\endgroup\$ – Dennis May 30 '15 at 19:43
34
\$\begingroup\$

GolfScript, 10 bytes

!:{)::(}:!

Try it online with Web Golfscript: N = 0, N = 1, N = 2, N = 3, N = 41

Web GolfScript has a 1024 character limit, but the Ruby interpreter handles N = 32767 perfectly:

$ curl -Ss http://www.golfscript.com/golfscript/golfscript.rb > golfscript.rb
$ echo '"!:{):"32768*":(}:!"32768*' | ruby golfscript.rb > mirror-level-32767.gs
$ ruby golfscript.rb mirror-level-32767.gs
32768

How it works

Without any input, GolfScript initially has an empty string on the stack.

In the first left half, the following happens:

  • ! applies logical NOT to the empty string. This pushes 1.

  • :{ saves the integer on the stack in the variable {.

    Yes, that is a valid identifier, although there's no way to retrieve the stored value.

  • ) increments the integer on the stack.

  • : is an incomplete instruction.

The the subsequent left halves, the following happens:

  • :! (where : is a leftover from before) saves the integer on the stack in the variable !.

    Yes, that is also a valid identifier. This breaks the ! command, but we don't use it anymore.

  • :{, ) and : work as before.

In the first right half, the following happens:

  • :: (where : is a leftover from before) saves the integer on the stack in the variable :.

    Yes, even that is valid identifier. As with {, there's no way to retrieve the stored value.

  • ( decrements the integer on the stack, yielding the number of left halves.

  • }, since it is unmatched and terminates execution immediately.

    This is an undocumented feature. I call them supercomments.

The remaining code is simply ignored.

\$\endgroup\$
  • \$\begingroup\$ It seems really weird to have an unmatched } in the 2nd half of your code in a mirror competition. \$\endgroup\$ – ballesta25 May 28 '15 at 20:19
  • \$\begingroup\$ It's a common trick in palindrome variants. In "\""/", the fourth double quote would be unmatched as well since the second one has been escaped. \$\endgroup\$ – Dennis May 28 '15 at 20:35
27
\$\begingroup\$

Z80 Machine Code, 8 6 bytes*

<8ww8> * Assumes certain conditions by entering from Amstrad BASIC

<   INC A         // A=A+1
8w  JR C, #77     ## C is unset unless A has overflowed, does nothing

w   LD (HL), A    // Saves A to memory location in HL (randomly initialised)
8>  JR C, #3E     ## C is still unset, does nothing

A is initially 0 when entered from BASIC. It increments A n times, then writes it n times to the same memory location (which is set to a slightly random location by BASIC)! The JR Jump Relative operation never does anything since the C flag is always unset, so is used to "comment out" the following byte! This version is slightly cheating by assuming certain entry conditions, namely entering from BASIC guarantees that A is always 0. The location of (HL) is not guaranteed to be safe, and in fact, is probably a dangerous location. The below code is much more robust which is why it's so much longer.

Z80 Machine Code, 30 bytes

As ASCII: o!.ww.!>A=o>{))((}<o=A<!.ww.!o

Basically, the first half guarantees creation of a zero value and the second half increments it and writes it to memory. In the expanded version below ## denotes code that serves no purpose in its half of the mirror.

o   LD L, A       ## 
!.w LD HL, #772E  // Load specific address to not corrupt random memory!
w   LD (HL), A    ## Save random contents of A to memory
.!  LD L, #21     ## 
>A  LD A, #41     // A=#41
=   DEC A         // A=#40
o   LD L, A       // L=#40
>{  LD A, #7B     ## 
)   ADD HL, HL    // HL=#EE80
)   ADD HL, HL    // HL=#DD00. L=#00 at this point

((  JR Z, #28     ## 
}   LD A, L       // A=L
<   INC A         // A=L+1
o   LD L, A       // L=L+1
=   DEC A         // A=L
A   LD B, C       ## 
<   INC A         // A=L+1
!.w LD HL, #772E  // Load address back into HL
w   LD (HL), A    // Save contents of A to memory
.!  LD L, #21     ## 
o   LD L, A       // L=A

Breakdown of allowed instructions:

n   op    description
--  ----  -----------
28  LD    LoaD 8-bit or 16-bit register
3   DEC   DECrement 8-bit or 16-bit register
1   INC   INCrement 8-bit or 16-bit register
1   ADD   ADD 8-bit or 16-bit register

Available but useless instructions:
3   JR    Jump Relative to signed 8-bit offset
1   DAA   Decimal Adjust Accumulator (treats the A register as two decimal digits
          instead of two hexadecimal digits and adjusts it if necessary)
1   CPL   1s ComPLement A
1   HALT  HALT the CPU until an interrupt is received

Out of the 39 instructions allowed, 28 are load operations (the block from 0x40 to 0x7F are all single byte LD instructions), most of which are of no help here! The only load to memory instruction still allowed is LD (HL), A which means I have to store the value in A. Since A is the only register left with an allowed INC instruction this is actually quite handy!

I can't load A with 0x00 to start with because ASCII 0x00 is not an allowed character! All the available values are far from 0 and all mathematical and logical instructions have been disallowed! Except... I can still do ADD HL, HL, add 16-bit HL to itself! Apart from directly loading values (no use here!), INCrementing A and DECrementing A, L or HL this is the only way I have of changing the value of a register! There is actually one specialised instruction that could be helpful in the first half but a pain to work around in the second half, and a ones-complement instruction that is almost useless here and would just take up space.

So, I found the closest value to 0 I could: 0x41. How is that close to 0? In binary it is 0x01000001. So I decrement it, load it into L and do ADD HL, HL twice! L is now zero, which I load back into A! Unfortunately, the ASCII code for ADD HL, HL is ) so I now need to use ( twice. Fortunately, ( is JR Z, e, where e is the next byte. So it gobbles up the second byte and I just need to make sure it doesn't do anything by being careful with the Z flag! The last instruction to affect the Z flag was DEC A (counter-intuitively, ADD HL, HL doesn't change it) and since I know that A was 0x40 at that point it's guaranteed that Z is not set.

The first instruction in the second half JR Z, #28 will do nothing the first 255 times because the Z flag can only be set if A has overflowed from 255 to 0. After that the output will be wrong, however since it's only saving 8-bits values anyway that shouldn't matter. The code shouldn't be expanded more than 255 times.

The code has to be executed as a snippet since all available ways of returning cleanly have been disallowed. All the RETurn instructions are above 0x80 and the few Jump operations allowed can only jump to a positive offset, because all 8-bit negative values have been disallowed too!

\$\endgroup\$
  • 6
    \$\begingroup\$ WHAT. WHAT IS THIS. \$\endgroup\$ – cloudfeet May 29 '15 at 16:28
  • 4
    \$\begingroup\$ Now I've seen this answer, using GolfScript/J/etc. just seems like cheating. :p \$\endgroup\$ – cloudfeet May 29 '15 at 16:29
  • \$\begingroup\$ Are there Z80-compatible processors with a 16-bit A register? I'm asking since the question requires the code has to work for N = 32767. \$\endgroup\$ – Dennis May 29 '15 at 17:12
  • 1
    \$\begingroup\$ @Dennis For a program to work for N = 32767 it must be at least 2 x 32767 or 65534 bytes long. The Z80 can only address 65536 bytes of memory, making this an impossible task since I don't believe I can make the program smaller than 6 bytes! The A register is always 8 bits, otherwise the processor would not be compatible with the Z80. I would say that given enough memory and computing power has been covered off here! \$\endgroup\$ – CJ Dennis May 30 '15 at 2:22
  • 1
    \$\begingroup\$ @Dennis You understand why no Z80 compatible processor will have an A register of anything other than 8 bits? Changing it to 16-bits would break code relying on 255+1=0 for example. You would have to invent a CPU, lets call it the Z160, that uses a default 16-bit register but still uses the same 8-bit instruction set from the Z80. Weird! \$\endgroup\$ – CJ Dennis May 31 '15 at 2:01
19
\$\begingroup\$

J, 16 14 bytes

(_=_)]++[(_=_)

Usages:

   (_=_)]++[(_=_)
1
   (_=_)]+(_=_)]++[(_=_)+[(_=_)
2
   (_=_)]+(_=_)]+(_=_)]++[(_=_)+[(_=_)+[(_=_)
3

Explanation:

  • J evaluates from right to left.

  • (_=_) is inf equals inf which is true, has a value of 1, so the expression becomes 1+]...[+1. ((8=8) would also work but this looks cooler. :))

  • [ and ] return their the left and right arguments respectively if they have 2 arguments. If they get only 1 they return that.

  • + adds the 2 arguments. If it gets only 1 it returns that.

Now let's evaluate a level 3 expression (going from right to left):

(_=_)]+(_=_)]++[(_=_)+[(_=_)  NB. (_=_) is 1

1]+1]+1]++[1+[1+[1  NB. unary [, binary +

1]+1]+1]++[1+[2  NB. unary [, binary +

1]+1]+1]++[3  NB. unary [, unary +

1]+1]+1]+3  NB. unary +, binary ]

1]+1]+3  NB. unary +, binary ]

1]+3  NB. unary +, binary ]

3

As we see the right half of the 1's gets added and the left side of the 1's gets omitted resulting in the desired integer N, the mirror level.

Try it online here.

\$\endgroup\$
12
\$\begingroup\$

Haskell, 42 bytes

(8-8)-(-(8-8)^(8-8))--((8-8)^(8-8)-)-(8-8)

Luckily a line comment in Haskell (-> --) is mirrorable and half of it (-> -) is a valid function. The rest is some math to get the numbers 0 and 1. Basically we have (0)-(-1) with a comment for N=0 and prepend (0)-(-1)- in each step.

If floating point numbers are allowed for output, we can build 1 from 8/8 and get by with 26 bytes:

Haskell, 26 bytes

(8-8)-(-8/8)--(8\8-)-(8-8)

Outputs 1.0, 2.0, etc.

\$\endgroup\$
  • 2
    \$\begingroup\$ This is technically invalid since it must be a full program. \$\endgroup\$ – Calvin's Hobbies May 29 '15 at 12:48
  • \$\begingroup\$ @Calvin'sHobbies: I see. Do we have consensus on the minimum requirements for a full program? I've searched meta, but only found a discussion about functions, not programs. Depending on the definition of a full program I might be able to fix my solution. \$\endgroup\$ – nimi May 29 '15 at 15:05
  • \$\begingroup\$ I'd call it a full program if you can save it in a file program.hs and then run $ runhaskell program.hs from the command line and see the output. I don't know Haskell so I can't say exactly what needs to change. \$\endgroup\$ – Calvin's Hobbies May 29 '15 at 15:13
  • 2
    \$\begingroup\$ @Calvin'sHobbies: runhaskell is a shell script that sets up some environment and finally calls ghc, the Haskell compiler. You can run my code directly with ghc: ghc -e "(8-8)-(-8/8)--(8\8-)-(8-8)". This launches ghc which evaluates the code provided as an argument, prints the result and exits. No REPL, no interaction. Of course this would add +1 to the byte count for -e. \$\endgroup\$ – nimi May 29 '15 at 15:26
  • \$\begingroup\$ @nimi: -e doesn't contribute to the score in this case. We don't count bytes for perl -E or gcc -std=c99 either. \$\endgroup\$ – Dennis May 29 '15 at 16:57
11
\$\begingroup\$

CJam, 14 bytes

]X+:+OooO+:+X[

Try it online in the CJam interpreter: N = 0, N = 1, N = 2, N = 3, N = 41

Note that this code finishes with an error message. Using the Java interpreter, that error message can be suppressed by closing or redirecting STDERR.1

How it works

In the left halves, the following happens:

  • ] wraps the entire stack in an array.

  • X appends 1 to that array.

  • :+ computes the sum of all array elements.

  • Oo prints the contents of empty array (i.e., nothing).

In the first right half, the following happens:

  • o prints the integer on the stack, which is the desired output.

  • O+ attempts to append an empty array to the topmost item of the stack.

    However, the stack was empty before pushing O. This fails and terminates the execution of the program.

The remaining code is simply ignored.

1According to the meta poll Should submissions be allowed to exit with an error?, this is allowed.

\$\endgroup\$
  • \$\begingroup\$ I'd be skeptical on accepting this due to the error thing, but since its not winning I'm not too concerned. \$\endgroup\$ – Calvin's Hobbies May 28 '15 at 20:41
  • \$\begingroup\$ Tasks like this one are suprisingly hard in CJam, considering that it is a golfing language. Even a syntax error (e.g., an undefined operator) in a unexecuted block will prevent the entire program from executing. I'm still trying to get rid of the error. \$\endgroup\$ – Dennis May 28 '15 at 21:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.