30
\$\begingroup\$

Here is a simple ASCII art ruby:

  ___
 /\_/\
/_/ \_\
\ \_/ /
 \/_\/

As a jeweler for the ASCII Gemstone Corporation, your job is inspect the newly acquired rubies and leave a note about any defects that you find.

Luckily, only 12 types of defects are possible, and your supplier guarantees that no ruby will have more than one defect.

The 12 defects correspond to the replacement of one of the 12 inner _, /, or \ characters of the ruby with a space character (). The outer perimeter of a ruby never has defects.

The defects are numbered according to which inner character has a space in its place:

defect numbers

So a ruby with defect 1 looks like this:

  ___
 /\_/\
/_/  _\
\ \_/ /
 \/_\/

A ruby with defect 11 looks like this:

  ___
 /\_/\
/_/ \_\
\ \_/ /
 \ _\/

It's the same idea for all other defects.

Challenge

Write a program or function that takes in the string of a single, potentially defective ruby. The defect number should be printed or returned. The defect number is 0 if there is no defect.

Take input from a text file, stdin, or a string function argument. Return the defect number or print it to stdout.

You may assume that the ruby has a trailing newline. You may not assume that it has any trailing spaces or leading newlines.

The shortest code in bytes wins. (Handy byte counter.)

Test Cases

The 13 exact types of rubies, followed directly by their expected output:

  ___
 /\_/\
/_/ \_\
\ \_/ /
 \/_\/
0
  ___
 /\_/\
/_/  _\
\ \_/ /
 \/_\/
1
  ___
 /\ /\
/_/ \_\
\ \_/ /
 \/_\/
2
  ___
 /\_/\
/_  \_\
\ \_/ /
 \/_\/
3
  ___
 /\_/\
/_/ \_\
\  _/ /
 \/_\/
4
  ___
 /\_/\
/_/ \_\
\ \ / /
 \/_\/
5
  ___
 /\_/\
/_/ \_\
\ \_  /
 \/_\/
6
  ___
 /\_/\
/_/ \ \
\ \_/ /
 \/_\/
7
  ___
 /\_ \
/_/ \_\
\ \_/ /
 \/_\/
8
  ___
 / _/\
/_/ \_\
\ \_/ /
 \/_\/
9
  ___
 /\_/\
/ / \_\
\ \_/ /
 \/_\/
10
  ___
 /\_/\
/_/ \_\
\ \_/ /
 \ _\/
11
  ___
 /\_/\
/_/ \_\
\ \_/ /
 \/_ /
12
\$\endgroup\$
  • \$\begingroup\$ To clarify, the ruby cannot have any trailing spaces, right ? \$\endgroup\$ – Optimizer Apr 7 '15 at 5:44
  • \$\begingroup\$ @Optimizer Correct \$\endgroup\$ – Calvin's Hobbies Apr 7 '15 at 6:02
  • \$\begingroup\$ @Calvin'sHobbies May we also assume the input does not have a trailing newline? \$\endgroup\$ – orlp Apr 7 '15 at 8:39
  • \$\begingroup\$ @orlp Yes. That's the whole point of may. \$\endgroup\$ – Calvin's Hobbies Apr 7 '15 at 9:19
  • \$\begingroup\$ The rubies are symmetrical. So shouldn't error #7 be the same as error #10, for example? \$\endgroup\$ – DavidC Apr 7 '15 at 23:18

12 Answers 12

13
\$\begingroup\$

CJam, 27 23 bytes

F7EC5ZV4DI8G6]qBb67%J%#

Convert base 11, take mod 67, take mod 19 of the result then find the index of what you have in the array

[15, 7, 14, 12, 5, 3, 0, 4, 13, 18, 8, 16, 6]

Magic!

Try it online.

\$\endgroup\$
34
\$\begingroup\$

Ruby 2.0, 69 bytes

#!ruby -Kn0rdigest
p'×ñF<ìX‚ɲŸ_'.index Digest::MD5.digest(gets)[0]

Hexdump (to faithfully show the binary data in the string):

00000000  23 21 72 75 62 79 20 2d  4b 6e 30 72 64 69 67 65  |#!ruby -Kn0rdige|
00000010  73 74 0a 70 27 d7 f1 46  3c 1f ec 58 82 c9 b2 9f  |st.p'..F<..X....|
00000020  5f 02 27 2e 69 6e 64 65  78 20 44 69 67 65 73 74  |_.'.index Digest|
00000030  3a 3a 4d 44 35 2e 64 69  67 65 73 74 28 67 65 74  |::MD5.digest(get|
00000040  73 29 5b 30 5d                                    |s)[0]|

Explanation:

  1. The -Kn option reads the source file as ASCII-8BIT (binary).
  2. The -0 option allows gets to read in the whole input (and not just one line).
  3. The -rdigest option loads the digest module, which provides Digest::MD5.
  4. The code then does an MD5 of the input, takes the first byte of the digest, and gets its index in the given binary string.
\$\endgroup\$
  • \$\begingroup\$ Lucky that MD5 is unique on first char itself \$\endgroup\$ – Optimizer Apr 7 '15 at 5:52
  • 15
    \$\begingroup\$ No luck is required. Each byte has 256 possibilities, so the first byte being different for 13 hashes isn't that unusual. But if they collided for any reason, I'd just use the second byte of the hash. \$\endgroup\$ – Chris Jester-Young Apr 7 '15 at 5:53
  • 14
    \$\begingroup\$ Write a Ruby inspector in Ruby. Naturally! \$\endgroup\$ – Mast Apr 7 '15 at 14:39
  • \$\begingroup\$ Next challenge: Inspect this post itself \$\endgroup\$ – Redwolf Programs May 7 at 15:57
7
\$\begingroup\$

Julia, 90 59 bytes

Definitely not the shortest, but the fair maiden Julia takes great care in the inspection of the royal rubies.

s->search(s[vec([18 10 16 24 25 26 19 11 9 15 32 34])],' ')

This creates a lambda function which accepts a string s and returns the corresponding ruby defect number. To call it, give it a name, e.g. f=s->....

Ungolfed + explanation:

function f(s)
    # Strings can be indexed like arrays, so we can define d to
    # be a vector of indices corresponding to potential defect
    # locations

    d = vec([18 10 16 24 25 26 19 11 9 15 32 34])

    # Check the specified locations for defects, returning the
    # defect number as the index where a space was found and
    # was not expected. If no spaces are found, 0 is returned.

    search(s[d], ' ')
end

Examples:

julia> f("  ___
 /\\ /\\
/_/ \\_\\
\\ \\_/ \/
 \\/_\\/")
2

julia> f("  ___
 /\\_/\\
/_/ \\_\\
\\ \\_/ \/
 \\/_\\/")
0

Note that backslashes have to be escaped in the input. I confirmed with @Calvin'sHobbies that it's okay.

Let me know if you have any questions or suggestions!


Edit: Saved 31 bytes with help from Andrew Piliser!

\$\endgroup\$
  • \$\begingroup\$ You can get rid of the for loop with search and array indexing. s->(d=reshape([18 10 16 24 25 26 19 11 9 15 32 34],12);search(s[d],' ')). I don't like the reshape, but I couldn't think of a shorter way to get a 1d array. \$\endgroup\$ – Andrew says Reinstate Monica Apr 7 '15 at 22:00
  • \$\begingroup\$ @AndrewPiliser: Thanks, I really appreciate your input! I edited to use your suggestion. Also, a shorter way than reshape() is to use vec(). :) \$\endgroup\$ – Alex A. Apr 7 '15 at 23:30
7
\$\begingroup\$

><> (Fish), 177 bytes

This is a long but unique solution. The program contains no arithmetic or branching apart from inserting input characters into fixed places in the code.

Notice that all the inspected ruby-building characters (/ \ _) can be "mirrors" in the ><> code that change the direction of the instruction pointer (IP).

We can use these input characters to build a maze from them with the code-modifying instruction p and at every exit (which created by a missing mirror in the input) we can print the corresponding number.

iiiiiiiii19pi1cpi18piiii2bpi27pii28pi3apiiiii37pi49pi36piiiii00pi45pii46p

    ;
   ;n
  ;nb
 ;n6S    0n;
 n3SB   cn;
 8SB!  4n;
 SB!  1n;
>B! U9n;
 ! U5
  U7n
 Uan;
 2n;
 n;
 ;

The S B U letters are the ones changed to / \ _ respectively. If the input is a full ruby the final code becomes:

\iiiiiiii19pi1cpi18piiii2bpi27pii28pi3apiiiii37pi49pi36piiiii00pi45pii46p

    ;
   ;n
  ;nb
 ;n6/    0n;
 n3/\   cn;
 8/\!  4n;
 /\!  1n;
>\! _9n;
 ! _5
  _7n
 _an;
 2n;
 n;
 ;

You can try out the program with this great online visual interpreter. As you can't input newlines there you have to use some dummy characters instead so you can input a full ruby as e.g. SS___LS/\_/\L/_/S\_\L\S\_/S/LS\/_\/. (Spaces also changed to S's because of markdown.)

\$\endgroup\$
5
\$\begingroup\$

CJam, 41 31 29 28 bytes

"-RI)11a!"q103b1e3%A/c#

As usual, for non printable characters, follow this link.

Try it online here

Explanation soon


Previous approach:

Pretty sure this can be reduced by changing the digits/conversion logic. But here goes the first attempt:

"<KJ[]\"O=":iqN-"/\\_ "4,er4b1e3%A/#

As usual, use this link for unprintable characters.

The logic is pretty simple

  • "Hash for each defect":i - This gets me the hash per defect as the index
  • qN-"/\\_ "4,er - this converts the characters to numbers
  • 4b1e3%A/ - this is the unique number in the base converted number
  • # Then I simply find the index of the unique number in the hash

Try it online here

\$\endgroup\$
  • \$\begingroup\$ So close, I'm 1 character shorter than you! \$\endgroup\$ – orlp Apr 7 '15 at 9:59
  • \$\begingroup\$ Oh, I already have 28. Was too busy to update \$\endgroup\$ – Optimizer Apr 7 '15 at 10:02
  • \$\begingroup\$ I think my answer is optimal for Pyth. Pyth really needs a hash function (.h right now is useless because it uses the built-in unreliable and bad hash()), until then I can't do better. \$\endgroup\$ – orlp Apr 7 '15 at 10:06
4
\$\begingroup\$

Slip, 123 108 + 3 = 111 bytes

^6 (`\\`_.?<?.?[ _]?|`_(`\.?(<.?|>)|`/.?.?>.?.?).?)| `_(`\.?<.?>?.?.?|`/(.?>.?.?.?|<`_))| `/\`_.?(.<.?>?.?)?

Run with the n and o flags, i.e.

py -3 slip.py regex.txt input.txt no

Alternatively, try it online.


Slip is a regex-like language which was made as part of the 2D pattern matching challenge. Slip can detect the location of a defect with the p position flag via the following program:

^6? `_[/\]|( `/|^6 `\)\`_

which looks for one of the following patterns (here S denotes here the match starts):

S_/    S_\    /_S    \_S    S/      _
                              _      \S

Try it online — coordinates are outputted as an (x, y) pair. Everything reads like a normal regex, except that:

  • ` is used for escaping,
  • <> turn the match pointer left/right respectively,
  • ^6 sets the match pointer to face leftward, and
  • \ slips the match pointer orthogonally to the right (e.g. if the pointer's facing rightward then it goes down a row)

But unfortunately, what we need is a single number from 0 to 12 saying which defect was detected, not where it was detected. Slip only has one method of outputting a single number — the n flag which outputs the number of matches found.

So to do this we expand the above regex to match the correct number of times for each defect, with the help of the o overlapping match mode. Broken down, the components are:

1 11:    `_`\.?<.?>?.?.?
2 10:    `/\`_.?(.<.?>?.?)?
4 9:     `_`/(.?>.?.?.?|<`_)
3 12:   ^6 `_`/.?.?>.?.?.?
5 7:    ^6 `\\`_.?<?.?[ _]?
6 8:    ^6 `_`\.?(<.?|>).?

Yes, that's an exccessive use of ? to get the numbers right :P

\$\endgroup\$
  • \$\begingroup\$ Haha, amazing. I need to add more types of output to my language. \$\endgroup\$ – BMac Apr 8 '15 at 14:52
4
\$\begingroup\$

JavaScript (ES6), 67 72

Simply looks for blanks in the given 12 locations

Edit Saved 5 bytes, thx @apsillers

F=b=>[..."0h9fnopia8evx"].map((v,i)=>b[parseInt(v,34)]>' '?0:d=i)|d

Test In Firefox / FireBug console

x='  ___\n /\\_/\\\n/_/ \\_\\\n\\ \\_/ /\n \\/_\\/' // no defects
;[...x].forEach((c,p,a)=>{
  a[p]=' ' // put a blank
  y=a.join('') // rebuild a string
  d=F(y) // check
  if (d) console.log('\n'+y+'\n'+d) // if defect, output
  a[p]=c // remove the blamk
})

Output

  ___
 / _/\
/_/ \_\
\ \_/ /
 \/_\/
9

  ___
 /\ /\
/_/ \_\
\ \_/ /
 \/_\/
2

  ___
 /\_ \
/_/ \_\
\ \_/ /
 \/_\/
8

  ___
 /\_/\
/ / \_\
\ \_/ /
 \/_\/
10

  ___
 /\_/\
/_  \_\
\ \_/ /
 \/_\/
3

  ___
 /\_/\
/_/  _\
\ \_/ /
 \/_\/
1

  ___
 /\_/\
/_/ \ \
\ \_/ /
 \/_\/
7

  ___
 /\_/\
/_/ \_\
\  _/ /
 \/_\/
4

  ___
 /\_/\
/_/ \_\
\ \ / /
 \/_\/
5

  ___
 /\_/\
/_/ \_\
\ \_  /
 \/_\/
6

  ___
 /\_/\
/_/ \_\
\ \_/ /
 \ _\/
11

  ___
 /\_/\
/_/ \_\
\ \_/ /
 \/_ /
12
\$\endgroup\$
  • \$\begingroup\$ @apsillers it's good and even better, thanks. As the input string always start with ' ',the leading 0 force the initialization of d to i at first loop, so the 'd=0' can be remove. \$\endgroup\$ – edc65 Apr 7 '15 at 16:50
2
\$\begingroup\$

C, 98 84 bytes

g(char*b){char*c="/'-5670(&,=?",*a=c;for(;*c&&!(*b=b[*c++-30]-32?0:c-a););return*b;}

UPDATE: A tad bit more clever about the string, and fixed an issue with non-defected rubies.

Unraveled:

g(char*b){
    char*c="/'-5670(&,=?",*a=c;
    for(;*c&&!(*b=b[*c++-30]-32?0:c-a);)
        ;
    return*b;
}

Quite straightforward, and just under 100 bytes.

For testing:

#include "stdio.h"
int main() {
    char b[100];
    scanf("%35c", b);
    printf("%d\n", g(b));
    return 0;
}

Input to STDIN.

How it works

Each defect in the ruby is located at a different character. This list shows where each defect occurs in the input string:

Defect 1: 17
Defect 2: 9
Defect 3: 15
Defect 4: 23
Defect 5: 24
Defect 6: 25
Defect 7: 18
Defect 8: 10
Defect 9: 8
Defect 10: 14
Defect 11: 31
Defect 12: 33

Since making an array of {17,9,15,23,24,25,18,10,8,14,31,33} costs a lot of bytes, we find a shorter way to create this list. Observe that adding 30 to each number results in a list of integers that could be represented as printable ASCII characters. This list is as follows: "/'-5670(&,=?". Thus, we can set a character array (in the code, c) to this string, and simply subtract 30 from every value we retrieve from this list to get our original array of integers. We define a to be equal to c for keeping track of how far along the list we have gotten. The only thing left in the code is the for loop. It checks to make sure we have not hit the end of c yet, and then checks if the character of b at the current c is a space(ASCII 32). If it is, we set the first, unused element of b to the defect number and return it.

\$\endgroup\$
2
\$\begingroup\$

Python 2, 146 88 86 71 bytes

The function f tests each segment location and returns the index of the defect segment. A test on the first byte in the input string ensures that we return 0 if no defects are found.

We now pack the segment offsets into a compact string and use ord() to recover them:

f=lambda s:sum(n*(s[ord('ARJPXYZSKIO`b'[n])-65]<'!')for n in range(13))

Testing with a perfect ruby:

f('  ___\n /\\_/\\\n/_/ \\_\\\n\\ \\_/ /\n \\/_\\/')
0

Testing with segment 2 replaced by a space:

f('  ___\n /\\ /\\\n/_/ \\_\\\n\\ \\_/ /\n \\/_\\/')
2

EDIT: Thanks to @xnor for the nice sum(n*bool for n in...) technique.

EDIT2: Thanks to @Sp3000 for extra golfing tips.

\$\endgroup\$
  • 2
    \$\begingroup\$ I think you can save chars by using an indicator-sum sum(n*(s[...]==' ')for ...). \$\endgroup\$ – xnor Apr 7 '15 at 22:27
  • 1
    \$\begingroup\$ Considering that the replaced chars are all after space, you can probably do something like <'!' instead of ==' ' for a byte. You can also generate the list with map(ord, ...), but I'm not sure how you feel about unprintables :) \$\endgroup\$ – Sp3000 Apr 8 '15 at 6:23
1
\$\begingroup\$

Pyth, 35 31 28 bytes

hx"*6#,54@"C%imCds.zT67

Requires a patched Pyth, the current latest version of Pyth has a bug in .z that strips off trailing characters.

This version doesn't use a hash function, it abuses the base conversion function in Pyth to compute a very stupid, but working hash. Then we convert that hash to a character and look its index up in a string.

The answer contains unprintable characters, use this Python3 code to generate the program accurately on your machine:

garbage = [42, 22, 54, 35, 44, 28, 31, 53, 52, 64, 16, 11]
prg = 'hx"' + "".join(chr(c) for c in garbage) +'"C%imCds.zT67'
open("golf_gen.pyth", "w").write(prg)
print(len(prg))
\$\endgroup\$
1
\$\begingroup\$

Haskell, 73 bytes

f l=last[x|x<-[0..12],l!!([0,17,9,15,23,24,25,18,10,8,14,31,33]!!x)==' ']

Same strategy as many other solutions: looking for spaces at the given locations. The lookup returns a list of indices from which I take the last element, because there is always a hit for index 0.

\$\endgroup\$
0
\$\begingroup\$

05AB1E, 16 bytes

•W)Ì3ô;4(•₆вèðk>

Try it online or verify all test cases.

Explanation:

•W)Ì3ô;4(•        # Push compressed integer 2272064612422082397
          ₆в      # Converted to Base-36 as list: [17,9,15,23,24,25,18,10,8,14,31,33]
            è     # Index each into the (implicit) input-string
             ðk   # Get the 0-based index of the first space in the indexed characters
                  # (-1 if not found, which means the ruby had no defects)
               >  # And increase it by 1 (which is output implicitly as result)

See this 05AB1E tip of mine (sections How to compress large integers? and How to compress integer lists?) to understand why •W)Ì3ô;4(• is 2272064612422082397 and •W)Ì3ô;4(•₆в is [17,9,15,23,24,25,18,10,8,14,31,33].

\$\endgroup\$

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.