Skip to content

Commit 1768c95

Browse files
committed
Added alphametics exercise to the track
1 parent 0b9f7ee commit 1768c95

File tree

12 files changed

+18362
-0
lines changed

12 files changed

+18362
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,14 @@
12441244
"strings"
12451245
],
12461246
"difficulty": 4
1247+
},
1248+
{
1249+
"slug": "alphametics",
1250+
"name": "Alphametics",
1251+
"uuid": "0c69f8bd-c99a-4cb2-b113-82deaf0915ee",
1252+
"practices": [],
1253+
"prerequisites": [],
1254+
"difficulty": 1
12471255
}
12481256
],
12491257
"foregone": [
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Instructions
2+
3+
Given an alphametics puzzle, find the correct solution.
4+
5+
[Alphametics][alphametics] is a puzzle where letters in words are replaced with numbers.
6+
7+
For example `SEND + MORE = MONEY`:
8+
9+
```text
10+
S E N D
11+
M O R E +
12+
-----------
13+
M O N E Y
14+
```
15+
16+
Replacing these with valid numbers gives:
17+
18+
```text
19+
9 5 6 7
20+
1 0 8 5 +
21+
-----------
22+
1 0 6 5 2
23+
```
24+
25+
This is correct because every letter is replaced by a different number and the words, translated into numbers, then make a valid sum.
26+
27+
Each letter must represent a different digit, and the leading digit of a multi-digit number must not be zero.
28+
29+
[alphametics]: https://en.wikipedia.org/wiki/Alphametics
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"marcelweikum"
4+
],
5+
"files": {
6+
"solution": [
7+
"alphametics.cpp",
8+
"alphametics.h"
9+
],
10+
"test": [
11+
"alphametics_test.cpp"
12+
],
13+
"example": [
14+
".meta/example.cpp",
15+
".meta/example.h"
16+
]
17+
},
18+
"blurb": "Given an alphametics puzzle, find the correct solution."
19+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#include "alphametics.h"
2+
3+
#include <string>
4+
#include <vector>
5+
#include <map>
6+
#include <optional>
7+
#include <algorithm>
8+
#include <sstream>
9+
#include <unordered_set>
10+
11+
namespace alphametics {
12+
13+
std::optional<std::map<char,int>> solve(const std::string& puzzle) {
14+
auto sep = puzzle.find("==");
15+
if (sep == std::string::npos) return std::nullopt;
16+
std::string left = puzzle.substr(0, sep);
17+
std::string right = puzzle.substr(sep + 2);
18+
19+
auto trim = [&](std::string& s) {
20+
auto b = s.find_first_not_of(' ');
21+
auto e = s.find_last_not_of(' ');
22+
s = (b == std::string::npos) ? std::string() : s.substr(b, e - b + 1);
23+
};
24+
trim(left); trim(right);
25+
26+
std::vector<std::string> addends;
27+
std::istringstream iss(left);
28+
for (std::string part; std::getline(iss, part, '+'); ) {
29+
trim(part);
30+
addends.push_back(part);
31+
}
32+
trim(right);
33+
std::string result = right;
34+
35+
std::vector<char> letters;
36+
std::unordered_set<char> seen, leading;
37+
auto collect = [&](const std::string& w) {
38+
if (w.size() > 1) leading.insert(w.front());
39+
for (char c : w) {
40+
if (seen.insert(c).second) letters.push_back(c);
41+
}
42+
};
43+
for (auto& w : addends) collect(w);
44+
collect(result);
45+
if (letters.size() > 10) return std::nullopt;
46+
47+
int n = letters.size();
48+
std::map<char,int> idx;
49+
for (int i = 0; i < n; ++i) idx[letters[i]] = i;
50+
std::vector<long long> weight(n, 0);
51+
52+
for (auto& w : addends) {
53+
long long mult = 1;
54+
for (int i = (int)w.size() - 1; i >= 0; --i) {
55+
weight[idx[w[i]]] += mult;
56+
mult *= 10;
57+
}
58+
}
59+
{
60+
long long mult = 1;
61+
for (int i = (int)result.size() - 1; i >= 0; --i) {
62+
weight[idx[result[i]]] -= mult;
63+
mult *= 10;
64+
}
65+
}
66+
67+
std::vector<bool> used(10, false);
68+
std::map<char,int> assign;
69+
bool found = false;
70+
std::map<char,int> solution;
71+
std::function<void(int,long long)> dfs = [&](int pos, long long sum) {
72+
if (found) return;
73+
if (pos == n) {
74+
if (sum == 0) { found = true; solution = assign; }
75+
return;
76+
}
77+
char c = letters[pos];
78+
for (int d = 0; d < 10 && !found; ++d) {
79+
if (used[d] || (d == 0 && leading.count(c))) continue;
80+
used[d] = true;
81+
assign[c] = d;
82+
dfs(pos + 1, sum + weight[pos] * d);
83+
used[d] = false;
84+
}
85+
};
86+
87+
dfs(0, 0);
88+
if (found) return solution;
89+
return std::nullopt;
90+
}
91+
92+
} // namespace alphametics
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#ifndef ALPHAMETICS_H
2+
#define ALPHAMETICS_H
3+
4+
#include <optional>
5+
#include <map>
6+
#include <string>
7+
8+
namespace alphametics {
9+
10+
std::optional<std::map<char,int>> solve(const std::string& puzzle);
11+
12+
} // namespace alphametics
13+
14+
#endif // ALPHAMETICS_H
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[e0c08b07-9028-4d5f-91e1-d178fead8e1a]
13+
description = "puzzle with three letters"
14+
15+
[a504ee41-cb92-4ec2-9f11-c37e95ab3f25]
16+
description = "solution must have unique value for each letter"
17+
18+
[4e3b81d2-be7b-4c5c-9a80-cd72bc6d465a]
19+
description = "leading zero solution is invalid"
20+
21+
[8a3e3168-d1ee-4df7-94c7-b9c54845ac3a]
22+
description = "puzzle with two digits final carry"
23+
24+
[a9630645-15bd-48b6-a61e-d85c4021cc09]
25+
description = "puzzle with four letters"
26+
27+
[3d905a86-5a52-4e4e-bf80-8951535791bd]
28+
description = "puzzle with six letters"
29+
30+
[4febca56-e7b7-4789-97b9-530d09ba95f0]
31+
description = "puzzle with seven letters"
32+
33+
[12125a75-7284-4f9a-a5fa-191471e0d44f]
34+
description = "puzzle with eight letters"
35+
36+
[fb05955f-38dc-477a-a0b6-5ef78969fffa]
37+
description = "puzzle with ten letters"
38+
39+
[9a101e81-9216-472b-b458-b513a7adacf7]
40+
description = "puzzle with ten letters and 199 addends"
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Get the exercise name from the current directory
2+
get_filename_component(exercise ${CMAKE_CURRENT_SOURCE_DIR} NAME)
3+
4+
# Basic CMake project
5+
cmake_minimum_required(VERSION 3.5.1)
6+
7+
# Name the project after the exercise
8+
project(${exercise} CXX)
9+
10+
# Get a source filename from the exercise name by replacing -'s with _'s
11+
string(REPLACE "-" "_" file ${exercise})
12+
13+
# Implementation could be only a header
14+
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}.cpp)
15+
set(exercise_cpp ${file}.cpp)
16+
else()
17+
set(exercise_cpp "")
18+
endif()
19+
20+
# Use the common Catch library?
21+
if(EXERCISM_COMMON_CATCH)
22+
# For Exercism track development only
23+
add_executable(${exercise} ${file}_test.cpp ${exercise_cpp} ${file}.h $<TARGET_OBJECTS:catchlib>)
24+
elseif(EXERCISM_TEST_SUITE)
25+
# The Exercism test suite is being run, the Docker image already
26+
# includes a pre-built version of Catch.
27+
find_package(Catch2 REQUIRED)
28+
add_executable(${exercise} ${file}_test.cpp ${exercise_cpp} ${file}.h)
29+
target_link_libraries(${exercise} PRIVATE Catch2::Catch2WithMain)
30+
# When Catch is installed system wide we need to include a different
31+
# header, we need this define to use the correct one.
32+
target_compile_definitions(${exercise} PRIVATE EXERCISM_TEST_SUITE)
33+
else()
34+
# Build executable from sources and headers
35+
add_executable(${exercise} ${file}_test.cpp ${exercise_cpp} ${file}.h test/tests-main.cpp)
36+
endif()
37+
38+
set_target_properties(${exercise} PROPERTIES
39+
CXX_STANDARD 17
40+
CXX_STANDARD_REQUIRED OFF
41+
CXX_EXTENSIONS OFF
42+
)
43+
44+
set(CMAKE_BUILD_TYPE Debug)
45+
46+
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(GNU|Clang)")
47+
set_target_properties(${exercise} PROPERTIES
48+
COMPILE_FLAGS "-Wall -Wextra -Wpedantic -Werror"
49+
)
50+
endif()
51+
52+
# Configure to run all the tests?
53+
if(${EXERCISM_RUN_ALL_TESTS})
54+
target_compile_definitions(${exercise} PRIVATE EXERCISM_RUN_ALL_TESTS)
55+
endif()
56+
57+
# Tell MSVC not to warn us about unchecked iterators in debug builds
58+
# Treat warnings as errors
59+
# Treat type conversion warnings C4244 and C4267 as level 4 warnings, i.e. ignore them in level 3
60+
if(${MSVC})
61+
set_target_properties(${exercise} PROPERTIES
62+
COMPILE_DEFINITIONS_DEBUG _SCL_SECURE_NO_WARNINGS
63+
COMPILE_FLAGS "/WX /w44244 /w44267")
64+
endif()
65+
66+
# Run the tests on every build
67+
add_custom_target(test_${exercise} ALL DEPENDS ${exercise} COMMAND ${exercise})
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include "alphametics.h"
2+
3+
namespace alphametics {
4+
5+
// TODO: add your solution here
6+
7+
} // namespace alphametics
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#if !defined(ALPHAMETICS_H)
2+
#define ALPHAMETICS_H
3+
4+
namespace alphametics {
5+
6+
// TODO: add your solution here
7+
8+
} // namespace alphametics
9+
10+
#endif // ALPHAMETICS_H

0 commit comments

Comments
 (0)