How Scrabble Scoring Works
Technical explanation of how NHL Scrabble calculates player name scores and generates standings.
Overview
NHL Scrabble analyzes NHL player names using standard Scrabble letter point values to create alternative team standings. This document explains the technical implementation.
Scrabble Letter Values
Standard Scrabble letter point values used for scoring:
Points |
Letters |
|---|---|
1 |
A, E, I, O, U, L, N, S, T, R |
2 |
D, G |
3 |
B, C, M, P |
4 |
F, H, V, W, Y |
5 |
K |
8 |
J, X |
10 |
Q, Z |
References:
Scrabble Letter Values (Wikipedia) - Official tile point values
Scrabble Letter Distributions (Wikipedia) - Comprehensive distribution information
Implementation:
SCRABBLE_VALUES = {
"A": 1,
"E": 1,
"I": 1,
"O": 1,
"U": 1,
"L": 1,
"N": 1,
"S": 1,
"T": 1,
"R": 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,
}
Scoring Rules:
Case-insensitive: All letters converted to uppercase before scoring
Letters only: Non-alphabetic characters (spaces, hyphens, apostrophes) are ignored
Full name: First name + last name are scored together
Examples:
>>> from nhl_scrabble.scoring import ScrabbleScorer
>>> scorer = ScrabbleScorer()
# High-scoring names (rare letters)
>>> scorer.calculate_score("Alexander Ovechkin") # V, H, K
37
>>> scorer.calculate_score("Zdeno Chara") # Z worth 10!
25
>>> scorer.calculate_score("Pavel Zacha") # Another Z
29
# Lower-scoring names (common letters)
>>> scorer.calculate_score("Connor McDavid") # All common letters
24
>>> scorer.calculate_score("Leon Draisaitl") # No high-value letters
14
Workflow
Step 1: Fetch NHL Data
Retrieve current NHL team rosters from the NHL API:
GET https://api-web.nhle.com/v1/standings/now
→ Returns team metadata (division, conference, abbreviations)
For each team:
GET https://api-web.nhle.com/v1/roster/{team_abbrev}/current
→ Returns roster (forwards, defensemen, goalies)
Implementation Details:
Rate Limiting: 0.3 second delay between roster fetches
Retry Logic: 3 retries with exponential backoff (1s, 2s, 4s)
Timeout: 10 seconds per request
Error Handling: Graceful degradation (skip failed teams)
Progress Tracking: Real-time progress bars during fetching
API Response Processing:
# Standings endpoint
{
"standings": [
{
"teamAbbrev": {"default": "TOR"},
"teamName": {"default": "Maple Leafs"},
"divisionName": "Atlantic",
"conferenceName": "Eastern",
...
}
]
}
# Roster endpoint
{
"forwards": [
{
"id": 8477404,
"firstName": {"default": "Auston"},
"lastName": {"default": "Matthews"},
...
}
],
"defensemen": [...],
"goalies": [...]
}
Step 2: Calculate Scrabble Scores
For each player, calculate the Scrabble value of their full name:
def calculate_score(text: str) -> int:
"""Calculate Scrabble score for any text string.
Args:
text: Text to score (player name)
Returns:
Total Scrabble score (sum of all letter values)
"""
score = 0
for char in text.upper():
if char in SCRABBLE_VALUES:
score += SCRABBLE_VALUES[char]
return score
Player Model:
@dataclass
class PlayerScore:
"""A player with their Scrabble score."""
id: int
first_name: str
last_name: str
full_name: str
score: int
team: str
Scoring Process:
Extract first name and last name from API response
Combine to create full name:
f"{first_name} {last_name}"Calculate Scrabble score for full name
Create
PlayerScoreobject with all details
Step 3: Aggregate Team Scores
For each team, aggregate player scores into team totals:
@dataclass
class TeamScore:
"""Team with aggregated Scrabble scores."""
abbreviation: str
name: str
division: str
conference: str
players: list[PlayerScore]
total: int # Sum of all player scores
average: float # Mean score per player
player_count: int # Number of players on roster
Aggregation Logic:
Group players by team
Calculate
total= sum of all player scoresCalculate
average= total / player_countSort players by score (descending) for display
Step 4: Generate Standings
Create division and conference standings from team scores:
Division Standings:
@dataclass
class DivisionStandings:
"""Standings for a single division."""
division: str
teams: list[TeamScore] # Sorted by total score
Conference Standings:
@dataclass
class ConferenceStandings:
"""Standings for a single conference."""
conference: str
teams: list[TeamScore] # Sorted by total score
Sorting Rules:
Primary: Total Scrabble score (descending)
Tiebreaker 1: Average score per player (descending)
Tiebreaker 2: Team abbreviation (alphabetical)
Step 5: Determine Playoff Bracket
Calculate playoff seeding based on NHL playoff format:
Playoff Structure:
Division Leaders (Top 3 per division): Automatic playoff spots
Wild Cards (2 per conference): Next best teams by total score
Conference Leaders: Best record in each conference
Presidents’ Trophy: Best overall record
Playoff Indicators:
y- Clinched divisionx- Clinched playoff spot (wild card)z- Clinched conferencep- Presidents’ Trophy winnere- Eliminated from playoffs
Seeding Logic:
def calculate_playoff_seeding(teams: list[TeamScore]) -> dict[str, list[PlayoffTeam]]:
# For each conference:
# 1. Top 3 teams per division (6 teams total)
# 2. Next 2 best teams across conference (wild cards)
# 3. Seed 1-8 based on points
# 4. Generate matchups: 1v8, 2v7, 3v6, 4v5
Step 6: Generate Reports
Create comprehensive reports from the analyzed data:
Report Types:
Top Players Report - Highest-scoring individual players
Team Report - All teams with top players per team
Division Report - Teams grouped by division
Conference Report - Eastern vs Western standings
Playoff Report - Mock playoff bracket
Stats Report - League-wide statistics
Report Formats:
Text - Human-readable terminal output with Rich formatting
JSON - Machine-readable structured data
HTML - Browser-friendly formatted report
NHL API Endpoints
Standings Endpoint
GET https://api-web.nhle.com/v1/standings/now
Purpose: Get current team standings and metadata
Response: Team abbreviations, names, divisions, conferences
Usage: Initial team list for roster fetching
Roster Endpoint
GET https://api-web.nhle.com/v1/roster/{team_abbrev}/current
Purpose: Get current team roster
Parameters: team_abbrev - Team abbreviation (e.g., “TOR”, “BOS”)
Response: Forwards, defensemen, goalies with player details
Usage: Fetch all players for Scrabble score calculation
Error Handling
Network Errors:
Automatic retry with exponential backoff
Timeout after 10 seconds
Graceful degradation (skip failed teams)
API Errors:
404 Not Found: Team not found (skip)
500 Server Error: Retry with backoff
Rate limiting: Respect 0.3s delay
Data Errors:
Missing player name: Skip player (log warning)
Invalid JSON: Skip team (log error)
Empty roster: Report zero-score team
Performance Characteristics
Timing:
API Fetching: ~30 seconds (32 teams × ~1s each)
Scoring: <100ms (simple arithmetic on ~750 players)
Report Generation: <200ms (formatting and output)
Total Runtime: ~30-35 seconds
Optimization Techniques:
Rate Limiting: Prevent API throttling
Retry Logic: Handle transient errors
Progress Bars: Show real-time status
Caching: Optional response caching for development
Parallel Processing: Not used (respects API rate limits)
Data Flow Diagram
┌─────────────┐
│ NHL API │
│ /standings │
└─────┬───────┘
│
▼
┌─────────────┐ ┌──────────────┐
│ Team List │────▶│ NHL API │
│ (32 teams) │ │ /roster/{id} │
└─────────────┘ └──────┬───────┘
│
▼
┌──────────────┐
│ Player Data │
│ (~750 total) │
└──────┬───────┘
│
▼
┌──────────────┐
│ Scrabble │
│ Scorer │
└──────┬───────┘
│
▼
┌──────────────┐
│ Team │
│ Processor │
└──────┬───────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌────────────────┐ ┌──────────┐ ┌──────────────┐
│ Division │ │Conference│ │ Playoff │
│ Standings │ │Standings │ │ Bracket │
└────────┬───────┘ └────┬─────┘ └──────┬───────┘
│ │ │
└───────────────┼────────────────┘
▼
┌──────────────┐
│ Reports │
│ (Text/JSON/ │
│ HTML) │
└──────────────┘
Code Examples
Basic Scoring
from nhl_scrabble.scoring import ScrabbleScorer
scorer = ScrabbleScorer()
score = scorer.calculate_score("Alexander Ovechkin")
print(f"Score: {score}") # Score: 37
Full Analysis
from nhl_scrabble.api import NHLClient
from nhl_scrabble.processors import TeamProcessor
from nhl_scrabble.scoring import ScrabbleScorer
# Initialize components
client = NHLClient()
scorer = ScrabbleScorer()
processor = TeamProcessor(client, scorer)
# Process all teams
teams, players, failed = processor.process_all_teams()
# Display results
for team in sorted(teams.values(), key=lambda t: t.total, reverse=True):
print(f"{team.name}: {team.total} points ({team.average:.2f} avg)")
Custom Report
from nhl_scrabble.reports import TeamReport
# Generate team report
report = TeamReport(top_players_per_team=10)
output = report.generate(teams)
print(output)
See Also
Why Scrabble Scoring? - Philosophy and rationale
Architecture Overview - System design
NHL API Strategy - API integration details
Getting Started Tutorial - Try it yourself
Understanding Output - Interpreting results
Implementation Notes
Design Decisions:
Standard Scrabble values: Use official values for familiarity
Full name scoring: First + last for complete player identity
Case-insensitive: Normalize for consistent scoring
Ignore non-letters: Focus on alphabetic characters only
NHL API: Use official data for accuracy and freshness
Limitations:
API availability: Depends on NHL API uptime
Rate limiting: ~30 second runtime due to API delays
No historical data: Only current rosters (API limitation)
Name changes: Player name changes not tracked historically
Future Enhancements:
Caching: Store results for faster repeated access
Historical analysis: Track scores across seasons
Alternative scoring: Custom letter values
International support: Handle diacritics and special characters
Statistical analysis: Correlations with actual hockey metrics