21
\$\begingroup\$

Background

In Boggle, a round is scored by adding up the points for each unique word a player has found (i.e. any word that more than one player has found is worth 0 points). The points are calculated based on the number of letters in each word, as follows:

3 letters: 1 point

4 letters: 1 point

5 letters: 2 points

6 letters: 3 points

7 letters: 5 points

8 or more letters: 11 points

Challenge

In this challenge, write a program or function that takes in a list of lists of strings representing each player's words and outputs a list of the players' scores. You can assume that there will be at least 2 players and all words will be 3 or more letters and will all be lowercase (or all uppercase if you would prefer). You may also assume that each player will only use each word once; that is, no player's list will contain duplicates. This is code golf, so shortest answer in bytes wins.

Rules

Input can be taken in any reasonable format. Examples include a list of lists of strings, a list of comma-separated strings, a comma separated string on each line of input, etc. Output can be in the form of a list of integers (or your language's equivalent) or you can print the values to stdout using a separator of your choice (such as a newline).

Test Cases

Input => Output

[["cat","dog","bird","elephant"],
 ["bird","dog","coyote"],
 ["dog","mouse"]]                 => [12,3,2]

[["abc","def","ghi"],
 ["ghi","def","abc"]]             => [0,0]

[["programming","puzzles"],
 ["code","golf"],
 []]                              => [16,2,0]
\$\endgroup\$

18 Answers 18

6
\$\begingroup\$

Husk, 21 20 19 bytes

-2 bytes thanks to Zgarb

Idea taken from A055228

ṠṀöṁ(⌈√Π-3▼8L)fε`#Σ

Try it online!

Explanation (Of older version)

            ṠṀ-oṠ-uΣ   Remove duplicated words
                   Σ   Concatenate
                  u    Remove duplicates
               oṠ-     Remove those unique elements from the list (give list of elements that appear more than once)
            ṠṀ-        Remove those words from each list in the input
m                      For each list
 ṁ(                    Map then sum
          L)           Length
        ▼8             Min(8,x)
      -3               Minus 3
     Π                 Factorial
    √                  Square root
   ⌈                   Ceiling
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ 20 bytes \$\endgroup\$ – Zgarb Sep 19 '17 at 19:16
  • \$\begingroup\$ Nice, I won't rearrange though, to save on the explanation. \$\endgroup\$ – H.PWiz Sep 19 '17 at 19:18
  • \$\begingroup\$ 19 bytes using f instead of - \$\endgroup\$ – Zgarb Sep 20 '17 at 7:12
  • \$\begingroup\$ Thanks, I couldn't quite get that method to work myself. \$\endgroup\$ – H.PWiz Sep 20 '17 at 7:49
  • \$\begingroup\$ Gah! Thought I'd managed to tie you, hadn't noticed you're down to 19 now. \$\endgroup\$ – Shaggy Sep 20 '17 at 21:23
3
\$\begingroup\$

R, 142 126 121 117 bytes

function(L)sapply(lapply(L,setdiff,(l=unlist(L))[duplicated(l)]),function(x)sum(c(1,1,2,3,5,11)[pmin(6,nchar(x)-2)]))

Try it online!

Takes L as a list of vectors of strings; returns the values.

First, it unlists the words, finds the duplicates, then removes them from the players' word lists. Then it takes these unique word lists and computes the scores of each, using pmin to ensure that words longer than 8 get scored as 11.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ You can shorten it to 108 bytes if you only take away the duplicates in the sapply inner function: function(L)sapply(L,function(x)sum(c(1,1,2,3,5,11)[pmin(6,nchar(x[!x%in%(l=unlist(L))[duplicated(l)]])-2)])) \$\endgroup\$ – plannapus Sep 20 '17 at 12:57
3
\$\begingroup\$

JavaScript (ES6), 92 bytes

a=>a.map(b=>b.reduce((s,v)=>s+(a.filter(b=>b.includes(v))[1]?0:+"11235"[v.length-3]||11),0))

Somewhat similar to Rick Hitchcock's answer but created mostly independently; I used a different method of summing (reduce) and different method for checking for repeated terms (filter + includes). Credit to him for the idea of checking for item [1] instead of checking .length>1, though.

Test Cases

let f=
a=>a.map(b=>b.reduce((s,v)=>s+(a.filter(b=>b.includes(v))[1]?0:+"11235"[v.length-3]||11),0))

;[[["cat","dog","bird","elephant"],["bird","dog","coyote"],["dog","mouse"]],[["abc","def","ghi"],["ghi","def","abc"]],[["programming","puzzles"],["code","golf"],[]]]
.forEach(test=>O.innerHTML+=JSON.stringify(test)+" -> "+JSON.stringify(f(test))+"\n")
<pre id=O></pre>

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ You should be able to save a byte by using s+= and removing the parentheses around the ternaries. And 3 more by using map instead of reduce: tio.run/##NY/… \$\endgroup\$ – Shaggy Sep 20 '17 at 7:39
  • \$\begingroup\$ Well done. Your use of reduce and includes makes your answer quite different from mine. \$\endgroup\$ – Rick Hitchcock Sep 20 '17 at 14:49
3
\$\begingroup\$

JavaScript (ES6), 106 93 bytes

[Saved 13(!) bytes, thanks to Arnauld, Shaggy, and JollyJoker.]

a=>a.map(b=>b.map(c=>x+=(a+'').split`,`.filter(d=>d==c)[1]?0:+'11235'[c.length-3]||11,x=0)|x)

Test cases:

let f=

a=>a.map(b=>b.map(c=>x+=(a+'').split`,`.filter(d=>d==c)[1]?0:+'11235'[c.length-3]||11,x=0)|x)

console.log(f([["cat","dog","bird","elephant"],
               ["bird","dog","coyote"],
               ["dog","mouse"]
              ])
           )  // [12,3,2]
           
console.log(f([["abc","def","ghi"],
               ["ghi","def","abc"]
              ])
           ) // [0,0]
           
console.log(f([["programming","puzzles"],
               ["code","golf"],
               []
              ])
            ) // [16,2,0]

| improve this answer | |
\$\endgroup\$
  • 2
    \$\begingroup\$ I think you can replace c[7]?11:c[6]?5:c[5]?3:c[4]?2:1 with '00011234'[c.length]||11. \$\endgroup\$ – Arnauld Sep 19 '17 at 22:28
  • \$\begingroup\$ That gives [15,2,0] instead of [16,2,0] for the last test case, but that's probably easily fixed. Will work some more after dinner, unless you post a genius answer (as you usually do). Thanks! : ) \$\endgroup\$ – Rick Hitchcock Sep 19 '17 at 22:31
  • 1
    \$\begingroup\$ Ah yes, sorry, that should be '00011235'. \$\endgroup\$ – Arnauld Sep 19 '17 at 22:35
  • 1
    \$\begingroup\$ I think you can save another few bytes on top of @Arnauld's suggestion like so. \$\endgroup\$ – Shaggy Sep 19 '17 at 23:28
  • 1
    \$\begingroup\$ There are at least 3 letters, '11235'[c.length-3]||11, right? \$\endgroup\$ – JollyJoker Sep 20 '17 at 7:17
2
\$\begingroup\$

Perl 6, 64 bytes

{@^a.map:{sum .map:{@a.Bag{$_}>1??0!!(1,1,2,3,5)[.comb-3]||11}}}

Try it online!

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Pyth, 26 bytes

Uses H.PWiz's formula.

m+Fm.E@.!a3hS,8lk2fq1/sQTd

Verify all the test cases.

The initial version, 33 bytes:

m+Fm*h+++*6>lk7y>lk6>lk5glk3q1/sQ

Verify all the test cases.

Explanation

m+Fm*h+++*6>lk7y>lk6>lk5>glk3q1/sQ   Full program.

m                                    Map over the input.
   m                                 Map over each sublist.
                        >lk3         Is the length higher than 2? 1 if True and 0 if False.
      +             >lk5             Plus "is length higher than 5?".
       +       y>lk6                 Plus "is length higher than 6?", doubled.
        +*6>lk7                      Plus "is length higher than 7?", times 6.
     h                               Increment.
                            q1/sQ    Count the occurrences of the element in the flattened
                                     input, and check if it equals 1. 0 if False, 1 if True.
    *                                Multiplication.
 +F                                  Sum each sublist.
| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Haskell, 76 75 bytes

f l=[sum[fst.last$zip[0,0,1,1,2,3,5,11]w|w<-p,[w]==(filter(==w)=<<l)]|p<-l]

Try it online!

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Japt, 29 25 24 23 21 20 bytes

Ëx@èøX ¥1©3nXÊm8)ʬc

Try it


Explanation

Implicit input of array U.

Ëx@

Map over the array (Ë) and reduce each sub-array by addition (x) after passing its elements through the following function, where X is the current word.

èøX

Count (è) the elements in U that contain (ø) X.

¥1

Check if that is equal to 1.

©

Logical AND (&&).

3nXÊm8)

Subtract (n) 3 from the minimum of (m) 8 and the length (Ê) of X.

ʬc

Factorial, square root and round up, respectively.

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Python 2, 106 105 88 84 bytes

-1 byte thanks to Jonathan Frech
-1 (17) bytes thanks to reffu

lambda x:[sum(sum([1,0,1,1,2,6][:len(s)-2])*(`x`.count(`s`)<2)for s in l)for l in x]

Try it online!

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ 105 bytes. \$\endgroup\$ – Jonathan Frech Sep 20 '17 at 8:13
  • \$\begingroup\$ 104 bytes \$\endgroup\$ – reffu Sep 21 '17 at 12:17
  • \$\begingroup\$ @reffu thanks, I've made some modifications to golf even further :3 \$\endgroup\$ – Rod Sep 21 '17 at 12:44
  • \$\begingroup\$ @Rod Nice, I forgot about the second parameter of sum trick. \$\endgroup\$ – reffu Sep 21 '17 at 13:08
1
\$\begingroup\$

Jelly, 26 bytes

ðċ@Ị¥ÐfL€«8ị“¡[żÇ’ḃ11¤Sµ€Ẏ

Try it online!

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

R, 117 bytes

A completely different approach from the other R answer:

function(L)sapply(L,function(x)sum(c(0:3,5,11)[cut(nchar(x[x%in%names(which(table(unlist(L))<2))]),c(0,2,4:7,Inf))]))

Test cases:

> f=function(L)sapply(L,function(x)sum(c(0:3,5,11)[cut(nchar(x[x%in%names(which(table(unlist(L))<2))]),c(0,2,4:7,Inf))]))
> L=list(c("cat","dog","bird","elephant"),c("bird","dog","coyote"),c("dog","mouse"))
> f(L)
[1] 12  3  2
> L=list(c("abc","def","ghi"),c("ghi","def","abc"))
> f(L)
[1] 0 0
> L=list(c("programming","puzzles"),c("code","golf"),c())
> f(L)
[1] 16  2  0

Takes the names occurring only once in the list, convert their length to a factor based on the given cut-off points and translates that into scores that is then summed.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ 114 bytes by combining our two approaches in the deduplication step. \$\endgroup\$ – Giuseppe Sep 20 '17 at 12:52
1
\$\begingroup\$

Java 10, 202 200 198 197 194 bytes

a->{int q=a.length,r[]=new int[q],i=0,j,f;for(;i<q;i++)for(var s:a[i]){for(f=j=0;j<q;)f|=a[j].contains(s)&!a[i].equals(a[j++])?1:0;r[i]+=f<1?(j=s.length())<5?1:j<6?2:j<7?3:j<8?5:11:0;}return r;}

-1 byte thanks to @ceilingcat.

Explanation:

Try it here.

a->{                       // Method with ArrayList<String>[] parameter & int[] return-type
  int q=a.length,          //  Length of the input-array
      r[]=new int[q],      //  Result integer-array the size of the input-array,
                           //  initially filled with 0s by default
      i=0,j,               //  Index integers
      f;                   //  Flag integer (used as boolean)
  for(;i<q;i++)            //  Loop `i` in the range [0, `q`):
    for(var s:a[i]){       //   Inner loop over the Strings of the `i`'th List
      for(f=j=0;           //    Reset the flag `f` and index `j` both to 0
          j<q;)            //    Inner loop `j` in the range [0, `q`) again:
        f|=a[j].contains(s)//     If the `j`'th list contains the current String
           &!a[i].equals(a[j++])?
                           //     and the `i`'th and `j`'th list aren't the same:
            1              //      Bitwise-OR the flag with 1 (0->1; 1->1)
           :               //     Else:
            0;             //      Bitwise-OR the flag with 0 (0->0; 1->1)
                           //    End of inner loop (3) (implicit / single-line body)
      r[i]+=               //    Increase the `i`'th item in the result integer-array by:
            f<1?           //     If the flag is still 0 (so the current String is unique):
             (j=s.length())<5?
                           //      If the length of the String is below 5:
              1            //       Increase by 1
             :j<6?         //      Else-if the length is 5 instead:
              2            //       Increase by 2
             :j<7?         //      Else-if the length is 6 instead:
              3            //       Increase by 3
             :j<8?         //      Else-if the length is 7 instead:
              5            //       Increase by 5
             :             //      Else (the length is above 7):
              11           //       Increase by 11
            :              //     Else (the flag is 1 - thus the String is not unique):
             0;            //      Leave it the same by increasing with 0
  return r;}               //  Return the resulting integer-array
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I love ternaries, and the only thing I hate about ScaLa is that they removed this ternary syntax. \$\endgroup\$ – V. Courtois Sep 25 '17 at 15:19
  • \$\begingroup\$ @V.Courtois Hmm, out of curiosity, how is the ternary syntax in Scala now? \$\endgroup\$ – Kevin Cruijssen Sep 26 '17 at 8:20
  • \$\begingroup\$ uh : if(bool1)exp1 else exp2 \$\endgroup\$ – V. Courtois Sep 26 '17 at 11:12
0
\$\begingroup\$

Perl 5, 104 + 2 (-na) = 106 bytes

push@a,[@F];map$k{$_}++,@F}{map{$s=0;map$s+=(($l=y///c)<8?$l<7?$l<5?1:$l-3:5:11)*($k{$_}<2),@$_;say$s}@a

Try it online!

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Clojure, 102 bytes

#(for[p %](apply +(for[w p](if(next(filter #{w}(flatten %)))0(get{3 1 4 1 5 2 6 3 7 5}(count w)11)))))

next returns nil if there is only one word w :)

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

PHP, 226 bytes

function x($a){foreach($a as$p){$q=call_user_func_array('array_diff',$a);array_push($a,array_shift($a));$x=0;array_map(function($b)use(&$x){$b=strlen($b);$x+=($b<5?1:($b==5?2:($b==6?3:($b==7?5:11))));},$q);$o[]=$x;}return $o;}

I think this could still be trimmed down quite a bit.

Ungolfed:

function x($a) {
    foreach ($a as $p) {
        $q = call_user_func_array('array_diff', $a);
        array_push($a, array_shift($a));
        $x = 0;
        array_map(function($b) use (&$x){
            $b = strlen($b);
            $x += ($b < 5 ? 1 : ($b == 5 ? 2 : ($b == 6 ? 3 : ($b == 7 ? 5 : 11))));
        }, $q);
        $o[] = $x;
    }
    return $o;
}

Try it online!

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Scala, 242 bytes

The function takes, as a parameter a, a Seq[Set[String]] and returns an Array[Int]. I use an Array for it to be mutable (4-char loss).

var s=Seq("")
a.foreach(x=>x.foreach(y=>s:+=y))
var u:Array[Int]=Array()
var i= -1
a.foreach(t=>{i+=1
u:+=0
t.map(x=>{val l=x.length
if(s.count(_==x)<2){if(l>7)u(i)+=11
if(l==7)u(i)+=5
if(l==6)u(i)+=3
if(l==5)u(i)+=2
if(l>2&l<5)u(i)+=1}})})
u

Try it online!

Might be optimizable, since I did not even work on the

if(l>7)u(i)+=11
if(l==7)u(i)+=5
if(l==6)u(i)+=3
if(l==5)u(i)+=2
if(l>2&l<5)u(i)+=1

part. Thanks for this challenge!

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Swift 4, 164 bytes*

{$0.map{Set($0).subtracting(Dictionary(grouping:$0.flatMap{$0},by:{$0}).flatMap{$1.count != 1 ?$0:nil}).map{[0,1,1,2,3,5,11][min(max($0.count-2,0),6)]}.reduce(0,+)}}

The above expression is technically correct, pure Swift. However, the expression is so complex that, due to exponential blowup in the type inference system, can't be processed before the compiler gives up after some arbitrary timeout (like 15s or something).

To make this expression compilable with the current compiler, it can be broken down like this:

{
let n = Dictionary(grouping:$0.flatMap{$0},by:{$0}).flatMap{$1.count != 1 ?$0:nil}
return $0.map{Set($0).subtracting(n).map{[0,1,1,2,3,5,11][min(max($0.count-2,0),6)]}.reduce(0,+)}
}

Test cases:

let f: (_ input: [[String]]) -> [Int] = {
    let n = Dictionary(grouping:$0.flatMap{$0},by:{$0}).flatMap{$1.count != 1 ?$0:nil}
    return $0.map{Set($0).subtracting(n).map{[0,1,1,2,3,5,11][min(max($0.count-2,0),6)]}.reduce(0,+)}
}

let testcases: [(input: [[String]], expected: [Int])] = [
    (input: [
            ["cat","dog","bird","elephant"],
            ["bird","dog","coyote"],
            ["dog","mouse"]
        ],
        expected: [12,3,2]
    ),
    (input: [
            ["abc","def","ghi"],
            ["ghi","def","abc"]
        ],
        expected: [0,0]
    ),
    (input: [
            ["programming","puzzles"],
            ["code","golf"],
            []
        ],
        expected: [16,2,0]
    ),
]

for (caseNumber, testcase) in testcases.enumerated() {
    let actual = f(testcase.input)
    assert(actual == testcase.expected,
        "Testcase #\(caseNumber) \(testcase.input) failed. Got \(actual), but expected \(testcase.expected)!")
    print("Testcase #\(caseNumber) passed!")
}

Broken down:

let verboseF: (_ input: [[String]]) -> [Int] = { playerHands in
    let allWords = playerHands.flatMap{$0}
    // demo data for first test case:
    // allWords: ["cat", "dog", "bird", "elephant", "bird", "dog", "coyote" "dog", "mouse"]

    let allWordsGroupedByThemselves = Dictionary(grouping: allWords, by: {$0})
    /* allWordsGroupedByThemselves:
    [
        "cat": ["cat"],
        "dog": ["dog", "dog", "dog"],
        "bird": ["bird", "bird"],
        "elephant": ["elephant"],
        "coyote": ["coyote"], "mouse": ["mouse"]
    ]*/

    let allWordsUsedMoreThanOnce = allWordsGroupedByThemselves.flatMap{$1.count != 1 ?$0:nil}
    // allWordsUsedMoreThanOnce: ["dog", "bird"]
    
    return playerHands.map{ hand in
        // demo data for first hand of first test case:
        // hand: ["cat","dog","bird","elephant"]

        let uniqueWordsInHand = Set(hand)
        // uniqueWordsInHand: ["cat","dog","bird","elephant"]

        let uniqueWordsInHandNotUsedByOthers = uniqueWordsInHand.subtracting(allWordsUsedMoreThanOnce)
        // uniqueWordsInHandNotUsedByOthers: ["cat", "elephant"]

        let wordLengths = uniqueWordsInHandNotUsedByOthers.map{$0.count}
        // wordLengths: [3, 8]

        let scores = wordLengths.map{ wordLength in
            return [0,1,1,2,3,5,11][min(max(wordLength-2, 0), 6)] //A look up table that maps word length to word score
        }
        //scores: [1, 11]

        let playerScore = scores.reduce(0,+)
        // playerScore: 12
        
        return playerScore
    }
}
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

ASP + Python, 137 bytes

u(P,W):-1{p(_,W)}1;p(P,W).s(P,S):-S=#sum{@v(W):u(P,W)};p(P,_).#script(python)
def v(w):return[1,1,2,3,5,11][min(len(w.string),8)-3]#end.

Expects data formatted as:

p(1,("cat";"dog";"bird";"elephant")).
p(2,("bird";"dog";"coyote")).
p(3,("dog";"mouse")).

Needs clingo 5.2.1 with python support.

Ungolfed:

unique(P,W):- 1 { player(_,W) } 1 ; player(P,W).
score(P,S):- S = #sum{@value(W): unique(P,W)} ; player(P,_).
#script (python)
def value(word):
    return [1,1,2,3,5,11][min(len(word.string),8)-3]
#end.

The python function is heavily inspired from the python answer.

| improve this answer | |
\$\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.