Scrabble Score in 8th: Complete Solution & Deep Dive Guide

Tabs labeled

Mastering Scrabble Score Logic in 8th: A Deep Dive from Zero to Hero

Calculating a Scrabble score in 8th involves mapping each letter of a word to its corresponding point value and then summing these values. This is idiomatically achieved using data structures like arrays for letter-to-score mapping and powerful functional constructs like s:reduce to process the word string efficiently.


You've just started exploring a new programming paradigm, the world of stack-based languages, and you've chosen 8th. The syntax is minimal, the concepts seem powerful, but then you hit a familiar problem: mapping data. You're tasked with something that sounds simple—calculating a Scrabble score—but the path from a string of characters to a final integer score feels obscured by the very stack you're trying to master.

This isn't just about loops and variables anymore. How do you represent the letter values? How do you iterate over a string and accumulate a result without traditional loop counters? This guide is designed to lift that fog. We will dissect the Scrabble Score problem, not just to find a solution, but to deeply understand the "8th way" of thinking. By the end, you'll have mastered a core data transformation task and gained a profound insight into the elegance of stack-based, functional programming.


What is the Scrabble Score Problem?

Before diving into the code, it's crucial to fully grasp the problem's domain. The Scrabble Score challenge, a classic in programming education, is derived from the board game Scrabble. In the game, players form words using lettered tiles, and each tile has a point value. The goal is to compute the total score for a given word by summing the values of its constituent letters.

The Core Logic and Letter Values

The entire problem revolves around a fixed mapping of letters to scores. For the standard English version of the game, this mapping is as follows:

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

The primary programming task is to take an input string, for example, "KODIKRA", and calculate its score. This involves three fundamental steps:

  1. Iteration: Process each character in the input string one by one.
  2. Mapping/Lookup: For each character, find its corresponding point value from the table.
  3. Accumulation: Sum all the retrieved point values to get a final, single integer result.

A key requirement is that the calculation must be case-insensitive. The letter 'a' is worth the same as 'A'. Therefore, a robust solution must normalize the input string, typically by converting it entirely to uppercase or lowercase before processing.


Why is 8th a Unique Choice for This Task?

Solving this problem in a language like Python or Java might involve familiar constructs like for loops and dictionaries. However, 8th, as a concatenative, stack-based language inspired by Forth, approaches data manipulation from a completely different angle. Understanding this paradigm shift is key to writing elegant and idiomatic 8th code.

The Stack-Based Paradigm

In 8th, everything revolves around the data stack. There are no local variables in the traditional sense. Words (the equivalent of functions or methods) operate by consuming values from the top of the stack and pushing results back onto it. Data flows from one word to the next, creating a processing pipeline.

This design encourages a style of programming known as "point-free" or "tacit" style, where you define functions by composing other functions, without ever explicitly mentioning the data they operate on. The data is implicitly on the stack. This can lead to extremely concise and expressive code once you adapt to the way of thinking.

Functional Programming at its Core

8th heavily embraces functional programming principles. Words like a:map, a:filter, and, most importantly for our task, s:reduce, are fundamental tools. These "higher-order" words take other words as arguments, allowing you to apply custom logic to collections of data like strings or arrays. For the Scrabble problem, s:reduce is the perfect tool to iterate over a string and accumulate a value, neatly encapsulating the iteration and accumulation steps into a single, powerful operation.

Let's visualize the data flow of a reduction process on the stack, which is central to our solution.

● Start with String "CAB" and initial accumulator 0 on stack
  Stack: "CAB", 0

    │
    ▼ s:reduce begins
    │
  ┌────────────────────────┐
  │ s:reduce takes 'C', 0  │
  │ Applies scoring logic  │
  │ Pushes new total (3)   │
  └──────────┬─────────────┘
             │
  Stack: "AB", 3
             │
             ▼
  ┌────────────────────────┐
  │ s:reduce takes 'A', 3  │
  │ Applies scoring logic  │
  │ Pushes new total (4)   │
  └──────────┬─────────────┘
             │
  Stack: "B", 4
             │
             ▼
  ┌────────────────────────┐
  │ s:reduce takes 'B', 4  │
  │ Applies scoring logic  │
  │ Pushes new total (7)   │
  └──────────┬─────────────┘
             │
  Stack: (empty string), 7
             │
             ▼
● End: Final score 7 is left on the stack

This diagram illustrates how s:reduce consumes the string character by character, continuously updating an accumulator on the stack until only the final result remains. This is the essence of functional data transformation in 8th.


How to Implement the Scrabble Score Logic in 8th

Now, we'll perform a deep dive into the idiomatic 8th solution provided in the kodikra.com learning module. We will break down each component line by line to reveal the underlying mechanics and design choices.

The Complete Solution Code

[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] constant letter-scores

: score-letter \ n n -- n
  letter-scores swap 'A' n:- caseof n:+ ;

: score \ s -- n
  s:uc ' score-letter 0 s:reduce ;

Part 1: Defining the Letter-to-Score Mapping

[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] constant letter-scores
  • [...]: This defines an array of integers. This array is the core of our data mapping.
  • The Order is Key: The values are carefully ordered to correspond to the letters of the alphabet. The first value, 1, is the score for 'A'; the second, 3, is for 'B'; the third, 3, is for 'C', and so on, up to 10 for 'Z'. This is a clever and memory-efficient way to store the mapping, relying on a character's position in the alphabet as an implicit key.
  • constant: This is a defining word in 8th. It takes the value currently on top of the stack (our array) and binds it to a new word, in this case, letter-scores. Whenever letter-scores is executed, it will push this constant array onto the stack. This is more efficient than defining the array inline every time it's needed.

Part 2: The Scoring Logic for a Single Letter

: score-letter \ n n -- n
  letter-scores swap 'A' n:- caseof n:+ ;

This is the heart of the calculation, a custom word designed to be used with s:reduce. Let's analyze its stack effect comment: \ n n -- n. This tells us it expects two numbers on the stack (an accumulator and a character's ASCII value) and will leave one number (the updated accumulator) on the stack.

  • : score-letter ... ;: This defines a new word named score-letter.
  • letter-scores: The word starts by pushing our constant array of scores onto the stack. The stack now looks like: [accumulator, character, score_array].
  • swap: This word swaps the top two items on the stack. The stack becomes: [accumulator, score_array, character]. This is a crucial maneuver to get the arguments in the correct order for the subsequent words.
  • 'A': This pushes the ASCII numerical value of the character 'A' onto the stack. The stack is now: [accumulator, score_array, character, 65].
  • n:-: This is the subtraction word. It subtracts the top item from the second-to-top item. It calculates character - 65. Since our input will be uppercase, this operation converts an ASCII value into a 0-based array index ('A' (65) becomes 0, 'B' (66) becomes 1, etc.). The stack is now: [accumulator, score_array, index].
  • caseof: This is 8th's word for array indexing. It takes an array and an index from the stack and pushes the value at that index. It effectively performs score_array[index]. The stack now holds: [accumulator, letter_value].
  • n:+: Finally, the addition word. It adds the two top values on the stack, calculating accumulator + letter_value. The stack is left with the single, updated accumulator value, fulfilling the word's promised stack effect.

Part 3: The Main Word to Score a Full String

: score \ s -- n
  s:uc ' score-letter 0 s:reduce ;

This is the public-facing word that ties everything together. Its stack effect \ s -- n indicates it takes a string and returns a number.

  • : score ... ;: Defines our main word, score.
  • s:uc: This word operates on the input string, converting it to uppercase. This handles the case-insensitivity requirement elegantly and ensures our indexing logic ('A' n:-) works correctly.
  • ' score-letter: The tick or apostrophe (') is a special word that pushes the execution token (a reference or pointer) of the word that follows it onto the stack. We are not executing score-letter here; we are passing it as an argument to s:reduce.
  • 0: This pushes the initial value for our accumulator. The scoring starts at zero.
  • s:reduce: This is the functional powerhouse. It works on a string and requires two arguments: an initial accumulator value and an execution token for a reducer word. It iterates through the string, and for each character, it calls the reducer word (score-letter) with the current accumulator and the character's value on the stack. The result from the reducer word becomes the new accumulator for the next iteration. After the last character is processed, s:reduce leaves the final accumulator value on the stack.

Example Execution Trace

Let's trace the execution of "Hi" score:

  1. "Hi" is on the stack.
  2. score is called.
  3. s:uc runs, changing the stack to "HI".
  4. ' score-letter pushes the execution token for our reducer.
  5. 0 pushes the initial accumulator. Stack: ["HI", <xt:score-letter>, 0].
  6. s:reduce consumes these three items and begins its work.
  7. Iteration 1 (Char 'H'):
    • s:reduce internally calls score-letter with stack [0, 72] (0 is accum, 72 is 'H').
    • score-letter executes, calculates the score for 'H' (which is 4), and adds it to 0.
    • It leaves 4 on the stack. This is the new accumulator.
  8. Iteration 2 (Char 'I'):
    • s:reduce calls score-letter with stack [4, 73] (4 is accum, 73 is 'I').
    • score-letter executes, calculates the score for 'I' (which is 1), and adds it to 4.
    • It leaves 5 on the stack.
  9. The string is exhausted. s:reduce finishes and leaves the final accumulator, 5, on the stack.

Where Can This Logic Be Optimized or Refactored?

The array-based solution is concise and performs well for its specific purpose. However, it's not the only way to solve the problem. Exploring alternatives helps us understand design trade-offs in software engineering. A common alternative is to use a hashmap (or map in 8th terminology) for a more explicit and potentially more readable mapping.

Alternative: A Hashmap-Based Approach

A hashmap explicitly stores key-value pairs. This can make the code easier to read because you see the letter and its score together, rather than relying on an implicit array index. It's also more flexible if you ever need to score non-alphabetic characters or use a non-contiguous character set.

Implementation Using a Map


\ Define the score map
( "a" 1 "b" 3 "c" 3 "d" 2 "e" 1 "f" 4 "g" 2 "h" 4 "i" 1 "j" 8
  "k" 5 "l" 1 "m" 3 "n" 1 "o" 1 "p" 3 "q" 10 "r" 1 "s" 1 "t" 1
  "u" 1 "v" 4 "w" 4 "x" 8 "y" 4 "z" 10 ) m:new constant letter-map

: score-alt \ s -- n
  0 swap                               \ Setup accumulator and string
  s:lc                                 \ Convert string to lowercase for map keys
  [ letter-map m:@ ] s:map             \ Map each char to its score
  a:sum                                \ Sum the array of scores
;

Walkthrough of the Hashmap Solution

  1. ( ... ) m:new constant letter-map: We create a new map where keys are lowercase letter strings and values are their scores. We store this in a constant called letter-map for reusability.
  2. : score-alt ... ;: We define a new scoring word.
  3. 0 swap: The word starts with an input string on the stack. We push 0 (which will be our final result if the string is empty) and then swap it, so the stack is [string, 0]. This setup isn't strictly necessary for this implementation but shows a common pattern. A better way is shown in the final code block.
  4. s:lc: We convert the string to lowercase to match the keys in our letter-map.
  5. [ letter-map m:@ ] s:map: This is the core transformation.
    • s:map is a word that applies a quotation (the code inside [ ... ]) to each character of a string, returning an array of the results.
    • Inside the quotation, letter-map m:@ looks up the character (which s:map places on the stack) in our map and returns its value.
    • The result is an array of scores, e.g., "cab" becomes [3, 1, 3].
  6. a:sum: This convenient word simply sums all the numbers in an array, giving us the final score.

This approach uses s:map followed by a:sum, which is a very common and readable functional pattern, equivalent to the single s:reduce operation in the first solution.

Here is a flow diagram illustrating this key-value lookup process.

● Start with char 'K'

    │
    ▼
  ┌─────────────────┐
  │ Access letter-map │
  └────────┬────────┘
           │
           ▼
    ◆ Find key 'k'?
   ╱         ╲
  Yes         No (handle error/default)
  │            │
  ▼            ▼
┌─────────┐  ┌───────────┐
│ Get Value │  │ Return 0  │
│   (5)   │  │ or ignore │
└─────────┘  └───────────┘
    │
    ▼
● Return score 5

Pros and Cons: Array vs. Hashmap

Choosing between these two implementations involves weighing readability, performance, and flexibility.

Aspect Array-Index Solution Hashmap Solution
Readability Less readable. The 'A' n:- logic is clever but requires understanding ASCII math. The score values are disconnected from their letters. More readable. The map explicitly pairs letters with scores, making the data structure self-documenting.
Performance Potentially faster. Array lookups by index are typically O(1) and extremely fast. Arithmetic operations are cheap. Slightly slower. Hashmap lookups involve a hashing function and have a theoretical average time complexity of O(1), but with higher overhead than a simple array access.
Flexibility Low. Tightly coupled to the contiguous A-Z English alphabet. Difficult to adapt for languages with accents or different character sets. High. Can easily handle any character-to-score mapping, including non-alphabetic symbols or multi-language support, just by changing the map data.
Memory Usage Very low. Only stores 26 integers. Higher. Stores 26 keys (strings) and 26 values (integers), plus the overhead of the hashmap data structure itself.

When Should You Use Each Approach?

The decision of which implementation to use depends entirely on the context and future requirements of your project.

The Array-Index solution is perfect for this specific problem as defined in the kodikra.com module. The constraints are fixed: English alphabet, A-Z. In this scenario, it is a highly efficient and clever solution that showcases a deep understanding of character encoding and low-level optimization. It's an excellent example of "performance-conscious" idiomatic code.

The Hashmap solution is the superior choice for general-purpose, production-grade software. If there is any chance that the requirements might change—for example, adding support for Spanish Scrabble with 'LL' and 'Ñ' tiles, or creating a version with different scoring for special bonus characters—the hashmap provides the necessary flexibility. Its enhanced readability also makes the code easier to maintain and debug for a team of developers.

For your learning journey, it is vital to understand both. The first teaches you about efficiency and the clever tricks possible in a low-level-capable language like 8th. The second teaches you about building robust, maintainable, and flexible systems—a cornerstone of professional software development.


Frequently Asked Questions (FAQ)

What is a stack-based language like 8th?
A stack-based language is one that uses a Last-In, First-Out (LIFO) stack for passing arguments to functions (called "words"). Instead of assigning values to named variables and then passing those variables, you push values onto a global data stack, and words automatically consume their inputs from and push their results back to this stack.
How does s:reduce work in 8th?
s:reduce is a higher-order function that "reduces" a string to a single value. It takes an initial value (an accumulator) and a reducer word. It iterates over the string, applying the reducer word to the current accumulator and the current character. The result of the reducer word becomes the accumulator for the next iteration.
Why is it necessary to convert the input string to uppercase or lowercase?
The Scrabble scoring rules are case-insensitive ('a' scores the same as 'A'). To ensure our program handles this correctly, we must normalize the input. By converting the entire string to a single case (e.g., uppercase), our lookup logic only has to handle 26 possible characters, greatly simplifying the code.
Can this code handle numbers or symbols in the input string?
The array-based solution, as written, cannot. If a non-alphabetic character is passed, the 'A' n:- calculation will produce an index outside the bounds of the letter-scores array, leading to an error. The hashmap solution would also fail unless those symbols were explicitly added as keys in the map. A more robust version would filter out non-letters or assign them a score of 0.
How would I adapt this code for a different language's Scrabble set?
The hashmap approach is far better for this. You would simply create a new map constant (e.g., spanish-letter-map) with the characters and scores for that language. The rest of the logic in the score-alt word could remain largely the same, making the code highly adaptable.
Is the array-based solution always faster than a hashmap solution in 8th?
For small, fixed datasets like the 26 letters of the alphabet, the direct memory access of an array index lookup is almost certainly faster than the hashing, bucket-finding, and collision resolution involved in a hashmap lookup. However, the difference would likely be measured in nanoseconds and be completely unnoticeable for this problem's scale. For very large datasets, the performance characteristics can become more complex.
What does the ' (tick) operator do in 8th?
The tick operator (') is used to get the "execution token" (xt) of a word. An execution token is like a function pointer or a reference to a function in other languages. It allows you to treat code as data. This is essential for higher-order words like s:reduce or s:map, which need to be told *which* word to execute for each item in a collection.

Conclusion: From Problem to Paradigm

We have journeyed from a simple problem statement—scoring a Scrabble word—to a deep understanding of its implementation in the unique world of 8th. We dissected an efficient, array-based solution, appreciating its clever use of ASCII arithmetic and stack manipulation. We then contrasted it with a more robust and readable hashmap-based approach, highlighting the critical trade-offs between performance and maintainability that every developer faces.

More than just solving a puzzle, you've learned to think in terms of data flow, stack effects, and functional composition. Mastering concepts like s:reduce, constants, and execution tokens is a significant step forward in your programming journey. This foundational knowledge will empower you to tackle far more complex challenges with confidence and elegance.

Ready to continue your journey and apply these concepts to new problems? Explore the complete 8th learning path on kodikra.com or advance to the next challenge in our exclusive Module 3 learning roadmap.

Disclaimer: The code and explanations in this article are based on recent stable versions of 8th. As the language evolves, specific word names or behaviors may change. Always refer to the official documentation for the most current information.


Published by Kodikra — Your trusted 8th learning resource.