Determine the "Luck" of a string

35

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.

charredgrass

Posted 2016-07-08T02:30:48.017

Reputation: 935

8Nice first challenge! – Alex A. – 2016-07-08T02:44:50.803

2Should the program accept uppercase input? – busukxuan – 2016-07-08T02:51:43.573

2@busukxuan Good question - no, it doesn't need to accept uppercase input. – charredgrass – 2016-07-08T03:00:22.800

@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. – charredgrass – 2016-07-08T04:15:19.417

1Can 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 – 2016-07-08T04:18:04.930

@cat I haven't thought about that, but I'd just use something reasonable - whatever makes it easiest or shortest but still works for at least the 3 test cases I mentioned (so it would need to be capable of negatives, and handle numbers larger than 32) – charredgrass – 2016-07-08T04:23:59.560

What's the output for luuck? – msh210 – 2016-07-08T04:31:00.807

@msh210 8, because uck is the longest string of consecutive matching characters – charredgrass – 2016-07-08T04:32:18.630

Oh, the question post says "If the word has multiple occurrences where it shares consecutive characters with lucky, use the longest consecutive string of the characters." and I missed it. Sorry. – msh210 – 2016-07-08T04:34:57.503

Answers

7

05AB1E, 36 32 28 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

Emigna

Posted 2016-07-08T02:30:48.017

Reputation: 50 798

Compression for 1 word can also be done with ', so for 26: Œv'¸éyåiyˆ}}¯é¤go¹'ƒÖ¦Ãg·- :). – Adnan – 2016-07-08T14:03:48.023

@Adnan: Weird. I was certain I had tried that. Apparently not. Thanks! – Emigna – 2016-07-08T14:14:29.850

why isn't this the top answer? – noɥʇʎԀʎzɐɹƆ – 2016-07-09T13:35:02.363

7

JavaScript (ES7), 123 112 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>

Neil

Posted 2016-07-08T02:30:48.017

Reputation: 95 035

Why do you use replace([^]) instead of match([])? Do you waste 3 bytes or is there a reason? – Titus – 2016-07-08T13:02:25.673

@Titus How many bytes does it cost to deal with a null match result? – Neil – 2016-07-08T14:24:12.853

1Four for a string, and a pair of () in this case; eating up all six that you would save with match(/[omen]/). Pity. – Titus – 2016-07-08T15:14:33.703

@Titus I think I tried both and found they were the same length so stuck with the one that I preferred. (Same goes for [5,4,3,2,1,0] for which I could have used [..."543210"] - it's the same length.) – Neil – 2016-07-08T15:19:54.690

Now finally ... found a neat trick. Saves 4 bytes on my PHP and 7 for your JS: use '*lucky' for the substr, so you can go with [1,2,3,4,5] instead of the spread. – Titus – 2016-07-08T17:27:42.347

1@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 – 2016-07-08T18:33:48.767

1-2*s.split(/[omen]/).length+2 is shorter. – Oriol – 2016-07-09T02:44:10.657

(s.match(/l?ucky?|c?ky?|l?uc?|l|y/)||[''])[0].length) is boring code and pretty inflexible, but 20 bytes shorter. – Titus – 2016-07-09T15:33:53.830

sorry only -18, insert |c – Titus – 2016-07-09T16:28:37.133

@Titus Sorry, that doesn't work for strings which have multiple lucky substrings. – Neil – 2016-07-09T17:13:22.567

correct. can you match all and sort results by length? how much would that cost? – Titus – 2016-07-10T11:02:08.900

@Titus About 35, I think. Sorry. – Neil – 2016-07-10T11:17:33.297

6

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")

Value Ink

Posted 2016-07-08T02:30:48.017

Reputation: 10 608

1._. that String#count is weird. +1 for (ab)using it. Also is it shorter to use gets rather than a function? – Downgoat – 2016-07-08T04:40:30.980

1@Downgoat if I gets I also has to puts for output, so not in this case. – Value Ink – 2016-07-08T06:21:01.117

6

Pyth, 27 26 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

Test it here.

busukxuan

Posted 2016-07-08T02:30:48.017

Reputation: 2 728

1I'm not an expert in Pyth but I believe you can change "omen" to just "omen and Pyth will understand – charredgrass – 2016-07-08T03:52:01.440

@charredgrass Oops, my mistake :-) – busukxuan – 2016-07-08T04:34:04.067

1Doesn't seem to work for string without chars form "lucky" in it. "memes" for example. – Emigna – 2016-07-08T12:30:38.803

1@Emigna Ah. the zero case again.... Thanks, fixed it! – busukxuan – 2016-07-08T12:56:57.270

4

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}

addison

Posted 2016-07-08T02:30:48.017

Reputation: 993

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. – charredgrass – 2016-07-08T03:21:48.013

3

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}

Christopher Burgdorff

Posted 2016-07-08T02:30:48.017

Reputation: 31

1You 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]) – addison – 2016-07-08T03:38:16.190

1Welcome 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) – charredgrass – 2016-07-08T03:38:28.417

Thanks for the tips! Got it down to 237, although it looks like the Ruby crowd's got me beat. – Christopher Burgdorff – 2016-07-08T03:45:27.420

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(); – charredgrass – 2016-07-08T03:49:18.187

Instead of substring you can use slice i believe (test this though, as I'm not sure) – Downgoat – 2016-07-08T04:39:34.277

You can use substr(n,e) instead of substring(n,e+n). It saves a byte over @Downgoat's slice(n,e+n) too. Also, "lucky" seems to only be used once, so you can probably trim out the variable declaration and go straight for "lucky".substr(... – Value Ink – 2016-07-08T09:07:11.040

@Downgoat You are correct, they are interchangeable in this case. More info: http://stackoverflow.com/questions/2243824/what-is-the-difference-between-string-slice-and-string-substring

– charredgrass – 2016-07-08T09:07:24.480

3

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

Zylviij

Posted 2016-07-08T02:30:48.017

Reputation: 390

3

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.

histocrat

Posted 2016-07-08T02:30:48.017

Reputation: 20 600

2

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.

user48818

Posted 2016-07-08T02:30:48.017

Reputation:

2

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)

jmilloy

Posted 2016-07-08T02:30:48.017

Reputation: 121

You can save one byte by using from intertools import* – wnnmaw – 2016-07-08T19:44:56.383

1

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!

Embodiment of Ignorance

Posted 2016-07-08T02:30:48.017

Reputation: 7 014

1

Python 3, 168 157 152 139 144 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.

Destructible Lemon

Posted 2016-07-08T02:30:48.017

Reputation: 5 908

while p+1 becomes while-~p – Leaky Nun – 2016-07-08T11:06:46.790

since b=s[p:q], len(b) must be q-p right? – Leaky Nun – 2016-07-08T11:11:02.013

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? – jmilloy – 2016-07-08T16:45:53.067

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? – jmilloy – 2016-07-08T16:51:42.823

sum(map(s.count,"omen")) saves one byte, making it 135 – Black Owl Kai – 2019-02-02T14:50:41.687

1

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

t-clausen.dk

Posted 2016-07-08T02:30:48.017

Reputation: 2 874

1

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>';

Titus

Posted 2016-07-08T02:30:48.017

Reputation: 13 814

1

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")

ForemanBob

Posted 2016-07-08T02:30:48.017

Reputation: 61

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. – Zylviij – 2016-07-09T01:23:50.360

0

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

melach

Posted 2016-07-08T02:30:48.017

Reputation: 1