Thinking outside the box - Am I doing it right?

59

6

I keep hearing that thinking outside the box is a goal worth achieving, but how can I tell if I'm successfully doing it?

To solve this dilemma I have already wrote a Brainwave-to-ASCII-translator which in theory should produce outputs like

                    #
   +------------+   #
   |   thinking |   #
   |            |   #
   +------------+   #
                    #

or

                   #
 +------+          #
 |      | thinking #
 |      |          #
 |      |          #
 +------+          #
                   #

which make it fairly easy to tell whether one is thinking outside the box or not. (The # are not part of the output and represent new lines.)

However, due to a bug sometimes only a smaller section of the output is returned:

   |         |         #
   +---------+         #
    thinking           #

        #
       +#
       |#
inking |#

    #
    #

The Task

Please help me to automatically classify the Brainwave-to-ASCII-translator output by writing a program or function which reads an ascii-reprensentation and returns whether thinking is in the box, outside of it or it could not tell from the input.

Input

A set of same-length strings either as a list or delimited by newlines containing

  • the string thinking or valid pre- or suffixes thereof
  • the characters +-| forming a rectangular box or valid parts of it
  • spaces
  • NO #, those are only included in the challenge to mark the ends of the input lines.

Output

  • a truthy value if thinking is outside the box
  • a falsy value if thinking is in the box
  • a distinct third maybe value if it can not be determined from the input whether thinking is in the box or not

Examples

Truthy:

                   #
 +------+          #
 |      | thinking #
 |      |          #
 |      |          #
 +------+          #
                   #

   |         |         #
   +---------+         #
    thinking           #

        #
       +#
       |#
       |#
inking |#

thinking #
-------+ #

 ++ # (thinking is not in the box, so it must be outside)
 ++ # (this is also the smallest possible box)

+ #
 t#

+----+# (The box is not wide enough to contain "thinking")

---# (The box is not high enough to contain "thinking")
---#

As string input:

"                   \n +------+          \n |      | thinking \n |      |          \n |      |          \n +------+          \n                   "
"   |         |         \n   +---------+         \n    thinking           "
"        \n       +\n       |\n       |\ninking |"
"thinking \n-------+ "
" ++ \n ++ "
"+ \n t"
"+----+"
"---\n---"
"g++"
"k\n+"

Falsy:

                    #
   +------------+   #
   |   thinking |   #
   |            |   #
   +------------+   #
                    #

  +---------------#
  |               #
  |               #
  |   thinking    #

      | #
king  | #
------+ #

+---#
|thi#
+---#

-#
n#
-#

As string input:

"                    \n   +------------+   \n   |   thinking |   \n   |            |   \n   +------------+   \n                    "
"  +---------------\n  |               \n  |               \n  |   thinking    "
"      | \nking  | \n------+ "
"+---\n|thi\n+---"
"-\nn\n-"

Maybe:

thinking#

g|#

think#
-----#

|          |# (box large enough to possibly contain the string)
|          |#

   +--#
   |  #

# (empty input)

As string input:

"thinking"
"g|"
"|t"
"-\ni"
"h\n-"
"think\n-----"
"|          |\n|          |"
"   +--\n   |  "
""

Rules

  • This is , so try to use as few bytes as possible.
  • The maybe value can be chosen freely as long as it's different from the truthy/falsy value and is the same for all maybe-inputs. It can also be an error.
  • You can assume that the input is always valid (eg. contains no other chars than +-ghiknt|, no more than one box, ...).

Laikoni

Posted 2016-12-04T22:36:28.433

Reputation: 23 676

Idea for truthy test case: +\n+, box too small for a word – Destructible Lemon – 2016-12-04T22:42:47.693

@DestructibleWatermelon Thanks, I'll add a similar test case. – Laikoni – 2016-12-04T22:48:25.823

You don't have the most basic cases in your test cases. Mind including the entire box with thinking in it, and the entire box with the entire word thinking outside it? – ATaco – 2016-12-05T00:33:53.953

Is there a possiblity of the word overlapping the box(thinking on the box)? – Mukul Kumar – 2016-12-05T03:40:43.360

@MukulKumar No, thinking and the box will never overlap. – Laikoni – 2016-12-05T09:13:48.557

@ATaco I included them in the string testcases now. – Laikoni – 2016-12-05T09:20:49.877

Must the box have at least one - or | on each side? Or can it look like +---+\n+---+, ++\n++ or even +? – feersum – 2016-12-05T10:51:18.177

@feersum The minimal box is "++\n++". – Laikoni – 2016-12-05T18:05:29.783

17This is a boundary case nightmare, jeez. – Magic Octopus Urn – 2016-12-07T22:04:23.710

Answers

11

Javascript (ES6), 274 263 bytes

f=(a,b=(b,c="")=>a=a.replace(b,c),c=b=>b.test(`,${a},`))=>(b(/\w+/,5),b(/\+/g,1),b(/\-/g,2),b(/\|/g,3),b(/\n/g,4),c(/[13][2 ]{0,7}[13]|[12] *4 *[12]/)||(b(/ /g),b(/43+(?=4)/g),!c(/353|24542|12+435|21453|35412|5342+1/)&&(!c(/^([^1]*|([^15]*1){1,2}[^15]*)$/)||-1)))

The function f returns true, false, or -1 as its "maybe" value. It should be called with one argument: the input. The other two parameters only exist to shorten the code.

Here's a less golfed version with comments:

var f = (input) => {
    var replace = (re, s) => input = input.replace(re, s);
    var test = re => re.test(input);

    /*
        Replace some "special" characters with ones that are shorter to put in a regex.
        "+" --> "1"
        "-" --> "2"
        "|" --> "3"
        "\n" --> ","
    */
    replace(/\+/g,1);
    replace(/\-/g,2);
    replace(/\|/g,3);
    replace(/\n/g,',');

    /*
        Shorten the word, if there is one, to just a single character "a".
    */
    replace(/[a-z]+/g,'a');

    /*
        Append a newline to the beginning and end of the input.
    */
    input = ','+input+',';

    /*
        Test the size of the box. These are the cases covered:
        /[13][2 ]{0,7}[13]/ : A horizontal edge or middle section has an inner width of fewer than 8 characters.
        /[12] *, *[12]/     : There are two horizontal edges in a row, with no space between.

        If either of these match, the word must be outside of the box. Return the truthy value (true).
    */
    if (test(/[13][2 ]{0,7}[13]|[12] *, *[12]/)) return true;

    /*
        Remove any spacing from the input. It it unnecessary from this point onwards.
    */
    replace(/ /g,'');

    /*
        Remove any lines with only vertical bars. These are also unnecessary.
    */
    replace(/,3+(?=,)/g,'');

    /*
        Edge / corner cases (heh). These are the cases covered:
        /3a3/    : two vertical bars with the word between.
        /2,a,2/  : two horizontal bars with the word between.
        /12+,3a/ : word inside the top left corner.
        /21,a3/  : word inside the top right corner.
        /3a,12/  : word inside the bottom left corner.
        /a3,2+1/ : word inside the bottom right corner.

        If any of these match, the word is inside the box. Return the falsy value (false).
    */
    if (test(/3a3|2,a,2|12+,3a|21,a3|3a,12|a3,2+1/)) return false;

    /*
        "Maybe" cases. These are the cases covered:
        /^[^1]*$/                : Input contains no corners, and may or may not contain a word.
        /^([^1a]*1){1,2}[^1a]*$/ : Input contains 1 or 2 corners, and no word.

        If either of these match, assuming the previous test cases have not been hit,
        we cannot tell if the word is inside or outside the box. Return the maybe value (-1).
    */
    if (test(/^([^1]*|([^1a]*1){1,2}[^1a]*)$/)) return -1;

    /*
        If none of the previous cases matched, the word must be outside of the box. Return the truthy value (true).
    */
    return true;
};

Had a lot of fun with this one. Thanks!

Edit: Saved 6 bytes thanks @L. Serné by modifying b to use a default argument, saving 3 bytes, and changing [a-z] to \w, saving 3 more bytes. Also saved 5 more bytes by making the word replacement non-global, saving 1 byte, and changing "a" to 5 and "," to 4, saving 4 bytes.

kyle1320

Posted 2016-12-04T22:36:28.433

Reputation: 171

Tried with console.log(f("input")). Seems to work. Great job on golfing this. – devRicher – 2017-01-02T10:58:37.450

Nice job on the answer. I tried answering this, and I got stuck halfway. I noticed 2 small byte-savers: change b=(b,c) to b=(b,c=""), and then you can remove the last argument of the two calls to b with an empty string as second argument, saving (2*3-3=) 3 bytes in total. Also, you can shorten the word regex from [a-z]+ to \w+ (do this before the other replaces, because this will also match digits) saving 3 more bytes. – Luke – 2017-01-02T16:27:48.000

Welcome to PPCG and nice first answer! – user41805 – 2017-01-02T20:20:00.633

Awarded bounty. Shortest answer. Great job, amazing answer. – devRicher – 2017-01-02T22:00:53.503

8

Python 2.7, 532 494 453 bytes

This one sure had a lot of special cases. My truthy and falsy values are the strings "True" and "False" respectively. My maybe value is an Index Error, as they're easy to trigger and one of my test cases triggers it if the input is an empty string, which is a maybe case anyway. I made use of regular expressions quite a bit.

I don't golf in python often, so I'm sure this could be golfed down more, but here's my code:

import re
def e(s):exit(str(s))
i=input()
T=1<2
F=2<1
a=len(i)+1
c=i.count('+')
s='[a-z]'
x=re.search
p=x(s,i)
k=x('\+.*'+s,i)
l=x(s+'.*\|',i)
r=x('\|.*'+s,i)
f=i.find('+')
g=i[f-1]=='-'and f>0
if x('\-.*\n.*\-',i):e(T)
if x('\+.{0,7}\+',i):e(T)
if c>1 and not p:i[a]
if c>3:e(not(r and l))
if c>0:
 if r and l:e(F)
 if g:
    if l:e(F)
    if p or k:e(T)
    i[a]
 if r or k:e(F)
 if p:e(T)
 i[a]
if x('-.*\s[a-z].*\s-',i):e(F)
if x('\|.*[a-z].*\|',i):e(F)
i[a]

In my golfed version, I display the True/False answer by calling exit(bool as string). Here's a commented version, in which the exit statements are replaced with return statements, and everything's been moved into a function:

import re
i=input()
T=True
F=False
def z():
    # some variables and shortcuts useful for testing

    # length of input +1. Used to throw an index out of bounds error on 'maybe'
    a=len(i)+1
    # c for i.Count()
    c=i.count
    # string used in regular expressions often
    s='[a-z]'
    # shorten regeX calls
    x=re.search
    # p is true is 'thinking' is Present on canvas
    p=x(s,i)
    # k is true if 'thinking' is Right of a 'Korner' (corner)
    k=x('\+.*'+s,i)
    # l is true if 'thinking' is Left of a pipe (|)
    l=x(s+'.*\|',i)
    # r is true if 'thinking' is right of a pipe
    r=x('\|.*'+s,i)
    # f is First corner (+) index
    f=i.find('+')

    # g is true if box is facing riGht (i.e. there is a dash before the first +)
    # for example, '---+' indicates the box faces right. if the + is the 0th
    # character, then the box must be facing left.
    # Note that this assignment also checks for the empty string 'maybe'
    # case, which is signalled by an IndexOutofBounds error
    # CASE 1: Empty Input
    # ex: ''
    g=i[f-1]=='-' and f>0

    # Begin testing all possible scenarios

    # CASE 2: Box is too short (vertically)
    # ex: ------
    #     ------
    if x('\-.*\n.*\-',i):return T

    # CASE 3: box is too short (horizontally)
    # ex: ||
    if x('\+.{0,7}\+',i):return T

    # CASE 4: box is not too short yet no corners (+) or text are present on canvas
    # ex:
    # |       |         --------
    # |       |   or
    # |       |         --------
    # this is a maybe case, so throw out of bounds error
    if c('+')>1 and not p:i[a]

    # CASE 5: Four corners visible (whole box visible)
    # ex: +---+
    #     | X |
    #     +---+
    # return false if text is both right of and left of pipes (i.e. in box)
    if c('+')>3:return not(r and l)

    # CASE 6: one or two corners visible
    # ex:
    # ----+        |    |          |
    #     |    or  +----+   or  ---+  etc
    # ----+
    # in this case, we idenify which way the box faces
    if c('+')>0:

        # CASE 6-A: Text is between two pipes
        # ex:
        #     |   X   |
        #     +-------+
        # if text is between pipes (box is extending from top or bottom of
        # canvas), then it is inside box
        if r and l:return F

        # CASE 6-B: Box faces right
        # if the box is riGht-facing, ex:
        # ----+
        #     |    or  ----+  etc
        # ----+            |
        if g:

            # CASE 6-B-a: Letters are left of a pipe or a + in a right facing box
            # ----+
            #  X  |   or  ----+
            # ----+         X |
            if l :return F

            # CASE 6-B-b: Letters are right of a pipe or + in a right facing box
            # ----+
            #     | X  or  ----+
            # ----+            | X
            if p or k:return T

            # CASE 6-B-c: right-facing otherwise
            # otherwise, it is a 'maybe' case
            # use an index out of bounds error to signal 'maybe'
            i[a]

        # CASE 6-C: Box faces left (or letters are on canvas yet not inside box)
        # ex:
        #   +----
        #   |        or   +---  or
        #   +----         |
        else:

            # CASE 6-C-a: Letters are right of a pipe or a + in a left facing box
            # if letters are right of pipe, they are inside box, ex:
            #   +----
            #   | X       or   +---  or X +---
            #   +----          | X        |
            if r or k:return F

            # CASE 6-C-b: Letters are left of a pipe in a left facing box
            # ex:
            #     +----
            #   X |        or     +---
            #     +----         X |

            # CASE 6-C-c: Letters are on canvas yet not left or right of
            # a pipe or a + (they must therefore be outside the box)
            # ex:
            #  |     |
            #  +-----+
            #     X
            if p:return T

            # CASE 6-C-d: text is not visible on canvas, and only part of the box is
            # ex:
            #  |     |
            #  +-----+
            #
            # this is a 'maybe' case, as text is off canvas
            # use an index out of bounds error to signal 'maybe'
            i[a]

    # CASE 7: No corners visible, nonempty input

    # CASE 7-A: No corners visible, letters sandwitched between dashes
    # ex:
    # -----
    #   X
    # -----
    # if there are no corners, yet letters are sandwitched between dashes,
    # then word is in box
    if x('-.*\s[a-z].*\s-',i):return F

    # CASE 7-B: No corners visible, letters sandwitched bewteen pipes
    # ex: |  X  |
    # in this case, word is inside box
    if x('\|.*[a-z].*\|',i):return F

    # If none of the above cases are met, it is a maybe, so throw the error
    i[a]

print z()

My solution assumes that the input is valid, i.e. 'Thinking' (or its substrings) are spelled correctly, there is only a single box, etc.

Edit: Saved 10 bytes thanks to @ais523's suggestion to change c to i.count('+'), 3 bytes thanks to @Pavel's suggestion of replacing True with 1<2 and False with 2>1, 23 bytes by removing an unneeded else block, and 2 bytes by removing some spaces.

Edit 2: Saved 36 bytes thanks to @Wheat Wizard who kindly pointed out that my 'tabs' were actually 5 spaces (D'oh!) and suggested some other improvements.

ren

Posted 2016-12-04T22:36:28.433

Reputation: 189

2Impressive. Someone actually did it. – devRicher – 2017-01-01T01:46:16.523

1I think i never changes, right? So you could probably save some bytes by storing i.count('+') in c rather than i.count, as you never call it with any argument but +. – None – 2017-01-01T02:16:20.080

1You can replace true and false with 1<2 and 2<1, right? – Pavel – 2017-01-01T10:17:07.403

1You don't have to carriage return and indent in your function definition. As far as I can tell you are using 4 spaces for indentation. You can indent to depth one using a single space and depth 2 with a single tab. – Post Rock Garf Hunter – 2017-01-02T06:13:11.360

@WheatWizard well this is embarassing... looks like Atom was converting tabs to 4 spaces. Thanks for the advice, it shaved off 36 bytes! – ren – 2017-01-02T08:15:20.420

I think there's a typo -- if c>1 and not p:i[a] should be if c<1 and not p:i[a]. This causes the case where the input contains a box large enough to store the word, but no word, to result in the maybe value rather than the truthy one. In fact, I think this case may be able to be removed entirely, since the maybe value is covered at the end, although I haven't tested it. – kyle1320 – 2017-01-02T17:04:55.433

8

Befunge, 535 bytes

This isn't pretty, and doesn't come close to competing with the existing answers, but it's the best I could achieve in Befunge.

Returns 1 if thinking outside the box, 0 if thinking inside the box, and -1 for maybe.

p10p20p130p140p150p9-:60p70p"~":80p90pvp8p04+1:g04p03:$p9g04+g9g\<<
0$$$$"xxxx"5p041p031p$_v#!-+55:_v#`0:~<p05+1g
v01g04$_v#*`\"|"\`"-"::<>0g\8\p"0"-!!40g1-\8\p:4-!:00g0`*!:00g\6\p40g\8\p00g!*4v
>p50g20p>8%:30g88+*+:3-v4v\-1g05!!*-"3"\-"4"::p\7\g05!!-3:+*+88g8g04:p00+g00*g0<
v!*-"3"\-"5"::p\6\g04!!<!>>7\p::"C"-\"3"-*!!50g\9\p"0"-!!50g1-\9\p:5-!:40g9g48*v
>!40g1-\6\p::"S"-\"3"-*!^>0#4g#p9#\g#94#\8#g*#0-#5!#p*#\5#70#\g#-*^#84g9g04:!*`<
>80g60g-8`90g70g-1`**+!:10g80g`60g10g`20g90g`70g20g`+++!!*\!-.@
^!g01***`"}"g09`"}"g08`g070`g060<

Try it online!

James Holderness

Posted 2016-12-04T22:36:28.433

Reputation: 8 298