# Determine the "Luck" of a string

Given a string, return that string's "luck".

A string's luck, as I completely just made up for the purpose of this challenge, is an integer, determined as so:

• The base luck for a string is 1.
• For each consecutive letter it shares with the word "lucky" (case insensitive), multiply the luck by 2. For instance, if your string was "lumberjack" or "smack" you'd multiply by 4. (More specifically, 2^number of consecutive characters shared.)
• The shared letters have to be in the same consecutive order it appears in "lucky" but can start anywhere in the word for the same value ("luc" has the same 8* multiplier as "cky").
• If the word has multiple occurrences where it shares consecutive characters with lucky, use the longest consecutive string of the characters.
• For ANY letter it shares with the word "omen" subtract 2 from the luck.
• It can match a character any amount of times, in any order. For instance the string "nnnnnomemenn" loses 24 luck (12 matching letters)

Example:

luck("lucky")
>>32


2^5 (5 consecutive letters) = 32

luck("firetruck")
>>6


2^3 - 2 (3 consecutive letters from uck, e shared with omen)

luck("memes")
>>-7


1 - 8 (base amount, 4 shared with "omen")

This is code golf, so the answer with the fewest bytes wins.

You can input and output any way you'd like - write a function, use standard input, etc.

For functions, assume whatever data type would make sense for that language. (For example, in JavaScript, you'd be passed a String and return a Number)

Edit: You can assume any input is lowercase.

• Nice first challenge! Commented Jul 8, 2016 at 2:44
• Should the program accept uppercase input? Commented Jul 8, 2016 at 2:51
• @busukxuan Good question - no, it doesn't need to accept uppercase input. Commented Jul 8, 2016 at 3:00
• @cat Not sure I quite understand what you're asking. But you can just assume all input will be lowercase and you don't need to catch any uppercase input. Commented Jul 8, 2016 at 4:15
• Can we assume an upper or lower bound on the luck of a given input? i.e. what's the smallest number of bits / data type I can get away with, or is it as big as my language can handle? that is, should it be int8_t str_luck(const char* str); or should it be uint64_t str_luck(const char* str);?
– cat
Commented Jul 8, 2016 at 4:18

## 05AB1E, 363228 26 bytes

Œv'¸éyåiyˆ}}¯é¤go¹'ƒÖ¦Ãg·-


Explanation

Œv         }                  # for each substring of input
'¸éyåi  }                   # if substring is part of "lucky"
yˆ                    # add it to global array
¯é¤               # get the longest such substring
go             # raise 2 to its length
¹'ƒÖ¦Ã       # remove all chars from input that isn't in "omen"
g·     # get length and multiply by 2
-    # subtract
# implicitly display


Try it online

Saved 2 bytes thanks to Adnan

• Compression for 1 word can also be done with ', so for 26: Œv'¸éyåiyˆ}}¯é¤go¹'ƒÖ¦Ãg·- :). Commented Jul 8, 2016 at 14:03
• @Adnan: Weird. I was certain I had tried that. Apparently not. Thanks! Commented Jul 8, 2016 at 14:14
• why isn't this the top answer? Commented Jul 9, 2016 at 13:35

## JavaScript (ES7), 123112 107 bytes

s=>2**[5,4,3,2,1,0].find((i,_,a)=>a.some(j=>s.includes("luckyL".substr(j,i))))-2*~-s.split(/[omen]/).length


Edit: Saved 11 bytes thanks to @Titus by assuming that the letter L does not appear in the input. Saved 5 bytes thanks to @Oriol. ES6 version for 125 114 109 bytes:

f=
s=>(1<<[5,4,3,2,1,0].find((i,_,a)=>a.some(j=>s.includes("luckyL".substr(j,i)))))-2*~-s.split(/[omen]/).length
;
<input oninput=o.textContent=f(this.value)><pre id=o></pre>

• Why do you use replace([^]) instead of match([])? Do you waste 3 bytes or is there a reason? Commented Jul 8, 2016 at 13:02
• @Titus How many bytes does it cost to deal with a null match result?
– Neil
Commented Jul 8, 2016 at 14:24
• Four for a string, and a pair of () in this case; eating up all six that you would save with match(/[omen]/). Pity. Commented Jul 8, 2016 at 15:14
• @Titus Not sure if that's what you meant but by adding an L to the end of the substr (which will never appear in the original string) I don't have to worry about extraneous matches and I can actually use the same array [5,4,3,2,1,0] both times, saving a whopping 13 bytes!
– Neil
Commented Jul 8, 2016 at 18:33
• -2*s.split(/[omen]/).length+2 is shorter. Commented Jul 9, 2016 at 2:44

# Pyth, 2726 28 bytes

-^2le+k}#"lucky".:Q)yl@"omen


1 byte saved thanks to OP :-)

Explanation:

                                 Implicit Q as input
.:Q              Find all substrings of input
+k}#"lucky"                 Filter for substring of "lucky", prepend "" in case of []
e                            Take last element, which is longest
l                             Get its length
^2                              Raise two to that
@"omen"Q   Filter Q for characters in "omen"
l           Get length; counts how many characters in "omen" there are
y            Double that
-                                Find the difference

• I'm not an expert in Pyth but I believe you can change "omen" to just "omen and Pyth will understand Commented Jul 8, 2016 at 3:52
• @charredgrass Oops, my mistake :-) Commented Jul 8, 2016 at 4:34
• Doesn't seem to work for string without chars form "lucky" in it. "memes" for example. Commented Jul 8, 2016 at 12:30
• @Emigna Ah. the zero case again.... Thanks, fixed it! Commented Jul 8, 2016 at 12:56

# Ruby, 91 87 bytes

String#count's finnicky usage strikes again! (When passed a String, it counts all occurrences of each letter in the function argument instead of all occurrences of the entire string.)

Try it online

->s{2**(z=0..5).max_by{|j|z.map{|i|s[b="lucky"[i,j]]?b.size: 0}.max}-2*s.count("omen")}


A version that takes in lines from STDIN and prints them: 89 bytes (86 +3 from the -n flag)

p 2**(z=0..5).max_by{|j|z.map{|i|$_[b="lucky"[i,j]]?b.size: 0}.max}-2*$_.count("omen")

• ._. that String#count is weird. +1 for (ab)using it. Also is it shorter to use gets rather than a function? Commented Jul 8, 2016 at 4:40
• @Downgoat if I gets I also has to puts for output, so not in this case. Commented Jul 8, 2016 at 6:21

## Ruby: 100 bytes

->s{2**(m=0;4.times{|j|1.upto(5){|i|m=[i,m].max if s.match"lucky"[j,i]}};m)-s.scan(/[omen]/).size*2}

• Try /[omen]/ as the regex to golf it down - it will match any character and is better in practical use than chaining |s for single characters. Commented Jul 8, 2016 at 3:21

# Javascript - 206 Bytes

r=>{var a="lucky";r:for(var e=5;e>0;e--)for(var n=0;6>n+e;n++){var o=a.substring(n,e+n);if(r.includes(o))break r}for(var t=0,e=0;e<r.length;e++)('omen'.indexOf(r[e])+1)&&t++;return Math.pow(2,o.length)-2*t}

• You could change this condition: s[k]=='o'||s[k]=='m'||s[k]=='e'||s[k]=='n' to look like this: "oman".split("").includes(s[k]) Commented Jul 8, 2016 at 3:38
• Welcome to PPCG! You can golf this down by removing whitespace to conserve bytes. Also, instead of (s[k]=='o'||s[k]=='m'||s[k]=='e'||s[k]=='n') you could use ('omen'.indexOf(s[k])+1) (assuming this is JavaScript) Commented Jul 8, 2016 at 3:38
• Thanks for the tips! Got it down to 237, although it looks like the Ruby crowd's got me beat. Commented Jul 8, 2016 at 3:45
• Another small thing: you can shorthand function luck(r) down to r=> to just make it an anonymous function, that's all that is necessary for this challenge. Also I made an edit to the challenge so you don't have to worry about case so you can remove the r=r.toLowerCase(); Commented Jul 8, 2016 at 3:49
• Instead of substring you can use slice i believe (test this though, as I'm not sure) Commented Jul 8, 2016 at 4:39

# Ruby, 57 bytes

b=gets.count'omen'
$.+=1while/[lucky]{#$.}/
p 2**$./2-2*b  gets sets $. to 1 as a side effect, then we increment it until the regular expression matching $. consecutive lucky characters no longer matches. # Haskell, 99 Another approach... I just learned about function aliasing import Data.List s=subsequences i=intersect l=length f n=2^(l$last$i(s"lucky")$s n)-2*l(i n$"omen")  ### Usage f"lucky" 32 f"firetruck" 6 f"memes" -7  # Mathematica, 86 bytes Code: 2^StringLength@LongestCommonSubsequence[#,"lucky"]-2StringCount[#,{"o","m","e","n"}]&  Explanation: LongestCommonSubsequence returns the longest contiguous substring common to the input and "lucky". StringLength gives its length. StringCount counts the number of occurrences of the characters of "omen" in the input. ## Python (139 Bytes) import itertools as t s=input() print 2**max([j-i for i,j in t.combinations(range(6),2)if'lucky'[i:j]in s]+[0])-2*sum(_ in'omen'for _ in s)  • You can save one byte by using from intertools import* Commented Jul 8, 2016 at 19:44 # TSQL, 233 bytes Golfed: DECLARE @t varchar(99)='oluck' ,@z INT=0,@a INT=0,@ INT=1,@c INT=0WHILE @a<LEN(@t)SELECT @a+=IIF(@=1,1,0),@z=IIF('LUCKY'LIKE'%'+x+'%'and @>@z,@,@z),@c+=IIF(x IN('O','M','E','N'),2,0),@=IIF(@+@a-1=LEN(@t),1,@+1)FROM(SELECT SUBSTRING(@t,@a,@)x)x PRINT POWER(2,@z)-@c  Ungolfed: DECLARE @t varchar(99)='oluck' ,@z INT=0 ,@a INT=0 ,@ INT=1 ,@c INT=0 WHILE @a<LEN(@t) SELECT @a+=IIF(@=1,1,0), @z=IIF('LUCKY'LIKE'%'+x+'%'and @>@z,@,@z), @c+=IIF(x IN('O','M','E','N'),2,0), @=IIF(@+@a-1=LEN(@t),1,@+1) FROM(SELECT SUBSTRING(@t,@a,@)x)x PRINT POWER(2,@z)-@c  Try it online # Haskell (134 132 Bytes) import Data.List c[]=[] c s@(_:x)=inits s++c x l=length g q=2^(maximum$map(\z->l q-l(q\\z))$c"lucky")-2*(l$intersect q"omen")


Not a code golfer nor a Haskell programmer, so would love some tips on this one.

(Example: g "firetruck")

• I am no Haskell Expert either, but managed to carve off a few byes by changing the algorithm slightly and using function aliases on reused functions. Commented Jul 9, 2016 at 1:23

# Python 3, 168157152139144 136 bytes

EDIT: Really obvious things I should have seen easier have been changed, and some slightly less obvious.

Edit 2: stoopid (˚n˚). The program threw errors. I fixed it up. not actually 153 :(

Thanks to Leaky Nun for saving 5 bytes, and to jmilloy for saving 13 8 bytes.

s=input()
p=q=k=len(s)
m=0
while-~p:
while-~q:m=(m,q-p)[(s[p:q]in"lucky")*q-p>m];q-=1
p-=1;q=k
print(2**m-2*sum(i in"omen"for i in s))


The program runs through all possibly possible substrings in input (possibly possible, because it computes impossible substrings as well, 8 to 7, for example), checks if the substring is in "lucky", then sets the exponent of 2 to the length of the substring should it be greater than the current value. Possibly could be improved by using only one while loop. Could possibly use some improvement; I'm still getting the hang of this.

• while p+1 becomes while-~p Commented Jul 8, 2016 at 11:06
• since b=s[p:q], len(b) must be q-p right? Commented Jul 8, 2016 at 11:11
• I stole you input and print method but did the rest very different, thanks! I think if you just do print(2**m-2*sum(i in"omen" for i in s)) for your last three lines you'll do better, like 148? Commented Jul 8, 2016 at 16:45
• Oh, and you can just move s[p:q] into the if clause while-~q:n=q-p;m=n if(s[p:q]in"lucky")*n>m else m;q-=1 for 143? Commented Jul 8, 2016 at 16:51
• sum(map(s.count,"omen")) saves one byte, making it 135 Commented Feb 2, 2019 at 14:50

# PHP program, 139 135 108 bytes

quantum leap fails for multiple substrings where the first occurence is shorter. :(

actually I could save another 7 bytes in PHP<5.4 with register_globals on

<?for($s=$argv[1];$i<5;$i++)for($j=6;--$j;)$r=max($r,strstr($s,substr('lucky*',$i,$j))?2**$j:1);echo$r-2*preg_match_all('/[omen]/',$s);


usage: php -d error_reporting=0 <filename> <string>

+5 for a function:

function f($s){for(;$i<5;$i++)for($j=6;--$j;)$r=max($r,strstr($s,substr('lucky*',$i,$j))?2**$j:1);return$r-2*preg_match_all('/[omen]/',$s);}  tests (on the function) echo '<table border=1><tr><th>input</th><th>output</th><th>expected</th><th>ok?</th></tr>'; foreach([ 'lumberjack'=>0, 'smack'=>2, 'nnnnnomemenn'=>-23, 'lucky'=>32, 'omen'=>-7, 'firetruck'=>6, 'memes'=>-7, 'determine the “luck” of a string'=>0, 'amazing'=>-3, 'wicked'=>2, 'chucky'=>16, 'uckyuke'=>14, 'ugly'=>2, 'lucy'=>8, 'lukelucky'=>30 ] as$x=>$e){$y=f($x); echo"$h<tr><td>",$x,'</td><td>',$y,'</td><td>',$e,'</td><td>',$e==\$y?'Y':'N',"</td></tr>";
}echo '</table>';


# Scala, 155 bytes

def f(w:String)=(1::List.fill((for(a<-1 to 5;s<-"lucky".sliding(a))yield if(w.contains(s)) a else 0).max){2}).product-2*w.filter("omen".contains(_)).length


# C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, 99 bytes

n=>(1<<Matches(n,"l?u?c?k?y?").OrderBy(x=>x.Value).Last().Length)-n.Count(x=>"omen".Any(c=>c==x))*2


Try it online!