Skip to content

Commit 0ab8cf0

Browse files
authored
Merge pull request #195 from atk/etl
new exercise: etl
2 parents eae6d8c + cdc2d1f commit 0ab8cf0

File tree

13 files changed

+387
-0
lines changed

13 files changed

+387
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@
9595
"prerequisites": [],
9696
"difficulty": 2
9797
},
98+
{
99+
"slug": "etl",
100+
"name": "ETL",
101+
"uuid": "73c35d68-55b8-4615-bce8-49c7defc0181",
102+
"practices": [],
103+
"prerequisites": [],
104+
"difficulty": 2
105+
},
98106
{
99107
"slug": "hamming",
100108
"name": "Hamming",
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Instructions
2+
3+
Your task is to change the data format of letters and their point values in the game.
4+
5+
Currently, letters are stored in groups based on their score, in a one-to-many mapping.
6+
7+
- 1 point: "A", "E", "I", "O", "U", "L", "N", "R", "S", "T",
8+
- 2 points: "D", "G",
9+
- 3 points: "B", "C", "M", "P",
10+
- 4 points: "F", "H", "V", "W", "Y",
11+
- 5 points: "K",
12+
- 8 points: "J", "X",
13+
- 10 points: "Q", "Z",
14+
15+
This needs to be changed to store each individual letter with its score in a one-to-one mapping.
16+
17+
- "a" is worth 1 point.
18+
- "b" is worth 3 points.
19+
- "c" is worth 3 points.
20+
- "d" is worth 2 points.
21+
- etc.
22+
23+
As part of this change, the team has also decided to change the letters to be lower-case rather than upper-case.
24+
25+
~~~~exercism/note
26+
If you want to look at how the data was previously structured and how it needs to change, take a look at the examples in the test suite.
27+
~~~~
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Introduction
2+
3+
You work for a company that makes an online multiplayer game called Lexiconia.
4+
5+
To play the game, each player is given 13 letters, which they must rearrange to create words.
6+
Different letters have different point values, since it's easier to create words with some letters than others.
7+
8+
The game was originally launched in English, but it is very popular, and now the company wants to expand to other languages as well.
9+
10+
Different languages need to support different point values for letters.
11+
The point values are determined by how often letters are used, compared to other letters in that language.
12+
13+
For example, the letter 'C' is quite common in English, and is only worth 3 points.
14+
But in Norwegian it's a very rare letter, and is worth 10 points.
15+
16+
To make it easier to add new languages, your team needs to change the way letters and their point values are stored in the game.

exercises/practice/etl/.eslintrc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"root": true,
3+
"extends": "@exercism/eslint-config-javascript",
4+
"env": {
5+
"jest": true
6+
},
7+
"overrides": [
8+
{
9+
"files": ["*.spec.js"],
10+
"excludedFiles": ["custom.spec.js"],
11+
"extends": "@exercism/eslint-config-javascript/maintainers"
12+
}
13+
]
14+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"authors": [
3+
"atk"
4+
],
5+
"files": {
6+
"solution": [
7+
"etl.wat"
8+
],
9+
"test": [
10+
"etl.spec.js"
11+
],
12+
"example": [
13+
".meta/proof.ci.wat"
14+
],
15+
"invalidator": [
16+
"package.json"
17+
]
18+
},
19+
"blurb": "Change the data format for scoring a game to more easily add other languages.",
20+
"source": "Based on an exercise by the JumpstartLab team for students at The Turing School of Software and Design.",
21+
"source_url": "https://turing.edu",
22+
"custom": {
23+
"version.tests.compatibility": "jest-27",
24+
"flag.tests.task-per-describe": false,
25+
"flag.tests.may-run-long": false,
26+
"flag.tests.includes-optional": false
27+
}
28+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
(module
2+
(memory (export "mem") 1)
3+
4+
(global $outputOffset i32 (i32.const 512))
5+
(global $zero i32 (i32.const 48))
6+
(global $nine i32 (i32.const 57))
7+
(global $A i32 (i32.const 65))
8+
(global $Z i32 (i32.const 90))
9+
(global $lower i32 (i32.const 32))
10+
(global $start i32 (i32.const 123))
11+
(global $stop i32 (i32.const 125))
12+
(global $reset i32 (i32.const 93))
13+
(global $equals i32 (i32.const 58))
14+
(global $quot i32 (i32.const 34))
15+
(global $delim i32 (i32.const 44))
16+
17+
;;
18+
;; Rewrite the incoming JSON to the new ETL format
19+
;;
20+
;; @param {i32} $inputOffset - offset of the JSON input in linear memory
21+
;; @param {i32} $inputLength - length of the JSON input in linear memory
22+
;;
23+
;; @returns {(i32,i32)} - offset and length of the JSON output in linear memory
24+
;;
25+
(func (export "transform") (param $inputOffset i32) (param $inputLength i32) (result i32 i32)
26+
(local $char i32)
27+
(local $number i32)
28+
(local $inputPos i32)
29+
(local $outputLength i32)
30+
(i32.store8 (global.get $outputOffset) (global.get $start))
31+
(local.set $outputLength (i32.add (local.get $outputLength) (i32.const 1)))
32+
(loop $chars
33+
(local.set $char (i32.load8_u (i32.add (local.get $inputOffset) (local.get $inputPos))))
34+
(if (i32.eq (local.get $char) (global.get $reset)) (then (local.set $number (i32.const 0))))
35+
(if (i32.and (i32.ge_u (local.get $char) (global.get $zero))
36+
(i32.le_u (local.get $char) (global.get $nine)))
37+
(then (local.set $number (i32.add (i32.mul (local.get $number) (i32.const 10))
38+
(i32.sub (local.get $char) (global.get $zero))))))
39+
(if (i32.and (i32.ge_u (local.get $char) (global.get $A))
40+
(i32.le_u (local.get $char) (global.get $Z))) (then
41+
(if (i32.gt_u (local.get $outputLength) (i32.const 1)) (then
42+
(i32.store8 (i32.add (global.get $outputOffset) (local.get $outputLength)) (global.get $delim))
43+
(local.set $outputLength (i32.add (local.get $outputLength) (i32.const 1)))))
44+
(i32.store8 (i32.add (global.get $outputOffset) (local.get $outputLength)) (global.get $quot))
45+
(local.set $outputLength (i32.add (local.get $outputLength) (i32.const 1)))
46+
(i32.store8 (i32.add (global.get $outputOffset) (local.get $outputLength))
47+
(i32.or (local.get $char) (global.get $lower)))
48+
(local.set $outputLength (i32.add (local.get $outputLength) (i32.const 1)))
49+
(i32.store8 (i32.add (global.get $outputOffset) (local.get $outputLength)) (global.get $quot))
50+
(local.set $outputLength (i32.add (local.get $outputLength) (i32.const 1)))
51+
(i32.store8 (i32.add (global.get $outputOffset) (local.get $outputLength)) (global.get $equals))
52+
(local.set $outputLength (i32.add (local.get $outputLength) (i32.const 1)))
53+
(if (i32.ge_u (local.get $number) (i32.const 10)) (then
54+
(i32.store8 (i32.add (global.get $outputOffset) (local.get $outputLength))
55+
(i32.add (i32.div_u (local.get $number) (i32.const 10)) (global.get $zero)))
56+
(local.set $outputLength (i32.add (local.get $outputLength) (i32.const 1)))))
57+
(i32.store8 (i32.add (global.get $outputOffset) (local.get $outputLength))
58+
(i32.add (i32.rem_u (local.get $number) (i32.const 10)) (global.get $zero)))
59+
(local.set $outputLength (i32.add (local.get $outputLength) (i32.const 1)))
60+
))
61+
(local.set $inputPos (i32.add (local.get $inputPos) (i32.const 1)))
62+
(br_if $chars (i32.lt_u (local.get $inputPos) (local.get $inputLength))))
63+
(i32.store8 (i32.add (global.get $outputOffset) (local.get $outputLength)) (global.get $stop))
64+
(local.set $outputLength (i32.add (local.get $outputLength) (i32.const 1)))
65+
(global.get $outputOffset) (local.get $outputLength)
66+
)
67+
)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
[78a7a9f9-4490-4a47-8ee9-5a38bb47d28f]
13+
description = "single letter"
14+
15+
[60dbd000-451d-44c7-bdbb-97c73ac1f497]
16+
description = "single score with multiple letters"
17+
18+
[f5c5de0c-301f-4fdd-a0e5-df97d4214f54]
19+
description = "multiple scores with multiple letters"
20+
21+
[5db8ea89-ecb4-4dcd-902f-2b418cc87b9d]
22+
description = "multiple scores with differing numbers of letters"

exercises/practice/etl/.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
audit=false

exercises/practice/etl/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Exercism
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default {
2+
presets: ["@exercism/babel-preset-javascript"],
3+
plugins: [],
4+
};

exercises/practice/etl/etl.spec.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import { compileWat, WasmRunner } from "@exercism/wasm-lib";
2+
3+
let wasmModule;
4+
let currentInstance;
5+
6+
beforeAll(async () => {
7+
try {
8+
const watPath = new URL("./etl.wat", import.meta.url);
9+
const { buffer } = await compileWat(watPath);
10+
wasmModule = await WebAssembly.compile(buffer);
11+
} catch (err) {
12+
console.log(`Error compiling *.wat: \n${err}`);
13+
process.exit(1);
14+
}
15+
});
16+
17+
18+
const transform = (input) => {
19+
const serializedInput = JSON.stringify(input);
20+
21+
const inputBufferOffset = 64;
22+
const inputBufferCapacity = 256;
23+
24+
const inputLengthEncoded = new TextEncoder().encode(serializedInput).length;
25+
if (inputLengthEncoded > inputBufferCapacity) {
26+
throw new Error(
27+
`String is too large for buffer of size ${inputBufferCapacity} bytes`
28+
);
29+
}
30+
31+
currentInstance.set_mem_as_utf8(inputBufferOffset, inputLengthEncoded, serializedInput);
32+
33+
const [outputOffset, outputLength] = currentInstance.exports.transform(
34+
inputBufferOffset, inputLengthEncoded
35+
);
36+
37+
const outputString = currentInstance.get_mem_as_utf8(outputOffset, outputLength);
38+
return JSON.parse(outputString);
39+
}
40+
41+
describe('Transform legacy to new', () => {
42+
beforeEach(async () => {
43+
currentInstance = null;
44+
if (!wasmModule) {
45+
return Promise.reject();
46+
}
47+
try {
48+
currentInstance = await new WasmRunner(wasmModule);
49+
return Promise.resolve();
50+
} catch (err) {
51+
console.log(`Error instantiating WebAssembly module: ${err}`);
52+
return Promise.reject();
53+
}
54+
});
55+
56+
test('single letter', () => {
57+
const old = { 1: ['A'] };
58+
const expected = { a: 1 };
59+
60+
expect(transform(old)).toEqual(expected);
61+
});
62+
63+
xtest('single score with multiple letters', () => {
64+
const old = { 1: ['A', 'E', 'I', 'O', 'U'] };
65+
const expected = {
66+
a: 1,
67+
e: 1,
68+
i: 1,
69+
o: 1,
70+
u: 1,
71+
};
72+
73+
expect(transform(old)).toEqual(expected);
74+
});
75+
76+
xtest('multiple scores with multiple letters', () => {
77+
const old = { 1: ['A', 'E'], 2: ['D', 'G'] };
78+
const expected = {
79+
a: 1,
80+
e: 1,
81+
d: 2,
82+
g: 2,
83+
};
84+
85+
expect(transform(old)).toEqual(expected);
86+
});
87+
88+
xtest('multiple scores with differing numbers of letters', () => {
89+
const old = {
90+
1: ['A', 'E', 'I', 'O', 'U', 'L', 'N', 'R', 'S', 'T'],
91+
2: ['D', 'G'],
92+
3: ['B', 'C', 'M', 'P'],
93+
4: ['F', 'H', 'V', 'W', 'Y'],
94+
5: ['K'],
95+
8: ['J', 'X'],
96+
10: ['Q', 'Z'],
97+
};
98+
const expected = {
99+
a: 1,
100+
b: 3,
101+
c: 3,
102+
d: 2,
103+
e: 1,
104+
f: 4,
105+
g: 2,
106+
h: 4,
107+
i: 1,
108+
j: 8,
109+
k: 5,
110+
l: 1,
111+
m: 3,
112+
n: 1,
113+
o: 1,
114+
p: 3,
115+
q: 10,
116+
r: 1,
117+
s: 1,
118+
t: 1,
119+
u: 1,
120+
v: 4,
121+
w: 4,
122+
x: 8,
123+
y: 4,
124+
z: 10,
125+
};
126+
127+
expect(transform(old)).toEqual(expected);
128+
});
129+
});

exercises/practice/etl/etl.wat

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
(module
2+
(memory (export "mem") 1)
3+
4+
;;
5+
;; Rewrite the incoming JSON to the new ETL format
6+
;;
7+
;; @param {i32} $inputOffset - offset of the JSON input in linear memory
8+
;; @param {i32} $inputLength - length of the JSON input in linear memory
9+
;;
10+
;; @returns {(i32,i32)} - offset and length of the JSON output in linear memory
11+
;;
12+
(func (export "transform") (param $inputOffset i32) (param $inputLength i32) (result i32 i32)
13+
(i32.const 0) (i32.const 0)
14+
)
15+
)

0 commit comments

Comments
 (0)