Determine a string's Scrabble score and validity

Your task is to determine if a given string is of proper length and can be represented with Scrabble tiles and, if so, output the sum of each letter's score.

If you don't know how to play Scrabble:, you have 100 tiles with various letters A–Z printed on them, as well as two wildcards that can represent any letter. Each letter has a certain number of points, and each tile (but not necessarily word) can only be used once. When a word is played, the point value of each tile used is added up, which becomes the score. As there are a limited number of letters available, a word can only have a certain letter as many times as that letter has tiles + any unused wildcards. The Scrabble board is 15×15 cells, so the word must be between 2 and 15 characters long.

For a list of the quantity and score of each letter in the English version, see below or http://boardgames.about.com/od/scrabble/a/tile_distribute.htm (archive).

Letter  Qty  Points        Letter  Qty  Points
-------------------        -------------------
A       9    1             O       8    1
B       2    3             P       2    3
C       2    3             Q       1    10
D       4    2             R       6    1
E       12   1             S       4    1
F       2    4             T       6    1
G       3    2             U       4    1
H       2    4             V       2    4
I       9    1             W       2    4
J       1    8             X       1    8
K       1    5             Y       2    4
L       4    1             Z       1    10
M       2    3             [wild]  2    0
N       6    1


Further rules

• The program shall take a single string of input from STDIN or the like.
• The input will always contain only uppercase letters.
• If the string contains more copies of a letter than there are unused wildcards or tiles for that letter OR the string's length is not between 2 and 15 inclusive, the program should output Invalid.
• Otherwise, the score should be added up with using data from the chart above and output.
• Do not use wildcards unless necessary.
• Do not worry about bonuses such as double word scores or whether the string is a real word.
• The program shall output the result through STDOUT or the like.
• Loopholes that are forbidden by default are not allowed.
• Using an external source such as a website, as well as any libraries, APIs, functions, or the like that calculate Scrabble scores or proper quantities are not alllowed.
• This is , so fewest bytes wins.

Walkthrough

Input: CODEGOLF
C -> 3, O -> 1, D -> 2, E -> 1, G -> 2, O -> 1, L -> 1, F -> 4
3 + 1 + 2 + 1 + 2 + 1 + 1 + 4 = 15
Output: 15


Testcases

Input             Output
------------------------
SCRABBLE          14
JAZZ              19
STACKEXCHANGE     32
XYWFHQYVZVJKHFW   81
PIZZAZZ           Invalid
KIXOKEJAJAX       Invalid
MISUNDERSTANDING  Invalid

• Might want to add a test case for a valid word which uses wild cards (eg. JAZZ = 19, not 29) – Alconja Aug 5 '14 at 0:41
• You know, this challenge would be so much more evil if it involved a language whose Scrabble tiles cannot be represented with a single character, like Spanish, Basque, Hungarian, Tuvan or Welsh. – guifa Aug 10 '14 at 21:26
• Are answers specifically required to output "Invalid", or may we choose any behavior as long as it is clearly not a score? For example, -1? – Kamil Drakari Aug 15 '17 at 15:36
• @KamilDrakari It must say exactly Invalid. – NinjaBearMonkey Aug 22 '17 at 1:52

Perl 5 228 205 186 184 178 177 153 150 149 142 137 135

Run with perl -E.

Golfed:

$_=<>;@a=@b=map-ord,' 0 0@0 H '=~/./g;say s!.!($a[$q=64-ord$&]+=8)<8?$-+=1-29/$b[$q]:++$j!ge~~[2..15]&&$j<3?$-:Invalid


This solution uses some non-printable characters, so a hexdump is provided below:

00000000: 245f 3d3c 3e3b 4061 3d40 623d 6d61 702d  $_=<>;@a=@b=map- 00000010: 6f72 642c 2703 0904 0909 2030 2030 030e ord,'..... 0 0.. 00000020: 4030 0e20 0704 4809 1809 601d 0e0e 6027 @0. ..H......' 00000030: 3d7e 2f2e 2f67 3b73 6179 2073 212e 2128 =~/./g;say s!.!( 00000040: 2461 5b24 713d 3634 2d6f 7264 2426 5d2b$a[$q=64-ord$&]+
00000050: 3d38 293c 383f 242d 2b3d 312d 3239 2f24  =8)<8?$-+=1-29/$
00000060: 625b 2471 5d3a 2b2b 246a 2167 657e 7e5b  b[$q]:++$j!ge~~[
00000070: 322e 2e31 355d 2626 246a 3c33 3f24 2d3a  2..15]&&$j<3?$-:
00000080: 496e 7661 6c69 64                        Invalid


Alternatively, using Ctrl+Key:

$_=<>;@a=@b=map-ord,'^C^I^D^I^I 0 0^C^N@0^N ^G^DH^I^X^I^]^N^N'=~/./g;print s!.!($a[$q=64-ord$&]+=8)<8?$-+=1-29/$b[$q]:++$j!ge~~[2..15]&&$j<3?$-:Invalid


Ungolfed+commented:

# Read in input
$_=<>; # @a and @b: represents approximately 8x the number of tiles (when rounded up). The # non-multiple-of-8 values distinguish tiles that are given equally, but are worth # different values @b=@a=map-ord,"...."~=/./g; # above is equivalent to # @a=@b=(-03,-09,-04,-09,-09,-32,-48,-32,-48,-03,-14,-64,-48,-14,-32,-07,-04,-72,-09,-24,-09,-96,-29,-14,-14,-96); say # for each character s!.! #$q: A->-1, B->-2, etc.
# decrement number of $q tiles, add points if needed, otherwise # increment j, which counts number of wilds used # truncate(1-29/b[q]): decimal values were chosen specifically # for this to return the point value. b[q] is the number of tiles # of the qth letter after a originally given. #$- contains the score, is initially zero (if in a one line program,
#   as the golfed version is), and is always an integer
($a[$q=64-ord$&]+=8)<8 ?$- += 1 - 29/$b[$q] : ++$j # s returns length, check if between 2 and 15 !ge ~~ [2..15] # make sure less than 3 negative tiles (aka wilds) &&$j < 3 ?
# print score
$- # or invalid : Invalid  • you can squeeze at least 20 bytes out of those arrays with some creative manipulation – Sparr Aug 5 '14 at 2:23 • Gah, always one step ahead of me. :) Have an upvote. – Alconja Aug 5 '14 at 22:44 • This has been interesting, our scores have been so close all the way. +1. – Level River St Aug 13 '14 at 9:59 • Does this work with -M5.010 (penalty of 0 because it specifies a version of the language to use) rather than -e (penalty of 1)? You might be able to save a byte in the arguments. – user62131 Nov 22 '16 at 7:31 C, Rev 2, 151 145 138 Inspired by the 159-byte code in @bebe's comment, I squeezed another 8 14 21 characters out: 4 bytes saved by rearranging the length counter i. This is initialised to 1 (assuming the program takes no arguments) then multiplied by 4 every time a letter is read. It overflows to zero when the word length is greater than 15, so to check if the word length is bad, we simply check if i<5 (I put i<9 so it will still give invalid for one-letter words if the user accidentally intialises i to 2 by putting a single argument on the command line.) 4 bytes saved by changing the loop condition test to a simple &31. This requires that the word is terminated with a space (ASCII 32) or a null character (ASCII 0.) Normally keyboard input is terminated by a newline (ASCII 10) so the program's a bit inconvenient to use, because you have to type the space then press return as well to get the computer to read the buffer. For newline-terminated strings, I could match but not beat the way bebe does it. 6 13 bytes saved by changing the encoding to -(number of tiles of each letter)-(score for that letter-1)*13. This now requires a range from -4 for L,S,U to -118 for Q,Z. The reason for using negative numbers is to avoid the unprintable ASCII range 0 to 31. Instead the range used is the two's complement of the negative numbers 256-4=252 to 256-118=138. These are printable, extended ASCII characters. There are issues with copying and pasting these in Unicode (the way it simplifies back to ASCII depends on the code page installed which can lead to unpredictable results) so I have included the correct ASCII codes in the program comment. The advantage of this encoding is the elimination of the variable r as the number of tiles is always reduced by 1 (as it is stored as a negative number, we do t[x]++. Additionally, the postfix operator means we can perform this increment at the same time as adding the score to s. //char t[]={32,247,228,228,239,244,215,240,215,247,164,203,252,228,250,248,228,138,250,252,250,252,215,215,164,215,138,0}; b,s; main(i,x){ for(char t[]=" ÷ääïô×ð×÷¤ËüäúøäŠúüúü××¤×Š";x=getchar()&31;i*=4) t[x]%13? s-=t[x]++/13-1: b++; printf(i<9|b>2?"Invalid":"%d",s); }  C,184 Rev 1 173 (or 172 with compiler option) I'm using GCC, and with the compiler option -std=c99 it will allow me to move char t[]="...." into the initialization of the for loop for a saving of one additional semicolon. For readability, I have shown the program without this change, and with whitespace left in. #define T t[x[1][i]-65] i,b,s; main(int r,char**x){ char t[]="Z>>QxS=SZW6(><P>m<(<(SSWSm"; for(;x[1][i];i++) T/10? s+=r=T%10+1,T-=r*10: b++; printf(i<2|i>15|b>2?"Invalid":"%d",s); }  The trick is in the datatable. For each letter an ASCII code of the form (total score of tiles for that letter)*10+(score of one tile-1) is stored in the table t[]. At runtime, these total scores are reduced as tiles are used up. The total score of all the tiles for each letter ranges from 12 for E down to 4 for L,S,U. This form of encoding enables only printable ASCII characters to be used (ASCII 120,x for E down to ASCII 40,( for L,S,U.) Using the number of tiles would need a range from 120 down to 10, which is why I avoided it. Thanks to a #define macro, a single symbol T is used in the main program to retrieve the letter index ifrom the first commandline argument, subtract ASCII A=65 from it to give an index ,and look it up in the table T: t[x[1][i]-65]. The for loop is used more like a while loop: the loop ends when a zero byte (string terminator) is encountered in the input string. If the tiles of that letter are not exhausted (T/10 is nonzero) s is incremented by the tile score T%10+1 to keep a total score. At the same time the tile score is stored in r, so that the value in the able represented by T can be decremented by r*10 to indicate that one tile has been used. If the tiles are exhausted, the wildcard/blank counter b is incremented. The printf statement is fairly self-explanatory. if the wordlength is out of bounds or the blank count is too high, print Invalid otherwise print the score s. • Since it's now another day, you can save one character by replacing r+=(r==7)*3 with r+=r-7?0:3. Also you don't need the brackets round T-=r*9,s+=r. – Alchymist Aug 7 '14 at 7:46 • @Alchymist Thanks for the tip about the brackets, I always forget there's no issues with operator precedence between ? and :. Your other point is superseded, as I have completely changed the encoding so there's no need for any special handling of Q and Z. Now down to 173/172 with your help. – Level River St Aug 7 '14 at 20:09 • with getchar() it's 159: l,w,f;main(t,i){for(char b[]="Z>>QxS=SZW6(><P>m<(<(SSWSm";(i=getchar()-65)>=0;l++)b[i]/10?f+=t=b[i]%10+1,b[i]-=t*10:w++;printf(l<2|l>15|w>2?"Invalid":"%d",f);} although I still don't get why char*foo=<string> crashes. it could save 2 chars. – bebe Aug 8 '14 at 16:04 • @bebe char*foo="string" is a string literal, and its contents are not allowed to be modified. On the other hand, char foo[]="string" creates an array of chars initialized to string\0, which may then be modified. – es1024 Aug 9 '14 at 12:10 • @bebe cool, I missed the idea of using getchar(). I have used your improvements to the code (with my variable names for consistency with the rest of my answer), plus an improvement to the wordlength validity check, and a cheeky improvement to the loop condition test (I tried shortening yours but could not do so with the same functionality.) I also tried getche() and getch() but my compiler (gcc on cygwin) would not link them automatically. – Level River St Aug 10 '14 at 1:18 JavaScript (ES6) - 241230199 182 f=s=>{for(i=t=_=0,E=12,A=I=9,B=C=M=P=28,D=17,F=H=V=W=Y=41,G=16,J=X=92,K=53,L=S=U=4,N=R=T=6,O=8,Q=Z=118;c=s[i++];)this[c]%13<1?_++:t+=1+this[c]--/13|0;alert(i<3|i>16|_>2?"Invalid":t)}  Edit - changed the way I encoded the quantities/scores to reduce size and remove non-ascii variables Edit 2 - changed the quantity/score encodings to integers instead of strings Edit 3 - switched to %13 (thanks @edc65), inverted the encoding, modified the values directly, and a few other minor improvements Tested in Firefox console. • +1 very clever. Suggestions: 1.f[c]=1+f[c]||1 -> f[c]=-~f[c], 2.why not use % 13 – edc65 Aug 5 '14 at 11:55 • 192 f=s=>{for(E=12,A=I=9,B=C=M=P=28,D=17,F=H=V=W=Y=41,G=16,J=X=92,K=53,L=S=U=4,N=R=T=6,O=8,Q=Z=118,$=2,t=i=0;c=s[i++];)(f[c]=-~f[c])>(l=this[c])%13?--$:t+=l/13+1|0;alert(i<3|i>16|$<0?"Invalid":t)} – edc65 Aug 5 '14 at 12:14
• @edc65 - Thanks heaps. Hadn't seen that first trick, but didn't end up using it as I'm directly modifying the values now (mentally filing it away for future golf). %13 is a stroke of genius though. I got stuck thinking I had to store things in digits, but maths doesn't care about the difference between base10 and base13. – Alconja Aug 5 '14 at 22:42
• Nice! (Doesn't work in Chrome's console, BTW: SyntaxError: Unexpected token >.) – DLosc Aug 6 '14 at 0:57
• @DLosc - Yeah, I think Firefox is the only browser at the moment that supports all the ECMAScript 6 stuff (Chrome doesn't like the f=s=>{...} notation). – Alconja Aug 6 '14 at 2:00

Python 3, 217 201

b=2;i=s=0;w=input()
while i<26:n=w.count(chr(i+65));q=int('9224c232911426821646422121'[i],16);b-=max(0,n-q);s+=min(n,q)*int('1332142418513113a11114484a'[i],16);i+=1
print(["Invalid",s][-b<1<len(w)<16])


Ungolfed:

b=2    # number of blanks available
i=s=0  # letter index 0..25, running score tally
w=input()

# Loop through each letter of the alphabet
while i<26:
# Get number of occurrences in the word
n=w.count(chr(i+65))
# Get quantity of the letter from hex encoded string
q=int('9224c232911426821646422121'[i],16)
# Remove blanks for each occurrence over that letter's quantity
b-=max(0,n-q)
# Score the non-blank tiles, getting scores from hex-encoded string
s+=min(n,q)*int('1332142418513113a11114484a'[i],16)
# Increment
i+=1

# If b > -1 and 1 < len(w) < 16, print the score; otherwise, print "Invalid"
print(["Invalid",s][-b<1<len(w)<16])


Edit: Thanks to @BeetDemGuise for a tip that ultimately led me to much more than a 1-character reduction! Original code below:

q=[77-ord(x)for x in'DKKIAKJKDLLIKGEKLGIGIKKLKL'];b=2;s=0;w=input()
for c in set(w):n=w.count(c);o=ord(c)-65;b-=max(0,n-q[o]);s+=min(n,q[o])*(1+int('02210313074020029000033739'[o]))
print(["Invalid",s][-b<1<len(w)<16])

• Its quite minimal but you can save 1byte by encoding your scores string in hex: int('1332142418513113a11114484a'[o],16) :) – BeetDemGuise Aug 6 '14 at 20:07

BEFUNGE 93 - 210 bytes.

But it doesn't check the 15 letter limit.

v1332142418513113:11114484: >01g:"0"-!#v_1-01p1+\v
9224<232911426821646422121v  "Invalid"<      vp0<
<vp00p10"20"p200p900
>>~:55+-!#v_"@"-::1g:"0"-! #^_1-\1p0g+"0"-02g+>02p
_v#:-1<    #p90+g90-"0"g1:<
@.g20<        @,,,,,,,<


C, 197

Assumes the string is provided as a command line argument, e.g. ./scrabble STACKEXCHANGE

s;n;m=31;main(int c,char**v){char d[]="BIBBDLBCBIAADBFHBAFDFDBBABA@ACCBADBDAHEACAACJAAAADDHDJ";for(;c=*v[1]++&m;d[c]--,s+=d[c+27]&m)n+=1+m*(!(d[c]&m||d[c=0]&m));printf(n>1&&n<16?"%d":"Invalid",s);}


JavaScript - 232 201

t=[9,2,2,4,12,2,3,2,9,1,1,4,2,6,8,2,1,6,4,6,4,2,2,1,2,1];w=r=0;for(i=y=z.length;i--;){x=z.charCodeAt(i)-65;if(!t[x])w++;else{t[x]--;r+=-~"02210313074020029000033739"[x]}}alert(w>2|y<2|y>15?"Invalid":r)


z stores word. Outputs as alert.

Edit: improved as per recommendations below.

• s is only used once, so you don't need to make it a variable at all; you can remove that declaration and replace r+=s[x] with r+=-~"02210313074020029000033739"[x]. Also, you don't need parentheses around (w>2|y<2|y>15) in the alert. – NinjaBearMonkey Aug 5 '14 at 16:52

Save it as scrabble.hs and then compile it using

ghc --make scrabble && ./scrabble


Then enter your word as input and press enter

l=['A'..'Z']
sc=zip l [1,3,3,2,1,4,2,4,1,8,5,1,3,1,1,3,10,1,1,1,1,4,4,8,4,10]
vfs a y =snd $filter (\x -> fst x == y) a !! 0 q = zip l [9,2,2,4,12,2,3,2,9,1,1,4,2,6,8,2,1,6,4,6,4,2,2,1,2,1] i s =filter (\x -> (fst x) >=0) [(length [x | x <- s, x == a] - vfs q a,a) | a <- l] main = do s <- getLine if length s <= 15 && length s > 2 && sum (map fst (i s)) <= 2 then putStrLn$ show (sum [vfs sc x| x <- s] - sum [(vfs sc (snd x)) * (fst x) | x <- (filter (\x -> fst x > 0) (i s))])
else do
putStrLn "Invalid"

• You can remove a lot of spaces and in Haskell ['A', 'B', 'C'] == "ABC". Also you can use only one space for each level of indent. And you can use shorter names. There is a lot for golfing. – Ray Aug 6 '14 at 17:08
• @Ray Have done that, I am new to Haskell, is there a way to represent lists of Ints more concisely than [1,2,3]? – Tuomas Laakkonen Aug 6 '14 at 17:25
• "ABCDEFG" can be written as ['A'..'G'], [1,2,3] can be written as [1..3] – Ray Aug 6 '14 at 17:56
• How do you obtain your byte count? wc gives me over 500 characters for your code. – TheSpanishInquisition Aug 7 '14 at 18:37
• @TheSpanishInquisition Just got a update for my st3 word count extension, the author had swapped the two counts accidentally, edited to 538 – Tuomas Laakkonen Aug 8 '14 at 2:18

Python 2.7 - 263

I couldn't come anywhere close to DLosc's answer, but this treats each letter as a 'bag' that you pull from, until its empty, then you pull blanks, and when that is empty it errors.

S=input().lower()
X={chr(97+i):[int(y)+1]*(77-ord(x))for i,(x,y)in enumerate(zip('DKKIAKJKDLLIKGEKLGIGIKKLKL','02210313074020029000033739'))}
B=[0,0]
try:
if len(S)>15:1/0
print sum(map(lambda x:X[x].pop()if len(X[x])>0 else B.pop(),S))
except:
print "invalid"

• This is a neat approach! You need raw_input if it's Python2 (one thing I like about Python3). The input is guaranteed uppercase, so remove .lower() and change 97+i to 65+i. Input less than 2 characters also needs to be invalid. You can raise the zero-division error without an if statement: divide your total score by (1<len(S)<16). A couple other tweaks like putting the prints on the same line as the block headers and deleting the space before "Invalid" brings it down to 250 by my count. :) – DLosc Aug 6 '14 at 20:29

As far down as I could make it for now:

import Data.List
t="CdC8d::Od;D;d41N:dd:6dNdN;;4;6"
s w@(_:_:_)=let d=concat(zipWith(replicate.(div11).f 33)t("AEIO"++['A'..]))\\w;y=drop 15w in if length(d++w++y++y++y)>100 then s""else show$187-(sum$map((mod11).f 0.(t!!).f 61)d)
s _="Invalid"
main=interact s


This code abides by the rules very strictly, so make sure you don't pass it any extra characters (such as end-of-line). Use like this: echo -n "JAZZ" | runghc scrabble.hs.

Explanation

The pattern (_:_:_) makes sure that only strings of at least two characters are considered, everything else results in "Invalid" (fallback pattern _). The table of tiles is encoded as 11*nTiles+value converted to ASCII with an offset that allows the lookup modulo 11 to work, where the letters AEIO are duplicated because they occur more than 6 times each. The pool of tiles is then created using replicate, from which the characters in the word are removed as they occur (list difference, \\). The pool contains 98 tiles, so if the total length of the word and the remaining part of the pool is larger then 100, then we have used too many wildcards. Also, the word minus the first 15 letters is added three times to the length computation so any word longer than 15 letters automatically appears to use up three wildcards and therefore is invalid. Scoring is done on the remaining pool, which initially had 187 points, which we simply subtract from. Note the f 61 rather than f 65, 65 being the ASCII number of 'A', because of the duplicate "AEIO" at the beginning of the pool. The rest is just boilerplate.

Python3 - 197

s,i,c,r=input(),0x1a24182424416141611a2381612341151891243224c142232391,[],[]; p=len(s)
for w in s:e=8*ord(w)-520;c+=[s.count(w)<=i>>e+4&15];r+=[i>>e&15]
print(['Invalid',sum(r)][all([p>2,p<15]+c)])


Let's put the bignums to use :D (It doesn't handle wildcards currently, I've skipped reading that rule entirely, damn)

Ruby - 195

b=2
i=s=0
w=$*[0] (?A..?Z).map{|l|n=w.count(l);q='9224c232911426821646422121'[i].to_i(16);b-=[0,n-q].max;s+=[n,q].min*'1332142418513113a11114484a'[i].to_i(16);i+=1} p(-b<1&&w.size<16?s:'Invalid')  I'm assuming the output of "Invalid" is fine, if not I would need to do $><<(-b<1&&w.size<16?s:'Invalid') which would bump it up to 198

Clojure - 325

I haven't done clojure in a while so I'm sure there are several ways to improve my solution .ie the qty and pts lists

(let[w(first *command-line-args*)o(map #(count(filter #{%}(seq w)))(map char(range 65 91)))i(apply +(filter neg?(map #(- % %2)'(9 2 2 4 12 2 3 2 9 1 1 4 2 6 8 2 1 6 4 6 4 2 2 1 2 1) o)))](println(if(or(> -2 i)(not(<= 2(count w)15)))"Invalid"(apply +(map #(* % %2)o'(1 3 3 2 1 4 2 4 1 8 5 1 3 1 1 3 10 1 1 1 1 4 4 8 4 10))))))


Some what Un-golfed

(let [word    (first *command-line-args*)
letters (map char(range 65 91))
occ     (map #(count (filter #{%} (seq word))) letters)
invalid (apply + (filter neg? (map #(- % %2)
'(9 2 2 4 12 2 3 2 9 1 1 4 2 6 8 2 1 6 4 6 4 2 2 1 2 1)
occ)))
score   (apply + (map #(* % %2) occ '(1 3 3 2 1 4 2 4 1 8 5 1 3 1 1 3 10 1 1 1 1 4 4 8 4 10)))]
(println
(if (or (> -2 invalid)
(not (<= 2 (count word) 15)))
"Invalid"
score)))


ES6: 184 (non-strict)

w is assumed to already contain the word. r is the output string.

i=0,o=[..."291232342c124322491181541236181231a61416141242418241a"].map(c=>parseInt(c,16)),r=!w[16]&&w[2]&&[...w].every(c=>o[c=c.charCodeAt()*2-129]-->0?i+=o[c+1]:o[0]--)?i+"":"Invalid"


Here's it explained and a little less golfed:

// The sum.
i = 0,

// The data for the letters. It's encoded similar to the Ruby version, with
// the first being the wildcard holder. The rest hold in hex form the
// following: first = quantity left, second = value.
// The .map(c => parseInt(c, 16) simply parses all the hex characters.
o = [..."291232342c124322491181541236181231a61416141242418241a"]
.map(c => parseInt(c, 16)),

// The result, r.
r = !w[16] || // If there is a 16th character in the word or no 2nd character,
w[2] &&   // then the next section isn't evaluated. It immediately equates
// to true, thus returning "Invalid".
[...w] // Convert the string into an array of characters (ES6 equivalent to
// .split('')
.every(c => // This loop terminates when the callback returns a falsy
// value.
// Gets the ASCII value, subtracts 65, doubles it (the lookup table is
// in pairs within one array), and decrements the counter at that entry.
// The lookup table also doubles as a data holder.
o[c = c.charCodeAt() * 2 - 129]--
> 0 ?  // Test if there is something to take away. This must return
// false at 0 and -1 so wildcards can be taken.
i += o[c+1] : // If there was something to take away, then add the
// letter value to the sum.
o[0]--) // Otherwise, take a wildcard. If this is already at 0, then
// it returns falsy.
? "Invalid" : i + "" // This is where the text is returned.


Dart - 201

main(a,{x:0xa14281424214161416a132181632145181194223421c24323219,i,r:0,m,s:2}){if((m=a[0].length)>1&&m<16)for(i in a[s=0].codeUnits)x>>(m=i*8-520)&15>0?r+=(x-=1<<m)>>m+4&15:++s;print(s<2?r:"Invalid");}


This requires bignums, so it won't compile to JavaScript.
With more whitespace:

main(a,{x:0xa14281424214161416a132181632145181194223421c24323219,i,r:0,m,s:3}){
if((m=a[0].length)>1&&m<16)
for(i in a[s=0].codeUnits)
x>>(m=i*8-520)&15>0
? r+=(x-=1<<m)>>m+4&15
: ++s;
print(s<3?r:"Invalid");
}


PHP, 180170 168 bytes

for($q=str_split(KDKKIAKJKDLLIKGEKLGIGIKKLKL);$o=31&ord($argv[1][$i++]);)$s+=$q[$o]++>L?$q[0]++>L?$f=1:0:X02210313074020029000033739[$o]+1;echo$f|$i<3|$i>16?Invalid:$s;


Yay! beating JS!

breakdown

for(
$q=str_split(KDKKIAKJKDLLIKGEKLGIGIKKLKL); // init quantities: L=1,A=12$o=31&ord($argv[1][$i++]);                  // loop through characters: map to [1..26]
)
$s+= // increase score by ...$q[$o]++>L? // old quantity below 1?$q[0]++>L?$f=1 // no more wildcards? set error flag :0 // wildcard: 0 points :X02210313074020029000033739[$o]+1;         // else: letter score
echo$f|$i<3|$i>16?Invalid:$s;                   // output
`

I´m so glad there is no letter score larger than 10.