Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,14 @@
"prerequisites": [],
"difficulty": 7
Comment thread
glennj marked this conversation as resolved.
},
{
"slug": "alphametics",
"name": "Alphametics",
"uuid": "cc316db6-9007-40fe-af64-92f00d5f148e",
"practices": [],
"prerequisites": [],
"difficulty": 8
},
{
"slug": "change",
"name": "Change",
Expand Down
59 changes: 59 additions & 0 deletions exercises/practice/alphametics/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Instructions

Given an alphametics puzzle, find the correct solution.

[Alphametics][alphametics] is a puzzle where letters in words are replaced with numbers.

For example `SEND + MORE = MONEY`:

```text
S E N D
M O R E +
-----------
M O N E Y
```

Replacing these with valid numbers gives:

```text
9 5 6 7
1 0 8 5 +
-----------
1 0 6 5 2
```

This is correct because every letter is replaced by a different number and the
words, translated into numbers, then make a valid sum.

Each letter must represent a different digit, and the leading digit of a
multi-digit number must not be zero.

## Input

Your script will receive the alphametic puzzle as a single string via
standard input (stdin). The puzzle string follows the format
`WORD1 + WORD2 + ... == RESULT`.

For example:

```text
SEND + MORE == MONEY
```

## Output

If the puzzle has a solution, print a single line consisting of
space-separated `LETTER=DIGIT` pairs, sorted alphabetically by letter.

For the example above, the output should be:

```text
D=7 E=5 M=1 N=6 O=0 R=8 S=9 Y=2
```

If the puzzle has multiple valid solutions, printing any one of them is
acceptable.

If the puzzle has no solution, print nothing (an empty line).

[alphametics]: https://en.wikipedia.org/wiki/Alphametics
21 changes: 21 additions & 0 deletions exercises/practice/alphametics/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"blurb": "Given an alphametics puzzle, find the correct solution.",
"authors": [
"kirtisrivastava22"
],
"contributors": [],
"files": {
"solution": [
"alphametics.awk"
],
"test": [
"test-alphametics.bats"
],
"example": [
".meta/example.awk"
]
},
"icon": "crossword",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Alphametics"
}
103 changes: 103 additions & 0 deletions exercises/practice/alphametics/.meta/example.awk
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/usr/bin/awk -f
#
# Alphametics solver.
#
# Reads a puzzle from stdin in the form:
# WORD1 + WORD2 + ... == RESULT
# (a single "=" is also accepted)
#
# Prints one line of space-separated LETTER=DIGIT pairs, sorted
# alphabetically by letter, for a valid solution. Prints nothing
# if no solution exists.
#
# Approach: process the puzzle column by column, from the rightmost
# (units) digit to the leftmost, tracking the carry. Within each
# column, only the letters that appear for the first time are
# assigned digits (via backtracking), then the column's arithmetic
# is checked immediately. This prunes the search far more
# aggressively than trying full permutations of every letter.
function colchar(word, col, L) {
L = length(word)
if (col < L) return substr(word, L - col, 1)
return ""
}
# Try every valid digit for newl[idx..m], then verify/recurse.
function assign(idx, m, colletters, ncol, hasres, rch, newl, col, carr,
d, letter, i, colsum, total, digit_needed, newcarry, required) {
if (idx > m) {
colsum = 0
for (i = 1; i <= ncol; i++) colsum += assigned[colletters[i]]
total = colsum + carr
digit_needed = total % 10
newcarry = int(total / 10)
required = hasres ? assigned[rch] : 0
if (digit_needed != required) return 0
return solve(col + 1, newcarry)
}
letter = newl[idx]
for (d = 0; d <= 9; d++) {
if (used[d]) continue
if (leading[letter] && d == 0) continue
assigned[letter] = d
used[d] = 1
if (assign(idx + 1, m, colletters, ncol, hasres, rch, newl, col, carr)) return 1
delete assigned[letter]
used[d] = 0
}
return 0
}
function solve(col, carry,
colletters, ncol, i, ch, hasres, rch, newl, m, j, exists) {
if (col == maxlen) return (carry == 0)
ncol = 0
for (i = 1; i <= nwords; i++) {
ch = colchar(addends[i], col)
if (ch != "") colletters[++ncol] = ch
}
hasres = (col < length(result))
rch = hasres ? colchar(result, col) : ""
m = 0
for (i = 1; i <= ncol; i++) {
ch = colletters[i]
if (!(ch in assigned)) {
exists = 0
for (j = 1; j <= m; j++) if (newl[j] == ch) { exists = 1; break }
if (!exists) newl[++m] = ch
}
}
if (hasres && !(rch in assigned)) {
exists = 0
for (j = 1; j <= m; j++) if (newl[j] == rch) { exists = 1; break }
if (!exists) newl[++m] = rch
}
return assign(1, m, colletters, ncol, hasres, rch, newl, col, carry)
}
{
line = $0
split(line, parts, /\s*==\s*/)
lhs = parts[1]
result = parts[2]
nwords = split(lhs, addends, /\s*\+\s*/)
maxlen = length(result)
if (length(result) > 1) leading[substr(result, 1, 1)] = 1
for (i = 1; i <= nwords; i++) {
L = length(addends[i])
if (L > maxlen) maxlen = L
if (L > 1) leading[substr(addends[i], 1, 1)] = 1
}
if (solve(0, 0)) {
n = 0
for (l in assigned) letters[++n] = l
for (i = 1; i <= n; i++)
for (j = i + 1; j <= n; j++)
if (letters[j] < letters[i]) {
t = letters[i]; letters[i] = letters[j]; letters[j] = t
}
out = ""
for (i = 1; i <= n; i++) {
if (i > 1) out = out " "
out = out letters[i] "=" assigned[letters[i]]
}
print out
}
}
13 changes: 13 additions & 0 deletions exercises/practice/alphametics/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{{ header }}
{% for idx, case in cases %}
@test "{{ case.description }}" {
{% if idx == 0 %}# {% endif %}[[ $BATS_RUN_SKIPPED == "true" ]] || skip
run awk -f alphametics.awk <<< "{{ case.input.puzzle }}"
Comment thread
kirtisrivastava22 marked this conversation as resolved.
assert_success
{% if case.expected %}
assert_output "{% for letter, digit in case.expected | dictsort %}{{ letter }}={{ digit }}{% if not loop.last %} {% endif %}{% endfor %}"
{% else %}
refute_output
{% endif %}
}
{% endfor %}
37 changes: 37 additions & 0 deletions exercises/practice/alphametics/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# This is an auto-generated file.
#
# This file is synced by a bot with exercism/problem-specifications data.
# Anything added to this file will be overwritten unless the settings
# indicate otherwise. All added comments will be preserved.
#
# See https://github.com/exercism/docs/blob/main/building/tracks/tests.md

[e0c08b07-9028-4d5f-91e1-d178fead8e1a]
description = "puzzle with three letters"

[a504ee41-cb92-4ec2-9f11-c37e95ab3f25]
description = "solution must have unique value for each letter"

[4e3b81d2-be7b-4c5c-9a80-cd72bc6d465a]
description = "leading zero solution is invalid"

[8a3e3168-d1ee-4df7-94c7-b9c54845ac3a]
description = "puzzle with two digits final carry"

[a9630645-15bd-48b6-a61e-d85c4021cc09]
description = "puzzle with four letters"

[3d905a86-5a52-4e4e-bf80-8951535791bd]
description = "puzzle with six letters"

[4febca56-e7b7-4789-97b9-530d09ba95f0]
description = "puzzle with seven letters"

[12125a75-7284-4f9a-a5fa-191471e0d44f]
description = "puzzle with eight letters"

[fb05955f-38dc-477a-a0b6-5ef78969fffa]
description = "puzzle with ten letters"

[9a101e81-9216-472b-b458-b513a7adacf7]
description = "puzzle with ten letters and 199 addends"
11 changes: 11 additions & 0 deletions exercises/practice/alphametics/alphametics.awk
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/awk -f
#
# Read an alphametics puzzle from stdin, e.g.:
# SEND + MORE == MONEY
#
# Print a single line of space-separated LETTER=DIGIT pairs, sorted
# alphabetically by letter, for a valid solution (e.g. "D=7 E=5 M=1 N=6
# O=0 R=8 S=9 Y=2"). Print nothing if no solution exists.

{
}
Loading