11
\$\begingroup\$

On the TV cooking show Guy's Grocery Games, the chefs cannot begin shopping until Guy Fieri, the show's host, says "Three, two, one, go!" However, the words do not have to be all together. He often "hides" the words in things like this (based on a real example, but not quoting verbatim):

There are three of you here; after this round there will be two; but only one of you will go shop for up to $20,000.

(At the end of the show, the winning chef must find either five or ten items, depending on the episode, from the store; the number of items found determines how much money they win.)

Fairly regularly, he succeeds in confusing the chefs and they don't go. Often, they realize after a few seconds that they can go and frantically run down the aisles trying to make up for the lost time; other times, they don't realize it until the judges start shouting "Go! Go! Go!"

Can you do better?

Write a program that determines

  1. Whether the input string allows the chefs to go, and
  2. If so, at what point in the string they may go.

The chefs may go if the string contains three, followed by zero or more other characters, followed by two, followed by zero or more other characters, followed by one, followed by zero or more other characters, followed by go. Matching is case-insensitive.

The point at which the chefs may go is the zero-based index of the o in go.

Because ThreeTwoOneGo is 13 characters, this number will always be at least 12. If the chefs can go, return the point at which they can go. If they cannot, return no value, any falsy value, or any number of your choice less than or equal to 11. This number does not need to be consistent; it's okay if it varies randomly and/or with the input, as long as it is consistently less than or equal to 11.

\$\endgroup\$
8
  • 2
    \$\begingroup\$ If there is more than one "go" (e.g. "three two one go go go!") then does it matter which one is chosen? \$\endgroup\$
    – Neil
    Commented Dec 6, 2023 at 11:48
  • 4
    \$\begingroup\$ So the cooks gan go if they say "Three twos onerous gone"? :/ \$\endgroup\$ Commented Dec 6, 2023 at 12:34
  • 2
    \$\begingroup\$ @JonathanAllan yes, they can, at least for the purposes of this challenge. I think Guy has actually done something like that before, and it counted. \$\endgroup\$
    – Someone
    Commented Dec 6, 2023 at 15:03
  • 5
    \$\begingroup\$ @JonathanAllan I like this one: “three-dimensional creditworthiness of psychoneuroimmunologists is better than that of otorhinolaryngologists” \$\endgroup\$ Commented Dec 6, 2023 at 19:02
  • 7
    \$\begingroup\$ ..."Healthy threesomes outwork nonegos". \$\endgroup\$ Commented Dec 6, 2023 at 19:52

20 Answers 20

8
\$\begingroup\$

R, 51 bytes

regexpr("three.*two.*one.*g\\Ko",scan(,""),,T)[1]-1

Thanks to Bbrk24 for pointing out that \K (you have to use an extra backslash in R) sets where the match is considered to start.

R has a lot of built in regex functions, and this one handily tells you where the first match starts (-1 if there is no match). It's 1-indexed though, so we have to take 1 off (and hence, if the chefs can't go, -2 is returned, but that's less than 11 so we're good).

We need to specify perl=TRUE to use the \K feature. It's cheapest to specify that positionally rather than use pe=T.

two example cases where the code returns 25 for a string where the cooks can go, and -2 for a case where they can't go at all

Depending on conventions for how the input is available, the scan(,"") could turn into a variable (e.g. x) or function (\(x)) saving 8 or 4 bytes respectively.

\$\endgroup\$
7
  • 2
    \$\begingroup\$ You could take input as a function, not as a variable though \$\endgroup\$ Commented Dec 6, 2023 at 17:01
  • \$\begingroup\$ Nice. A link to let others see it in action (e.g. Attempt this online!) is helpful \$\endgroup\$ Commented Dec 6, 2023 at 19:01
  • 1
    \$\begingroup\$ And a further +1 byte to fix the requirement to be case-insensitive: R, 51 bytes: \(x)regexpr("three.*?two.*?one.*?g\\Ko",x,T,T)[1]-1 \$\endgroup\$ Commented Dec 6, 2023 at 21:33
  • 1
    \$\begingroup\$ The first example in my previous comment (Three two one go Three two one go go) will fail on your current code for two reasons: 1. The case sensitivity and 2. The greedy matching. (Try "three two one go three two one go" to observe the second problem in isolation.) \$\endgroup\$ Commented Dec 11, 2023 at 16:57
  • 2
    \$\begingroup\$ Couldn’t you get rid of the -1 on the end by matching \Kgo rather than g\Ko? \$\endgroup\$
    – Bbrk24
    Commented Dec 24, 2023 at 22:21
6
\$\begingroup\$

Perl 5 + -p, 34 bytes

Returns 0 on invalid inputs. Performs a match against the input, using the result to decrement the last position of the match, via @+, by 1 (on successful matches, otherwise 0).

$_=-/three.*?two.*?one.*?go/i+"@+"

Try it online!

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

JavaScript (ES11), 40 bytes

-6 bytes thanks to InSync

Returns -1 for invalid inputs.

s=>s.search(/(?<=three.*two.*one.*g)o/i)

Attempt This Online!

Method

Using a lookbehind assertion, we look for the position of the first "o" in the input string which is preceded by the pattern "three.*two.*one.*g".

\$\endgroup\$
4
  • 1
    \$\begingroup\$ I am deeply jealous that R's regex parser doesn't allow variable length regexes (i.e. no .*) in look-behind assertions! \$\endgroup\$
    – JDL
    Commented Dec 6, 2023 at 11:55
  • 1
    \$\begingroup\$ @JDL The workaround for that in some regex engines is to use \K instead, which resets where the match is considered to start. Go try /three.*two.*one.*g\Ko/i on regex101 with PCRE2, for example. \$\endgroup\$
    – Bbrk24
    Commented Dec 6, 2023 at 14:18
  • \$\begingroup\$ oh nice, I didn't know that one. That helps, thanks! (The best I could come up with otherwise was to reverse the whole thing and look for "og.*eno.*owt.*eehrt", which is not particularly golfy in R) \$\endgroup\$
    – JDL
    Commented Dec 6, 2023 at 15:57
  • 1
    \$\begingroup\$ -6 bytes with .search(): s=>s.search(/(?<=three.*two.*one.*g)o/i). \$\endgroup\$
    – InSync
    Commented Dec 7, 2023 at 9:08
4
\$\begingroup\$

Retina, 25 bytes

I^iw0`three.*two.*one.*go

Try it online!

The regex is pretty straightforward, all the magic comes from the configuration flags at the beginning:

  • I: find the position of the matches
  • ^: use the position of the last character instead of the first
  • i: case insensitive
  • w: do this for all possible ways (even overlapping) of matching this regex
  • 0: return only the first one

In practice, w0 will make sure that we return the earliest possible complete match, and is shorter than any other option based on lazy matching (.*?)

\$\endgroup\$
3
  • 2
    \$\begingroup\$ Wow, I've never seen an I stage before! \$\endgroup\$
    – Neil
    Commented Dec 7, 2023 at 0:58
  • \$\begingroup\$ Doesn't this return 1-based indexes? \$\endgroup\$
    – manatwork
    Commented Dec 24, 2023 at 19:44
  • 1
    \$\begingroup\$ I've since double-checked and @TwiNight used an I stage here. I didn't see it though because it's a relatively recent answer to an old question. \$\endgroup\$
    – Neil
    Commented Dec 30, 2023 at 22:42
4
\$\begingroup\$

Vyxal 3, 24 bytes

ʀ"ỌṚUmn»\6”Ṃ".*?"j∥ẋMṪL+

Try it Online! | Literate mode equivalent (doesn't work)

Explanation:

ʀ"ỌṚUmn»\6”Ṃ".*?"j∥ẋMṪL+­⁡​‎⁠‎⁡⁠⁢‏⁠‎⁡⁠⁣‏⁠‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏⁠‎⁡⁠⁢⁣‏⁠‎⁡⁠⁢⁤‏⁠‎⁡⁠⁣⁡‏⁠‎⁡⁠⁣⁢‏⁠‎⁡⁠⁣⁣‏⁠‎⁡⁠⁣⁤‏⁠‎⁡⁠⁤⁡‏⁠‎⁡⁠⁤⁢‏⁠‎⁡⁠⁤⁣‏⁠‎⁡⁠⁤⁤‏⁠‎⁡⁠⁢⁡⁡‏⁠‎⁡⁠⁢⁡⁢‏‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢⁡⁣‏⁠‎⁡⁠⁢⁡⁤‏⁠‎⁡⁠⁢⁢⁡‏‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁢⁢⁢‏⁠‎⁡⁠⁢⁢⁣‏⁠‎⁡⁠⁢⁢⁤‏‏​⁡⁠⁡‌­
 "ỌṚUmn»\6”Ṃ".*?"j        # ‎⁡Split "ỌṚUmn»\6” ("three two one go") on spaces and join by ".*?"
ʀ                 ∥ẋM     # ‎⁢Parallel apply regex search and regex match to lowercased input
                     ṪL+  # ‎⁣Add length - 1 to index
💎

Created with the help of Luminespire.

\$\endgroup\$
3
  • \$\begingroup\$ This looks for the last "go" of several valid matches, which is incorrect according to a comment of the OP. \$\endgroup\$
    – Neil
    Commented Dec 6, 2023 at 17:31
  • \$\begingroup\$ @Neil ty for letting me know \$\endgroup\$
    – math scat
    Commented Dec 6, 2023 at 21:11
  • \$\begingroup\$ Your Literate link is still out of date and neither work on threetwoonegoonego. \$\endgroup\$
    – Neil
    Commented Dec 6, 2023 at 21:48
3
\$\begingroup\$

Jelly, 24 bytes

“Ç<*tẉ{cIE»Ḳṫw¥ƒŒlL’ȧạ¥L

Try it online!

A monadic link taking a string and returning an integer.

Thanks to @JonathanAllan for pointing out the case-insensitive requirement!

Explanation

“Ç<*tẉ{cIE»Ḳ              | Compressed string "three two one go" split at spaces
            ṫw¥ƒŒl        | Reduce, starting with the main link argument lower-cased, then for each of the strings above, find the first position in the current string and take the tail starting from there
                   L      | Length
                    ’     | Decrease by 1
                     ȧạ¥L | And with absolute difference from this and the length of the original string
\$\endgroup\$
1
  • \$\begingroup\$ @JonathanAllan thanks, very similar! I've corrected mine to handle the case-insensitivity requirement. I'd been looking at the (currently) top R answer which used a case sensitive regex; although my answer is completely different, I think I'd assumed therefore that the case didn't matter and hadn't gone back to check that part of the question. \$\endgroup\$ Commented Dec 6, 2023 at 21:34
3
\$\begingroup\$

Retina 0.8.2, 36 bytes

i1M!`.*?three.*?two.*?one.*?g(?=o)
.

Try it online! Link includes test cases. Outputs 0 for disallowed inputs. Explanation:

i1M!`.*?three.*?two.*?one.*?g(?=o)

Keep only the text up to the g of go (or delete the text entirely if it doesn't contain the words in the right order).

.

Count the number of characters remaining.

\$\endgroup\$
2
  • \$\begingroup\$ If "three two one go" appears more than once in the input this will return the position of the last one, not the first one: tio.run/##PY3BCoMwEETv/… \$\endgroup\$
    – Leo
    Commented Dec 6, 2023 at 22:41
  • 1
    \$\begingroup\$ @Leo Nice spot, I forgot Retina defaults to returning all matches, so it was adding up all of the lengths of all of the matches. Fixed at a cost of a byte. \$\endgroup\$
    – Neil
    Commented Dec 7, 2023 at 0:53
3
\$\begingroup\$

Scala 3, 77 bytes

s=>new Regex("(?i)(?<=three.*two.*one.*g)o").findFirstMatchIn(s).map(_.start)

Attempt This Online!

\$\endgroup\$
2
  • \$\begingroup\$ I see there an "i" parameter passed on the Regex instantiation, but the code still seems to match case sensitively. Attempt This Online says it also uses Scala 3, so I guess is not version mismatch. Maybe different build of the interpreter? \$\endgroup\$
    – manatwork
    Commented Dec 24, 2023 at 19:49
  • \$\begingroup\$ @manatwork Thank you, now I have corrected it. \$\endgroup\$
    – 138 Aspen
    Commented Dec 24, 2023 at 23:35
2
\$\begingroup\$

05AB1E, 39 37 bytes

lηÅΔηU“„í‚•€µ‚œ“#εXsδÅ¿ƶ0K}.«âε˜D{Q}à

What a mess.. Once again I hate that 05AB1E lacks a builtin to get all matching indices, instead of only the first.. The ηU and εXsδÅ¿ƶ0K} are all to do that manually.. -_-

Will output -1 if it's invalid.

Try it online or verify a few test cases.

Explanation:

l                # Convert the (implicit) input-string to lowercase
 η               # Convert it to a list of prefixes
  ÅΔ             # Get the index of the first prefix that's truthy for
                 # (or -1 if none are):
    η            #  Get the prefixes of the current prefix
     U           #  Pop and store it in variable `X`
    “„í‚•€µ‚œ“   #  Push dictionary string "three two one go"
     #           #  Split it on spaces
      ε          #  Map over all four words:
       X         #   Push the earlier list of prefixes of the prefix
        s        #   Swap so the current word is at the top again
         δ       #   Map over each prefix, using the word as argument:
          Å¿     #    Check whether the prefix ends with the word as substring
            ƶ    #   Multiply each check by its 1-based index
             0K  #   Remove all 0s
      }.«        #  After the map: reduce this list of lists by:
         â       #   Taking the cartesian product of two lists
          ε      #  Map over each combination of values:
           ˜     #   Flatten it to a single list
            D{Q  #   Check if the four indices are in increasing order:
            D    #    Duplicate the list
             {   #    Sort the copy
              Q  #    Check if both lists are still the same
          }à     #  After the map: check if any was truthy
                 # (after which the found index is output implicitly as result)

See this 05AB1E tip of mine (section How to use the dictionary?) to understand why “„í‚•€µ‚œ“ is "three two one go".

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

Charcoal, 42 bytes

≔θηF⪪”↶⌊¶z₂1γmι.Jχ” ≔✂η⌕↧ηιLη¹η¿⊖LηI⁻Lθ⊖Lη

Try it online! Link is to verbose version of code. Does not output anything for disallowed inputs. Explanation:

≔θη

Make a copy of the input string.

F⪪”....” 

Loop over the four relevant words.

≔✂η⌕↧ηιLη¹η

Slice the string so that it starts at that word. (But if the word is not present, the Find returns -1, which causes the Slice to return the last character.)

¿⊖Lη

If the string still contains at least two characters (which means it begins go), then...

I⁻Lθ⊖Lη

Output 1 more than the number of characters removed.

\$\endgroup\$
1
\$\begingroup\$

Python 3, 93 77 bytes

I'm new to regex, so open to golfs. Returns an integer for truthy and a None object for falsy.

lambda n:((x:=re.search("three.*?two.*?one.*?go",n,2))and~-x.end())
import re
\$\endgroup\$
1
1
\$\begingroup\$

Uiua 0.6.1, 38 bytes

-1⧻⊢⊢regex"(?is).*three.*two.*one.*go"

Try it yourself

\$\endgroup\$
1
\$\begingroup\$

C (gcc): 119 113 bytes

main(c,v)char**v;{char*s="go\0.one\0two\0three",*p=*++v;for(c=4;c--;)p+=strcasestr(p,s+4*c)-(int)p;exit(++p-*v);}

Edit: Thanks to everyone helping in improving the answer! :D

  1. As always a minor improvement was made thanks to @ceilingcat (124 -> 122 bytes).
  2. Thanks to @Command Master for getting rid of the prototype (119 -> 113 bytes).

C (gcc -m32): 105 bytes

main(c,v)char**v;{char*s="go\0.one\0two\0three",*p=*++v;for(c=4;c--;)p=strcasestr(p,s+4*c);exit(++p-*v);}

The prototype feels like a huge waste of bytes. Unfortunately, I could not figure out how to work around that. :/

Edit: Using the -m32 flag you can get rid of the prototype entirely (explained in the comments below). Thanks to @Command Master for suggesting this trick. :)

Try it online

Explanation:

main(c,v)char**v;{
    // 's' contains the keywords that are padded with '.' so the pointer can
    // be moved by a fixed value to go to the next keyword.
    //
    // 'p' contains the index of each keyword (eventually the index of 'go').
    // If no match is found 'p' will be NULL and cause a segmentation fault.
    // which indicates that the input does not allow the chefs to go.
    // The sequence is stored in reverse as it saves more space.
    char*s="go\0.one\0two\0three",*p=*++v;

    for(c=4;c--;)
        // The haystack is 'p' instead of v[1] since this discards any characters
        // before the last match which prevent false positives like:
        // "two...three..one..go..."
        //
        // Adds the needle's offset from 'p' to 'p', the casting tricks the
        // compiler to get around the issue of having the declare the prototype
        // of strcasestr().
        p+=strcasestr(p,s+4*c)-(int)p;

    // 'p' has to be incremented as it contains the index of 'g' of 'go'.
    exit(++p-*v);
}
\$\endgroup\$
5
  • 1
    \$\begingroup\$ If you use -m32 you don't need the prototype. \$\endgroup\$ Commented Dec 20, 2023 at 4:07
  • \$\begingroup\$ @CommandMaster Nice trick! Out of curiosity do you know why this works? \$\endgroup\$ Commented Dec 21, 2023 at 13:35
  • \$\begingroup\$ It makes the program 32 bit, so the size of a pointer is the same as int which is the default return value without a prototype \$\endgroup\$ Commented Dec 21, 2023 at 15:17
  • 1
    \$\begingroup\$ Even without -m32 you can do p+=strcasestr(p,s+4*c)-(int)p; \$\endgroup\$ Commented Dec 21, 2023 at 15:20
  • \$\begingroup\$ Ah, that makes sense! Also, nice trick for the non--m32 code. :D \$\endgroup\$ Commented Dec 23, 2023 at 7:57
1
\$\begingroup\$

JavaScript (Node.js), 51 bytes

s=>(z=/three.*two.*one.*?go/gi).test(s)*z.lastIndex

Attempt This Online!

This outputs 0 for no match, or the index.

The RegExp.lastIndex property (which only works for RegExes that have the g flag enabled) gives the index of the end of the previous match.

\$\endgroup\$
9
  • \$\begingroup\$ It looks like you can save one byte by removing the "i" in "gi"? \$\endgroup\$
    – Someone
    Commented Dec 6, 2023 at 2:28
  • \$\begingroup\$ @Somebody the i makes the match case-insensitive. \$\endgroup\$ Commented Dec 6, 2023 at 2:44
  • \$\begingroup\$ Oh, okay, that makes sense. \$\endgroup\$
    – Someone
    Commented Dec 6, 2023 at 2:45
  • \$\begingroup\$ Your code returns the 0-based index of the character after the o in go. Of course, this can also be interpreted as the 1-based index of the o in go if the OP allows 1-base indexing. \$\endgroup\$
    – Arnauld
    Commented Dec 6, 2023 at 8:05
  • 1
    \$\begingroup\$ You code also finds the last "go", which seems incorrect. \$\endgroup\$
    – Neil
    Commented Dec 6, 2023 at 9:46
1
\$\begingroup\$

Swift, 156 bytes

Short version:

func c(_ s:String)->Int{let s=s.lowercased();return s.ranges(of: /three.*two.*one.*go/).first.map({s.distance(from:s.startIndex,to:$0.upperBound)-1}) ?? 0}

Verbose version:

func c2_(_ s: String) -> Int {
    let s = s.lowercased()
    return s.ranges(of: /three.*two.*one.*go/).first.map({
        s.distance(from: s.startIndex, to: $0.upperBound) - 1
    }) ?? 0
}
\$\endgroup\$
1
\$\begingroup\$

Gema, 38 characters

*\Cthree*two*one*g\Po*=@column@end
*=0

Sample run:

bash-5.2$ gema '*\Cthree*two*one*g\Po*=@column@end;*=0' <<< 'There are three of you here; after this round there will be two; but only one of you will go shop for up to $20,000.'
91

Try it online! / Try all test collected cases online!

\$\endgroup\$
1
\$\begingroup\$

TypeScript’s type system, 213 203 bytes

//@ts-ignore
type F<S,A=0,Z=any,B=Lowercase<S>>=B extends`${Z}three${Z}two${Z}one${Z}go${infer D}`?F<B extends`${infer B}go${D}`?B:0,[1]>:A extends 0?0:S extends`${Z}${infer R}`?F<R,[...A,1]>:A["length"]

Try it at the TS playground

\$\endgroup\$
2
  • \$\begingroup\$ Your playground code has one of the Z's lowercase. \$\endgroup\$
    – Bbrk24
    Commented Jan 5 at 4:09
  • \$\begingroup\$ @Bbrk24 Thanks, missed that. I write a lot of these on my phone and I have to write the code twice (once in the playground and then manually copying it into SE because playground doesn't let you select text on mobile), so there's room for a lot of little mistakes. In this case the code still works, because z evaluates to any because it isn't defined, but that throws an error. \$\endgroup\$ Commented Jan 5 at 11:46
1
\$\begingroup\$

Pip, 30 bytes

a~-`three.*?two.*?one.*?go`D$)

Outputs an index, or nothing if the chefs cannot go. Attempt This Online!

Explanation

   `three.*?two.*?one.*?go`     ; Regex, with non-greedy quantifiers
  -                             ; Make it case-insensitive
a~                              ; Find the first match in the program argument
                            $)  ; Get the index of the end of that match
                           D    ; Decrement
\$\endgroup\$
0
\$\begingroup\$

Python 3.8 (pre-release), 85 bytes

lambda s:([0]<sorted(x:=[*map(s.lower().find,('three','two','one','go'))])==x)*-~x[3]

Try it online!

Solution without regex

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

Bash, 77 bytes

IFS=: read l m<<<`grep -bioP three.*?two.*?one.*?go<<<$1`
echo $((${#m}+l-1))

Attempt This Online! (test cases shamelessly stolen from @manatwork's answer)

Bash + bc, 73 bytes

IFS=: read l m<<<`grep -bioP three.*?two.*?one.*?go<<<$1`
bc<<<${#m}+$l-1

ATO does not ship bc, so no link, sorry.

Use as script or bash function, with input string as first argument.
Outputs -1 if the chefs can't go.

Explanation:

                 grep -                         <<<$1   # in the first argument, match
                            three.* two.* one.* go      # the words anywhere
                         o                              # output only the match
                       b                                # and the index of the start of the match (format is hardcoded "index:match")
                          P        ?     ?     ?        # find words lazily, so the first instance allows the chefs to go
                        i                               # case-insensitive

IFS=:                                                   # set the Internal Field Separator to ':'
      read l m<<<`                                    ` # store output of grep in variables l and m, split input on IFS (implicit)
echo $((${#m}+l-1))                                     # output value of (length of match) + (index of start of match) -1
\$\endgroup\$

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