Ayna Seviyesini Çıkaran Bir Program Yaz


31

Yazdırılabilir 95 ASCII karakteri vardır:

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

In Consolas yazı (Stack Exchange kodu blok varsayılan), karakterlerin bazı simetri dikey ekseni etrafında aynalar vardır:

  • Bu karakter çiftleri birbirlerinin aynasıdır: () [] {} <> /\
  • Bu karakterler kendilerinin aynasıdır: ! "'*+-.8:=AHIMOTUVWXY^_ovwx| (Boşluğun bir olduğuna dikkat edin.)
  • Bunların aynaları yok: #$%&,012345679;?@BCDEFGJKLNPQRSZ`abcdefghijklmnpqrstuyz~

( i, l,0 , #, Ve muhtemelen diğer karakterler kendi aynalar bazı yazı tipleri vardır ama biz Consolas şekiller sadık kalacağız.)

Bir dize, merkezi bir dikey simetri çizgisine sahip olacak şekilde düzenlenmiş, yalnızca 39 ayna karakteriyle yapılmışsa, kendi başına bir ayna olduğu söylenir . Yani](A--A)[ kendi bir aynasıdır ama ](A--A(]değildir.

Aynası olan tek satırlık bir çift program yazınız. Sol yarısının N kopyası hazırlandı ve sağ yarısının N kopyası eklendiğinde N + 1 çıktısı alınmalı. N, negatif olmayan bir tam sayıdır.

Örneğin, program ](A--A)[(sol yarı:, ](A-sağ yarı:) ise -A)[, o zaman:

  • Koşu ](A--A)[çıkmalı 1. (N = 0)
  • Koşu ](A-](A--A)[-A)[çıkmalı 2. (N = 1)
  • Koşu ](A-](A-](A--A)[-A)[-A)[çıkmalı 3. (N = 2)
  • Koşu ](A-](A-](A-](A--A)[-A)[-A)[-A)[çıkmalı 4. (N = 3)
  • . . .
  • Koşu ](A-](A-](A-](A-](A-](A-](A-](A-](A-](A--A)[-A)[-A)[-A)[-A)[-A)[-A)[-A)[-A)[-A)[çıkmalı 10. (N = 9)
  • vb.

kurallar

  • Stdout'a veya dilinizin en yakın alternatifi. İsteğe bağlı bir takip hattı olabilir. Giriş yapılmamalıdır.
  • Proses teorik olarak N 15 / -1 veya daha yüksek N için yeterli bellek ve bilgi işlem gücü verilmiş olarak çalışmalıdır.
  • Tam bir program gereklidir, yalnızca bir REPL komutu değil.

Bayt cinsinden en kısa ilk program (N = 0 case) kazanır.


Bazı yazı tiplerinde #de kendi tercihine göre, fakat haklısın, konsolosunda değil.
SuperJedi224

1
Repls kullanmak izin verilir? Başka bir deyişle: tam bir geçerli program yazmalı mıyız yoksa bir ifade yeterli mi? Ghci'de çalışırken geçerli olacak, ancak geçerli bir tam program olmayan Haskell girişini düşünüyorum.
Bakuriu

@Bakuriu Tam bir program gereklidir. Haskell cevabı geçersiz.
Calvin'in Hobileri

4
Neden 'b' ve 'd' birbirinin aynası değil?
Planımı

1
@ThijsterHaar Aslında bunu düşünmedim ama onların Consolas şekilleri biraz farklı gibi görünüyor, üzgünüm: P
Calvin'in Hobileri

Yanıtlar:


20

Pip, 12 8 4 bayt

Şimdi% 66 daha az bayt ile!

x++x
  • xönceden tanımlanmış bir değişkendir "". Sayısal bağlamda, bu olur 0.
  • İlk yarı finali söyler +, formun bir ifadesini yapar x+x+...+x. Bu hiçbir şey yapmayan geçerli bir ifadedir.
  • 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


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.
Dennis

@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.
DLosc

The recursion limit is easily adjustable in Python, isn't it?
Dennis

@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.
DLosc

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.
Dennis

34

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.


It seems really weird to have an unmatched } in the 2nd half of your code in a mirror competition.
ballesta25

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.
Dennis

27

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!


6
WHAT. WHAT IS THIS.
cloudfeet

4
Now I've seen this answer, using GolfScript/J/etc. just seems like cheating. :p
cloudfeet

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.
Dennis

1
@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!
CJ Dennis

1
@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!
CJ Dennis

19

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.


12

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.


2
This is technically invalid since it must be a full program.
Calvin's Hobbies

@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.
nimi

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.
Calvin's Hobbies

2
@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.
nimi

@nimi: -e doesn't contribute to the score in this case. We don't count bytes for perl -E or gcc -std=c99 either.
Dennis

11

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.


I'd be skeptical on accepting this due to the error thing, but since its not winning I'm not too concerned.
Calvin's Hobbies

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.
Dennis
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.