# Score a Scrabble Play

## Background

In Scrabble, players take turns placing tiles on a grid so that each contiguous set of (more than one) tiles in every row and column makes a word. In one play, tiles can be placed anywhere in a single row or column as long as there is a contiguous set of tiles that includes all of the ones placed.1 A word is scored (without considering premium score spaces) by adding up the point value of each of its letters. The point values of the letters are as follows:

1 point: E, A, I, O, N, R, T, L, S, U
2 points: D, G
3 points: B, C, M, P
4 points: F, H, V, W, Y
5 points: K
8 points: J, X
10 points: Q, Z

A play is scored by adding up the scores of each new word created in a play. For example, in the play below, N and W were played to form three new words, scoring 5 (PAN) + 6 (SEW) + 6 (NEW) = 17 points.

PAST      PAST
AXE   ->  AXE
E        NEW

Apart from the starting play, each play must involve at least one already existing tile, so that it is connected to the rest of the board.

## The Challenge

Your challenge is to write a function which takes a play and returns the total points scored in that turn. You do not have to consider the legality of the words formed by the play, or any premium score squares. However, you should assume that the placement of the play will be valid (i.e. will connect to the board and be placed in a line) and that the board will be nonempty before the play. Unlike in Scrabble, a play can be more than 7 tiles, and the grid can be larger than 15x15.

Your function should take a mapping of the letters to their point values as a parameter. In addition to the letter point values, the function should take input in one of the following acceptable ways:

• Two grids representing the board, with one showing the board before the play and one showing the board after the play.

• A grid showing the board after the play and a list of the coordinates at which tiles were placed.

• A grid showing the board either before or after the play, and a map containing each letter of the play with the coordinate at which it was placed.

The grid can be exactly big enough to contain the relevant squares, or can be padded to any larger size. This is Code Golf so the fewest bytes wins.

## Examples

The examples use the first input method, with the board before and after the play separated with | and the expected output in bold along with the words formed above each example.

17 (PAN, SEW, NEW)

PAST | PAST
AXE  | AXE
E   | NEW

18 (ST, OO, LO, IT, DE, TOOTED)

SOLID | SOLID
| TOOTED

9 (HOPE)

|  H
ROLL | ROLL
|  P
|  E

4 (DOT)

A   | A O
NOT | NOT

6 (NEW)

PASTURE | PASTURE
AXE  Y  | AXE  Y
NEW  E  | NEW NEW

13 (PROGRAMS)

GRAM | PROGRAMS

1 This set must be in a single row or column, but can include letters that were already on the board, i.e. GRAM -> PROGRAMS

• Also, is SING -> SINGER a valid testcase? If so, also suggest add it.
– tsh
Jan 28, 2022 at 2:33
• how about | A or | RAN? Jan 28, 2022 at 13:52
• Your example placing TOOTED should result in a score of $18$ from TOOTED ($7$), ST ($2$), OO ($2$), LO ($2$), IT ($2$), and DE ($3$). Jan 28, 2022 at 20:50
• @pajonk You should assume the play is valid. By 'legality' I meant the legality of the words themselves. Jan 29, 2022 at 18:46
• @thejonymyster Your program does not need to consider these cases. Jan 29, 2022 at 19:13

# Python3, 387 bytes:

import re,itertools as I
lambda o,n:sum(sum(p[i]for i in j)for j in q(n)if all(j not in k for k in q(o))and len(j)>1)
q=lambda o:[j for k in[*map(list,I.zip_longest(*o,fillvalue=' '))]+o for j in re.findall('\w+',''.join(k))]
p={'E':1,'A':1,'I':1,'O':1,'N':1,'R':1,'T':1,'L':1,'S':1,'U':1,'D':2,'G':2,'B':3,'C':3,'M':3,'P':3,'F':4,'H':4,'V':4,'W':4,'Y':4,'K':5,'J':8,'X':8,'Q':10,'Z':10}

Try it online!

• You don't have to store the letter values in your answer. You can add p to the lambda's arguments and delete the dictionary and your answer will still be acceptable. Also what format is the input for your answer in? Jan 27, 2022 at 23:06
• Also, if you change p to a function, you can save 6 bytes by changing sum(p[i]for i in j) to sum(map(p,j)) Jan 27, 2022 at 23:28
• 294 bytes Jan 28, 2022 at 3:23
• Also, I'm pretty confident the $16$ for placing TOOTED should be $18$ (see my comment under OP), maybe you both have a similar bug (zip vs zip_longest?) EDIT: I tried adding spaces to pad the right, but I think your format function scuppers any attempt at that. Jan 28, 2022 at 21:31
• @JonathanAllan Repeated words are allowed. I've clarified my challenge and added some test cases as well. Jan 29, 2022 at 19:15

# Charcoal, 59 bytes

ＳθＷＳ⊞υιＵＭυ⭆Ｓ⌈⟦λ↧§ιμ⟧ＩΣＥ⁺υＥ§υ⁰⭆υ§λκΣＥ⪪ι ∧∧‹¹Ｌλ№α⌊λΣＥ↥λ⊕§θ⌕αν

Try it online! Link is to verbose version of code. Takes as input:

1. A string of 26 digits representing the values of the letter tiles minus 1.
2. The previous board state, as a rectangular list of strings
3. An empty line to delimit the two boards
4. The new board state, as a rectangular list of strings

The two boards must be the same size rectangle. Explanation:

Ｓθ

Input the decremented tile values.

ＷＳ⊞υι

Input the previous board state.

ＵＭυ⭆Ｓ⌈⟦λ↧§ιμ⟧

Input the new board state, but lowercase all letters that aren't new.

Ｅ⁺υＥ§υ⁰⭆υ§λκ

Loop over the new board and its transpose.

ΣＥ⪪ι

Map over each word and take the sum.

∧∧‹¹Ｌλ№α⌊λ

Only score words of at least two letters of which at least one is upper case.

ΣＥ↥λ⊕§θ⌕αν

Calculate the score of the word.

ＩΣ

Output the final total.

# Jelly, 20 bytes

,ZḲÇ€€€€Ẏ)œ-"/ẎḊƇFS A monadic Link that accepts a pair of boards (lists of lists of characters), the first being the board's state after the turn has been played, that yields the score. The boards must fully align, using spaces to pad as necessary. Try it online! (The header is a Link that performs the lookup of the value of a tile by its character*) Or see the test-suite. * Blank tiles can be incorporated by using a new character, e.g. b, too. ### How? ,ZḲÇ€€€€Ẏ)œ-"/ẎḊƇFS - Link: pair of lists of equal-length lists of characters
)          - for each grid in the pair:
Z                   -   tranpose
,                    -   pair the grid and its transpose
\$€€            -   for each line in each of those:
Ḳ                  -     split at spaces
€€               -     for each tile character in each of those:
Ç                 -       call our tile value finding function
Ẏ           -   tighten
/      - reduce by:
"       -   zip with:
œ-        -     multiset difference
Ẏ     - tighten
Ƈ   - keep only those for which:
Ḋ    -   dequeue -> falsey for length one words
F  - flatten
S - sum

# Clojure, 180 bytes

#(let[s(fn[x](frequencies(for[y(concat x(apply map vector x))w(partition-by #{\ }y):when(second w)]w)))b(s %2)](apply +(for[[k v](merge-with -(merge b(s %3))b)n k](*(or(% n)0)v))))

Try it online!

Anonymous function, accepts the three arguments in the order of: scoring map, board before, board after.