Skip to content

Commit

Permalink
feat: add diamond exercise (#763)
Browse files Browse the repository at this point in the history
  • Loading branch information
vaeng authored Jan 12, 2024
1 parent b6b3993 commit 29458db
Show file tree
Hide file tree
Showing 12 changed files with 18,272 additions and 0 deletions.
12 changes: 12 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,18 @@
"topics": [
]
},
{
"slug": "diamond",
"name": "Diamond",
"uuid": "f8298f68-ef4e-41e1-99af-85a1bef71561",
"practices": [
],
"prerequisites": [
],
"difficulty": 3,
"topics": [
]
},
{
"slug": "run-length-encoding",
"name": "Run-Length Encoding",
Expand Down
52 changes: 52 additions & 0 deletions exercises/practice/diamond/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Instructions

The diamond kata takes as its input a letter, and outputs it in a diamond shape.
Given a letter, it prints a diamond starting with 'A', with the supplied letter at the widest point.

## Requirements

- The first row contains one 'A'.
- The last row contains one 'A'.
- All rows, except the first and last, have exactly two identical letters.
- All rows have as many trailing spaces as leading spaces. (This might be 0).
- The diamond is horizontally symmetric.
- The diamond is vertically symmetric.
- The diamond has a square shape (width equals height).
- The letters form a diamond shape.
- The top half has the letters in ascending order.
- The bottom half has the letters in descending order.
- The four corners (containing the spaces) are triangles.

## Examples

In the following examples, spaces are indicated by `·` characters.

Diamond for letter 'A':

```text
A
```

Diamond for letter 'C':

```text
··A··
·B·B·
C···C
·B·B·
··A··
```

Diamond for letter 'E':

```text
····A····
···B·B···
··C···C··
·D·····D·
E·······E
·D·····D·
··C···C··
···B·B···
····A····
```
21 changes: 21 additions & 0 deletions exercises/practice/diamond/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"authors": [
"vaeng"
],
"files": {
"solution": [
"diamond.cpp",
"diamond.h"
],
"test": [
"diamond_test.cpp"
],
"example": [
".meta/example.cpp",
".meta/example.h"
]
},
"blurb": "Given a letter, print a diamond starting with 'A' with the supplied letter at the widest point.",
"source": "Seb Rose",
"source_url": "https://web.archive.org/web/20220807163751/http://claysnow.co.uk/recycling-tests-in-tdd/"
}
25 changes: 25 additions & 0 deletions exercises/practice/diamond/.meta/example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include "diamond.h"

namespace diamond {
std::vector<std::string> rows(char middle_letter) {
if (middle_letter == 'A') return {"A"};
std::vector<std::string> diamond;
for(char c{'A'}; c <= middle_letter; ++c) {
int outer_spacing = middle_letter - c;
std::string row{};
row.append(outer_spacing, ' ');
row += c;
int inner_spacing = (c - 'A') * 2 - 1;
if (inner_spacing > 0) {
row.append(inner_spacing, ' ');
row += c;
}
row.append(outer_spacing, ' ');
diamond.emplace_back(row);
}
for(int i{middle_letter - 'A' - 1}; i >= 0; --i) {
diamond.emplace_back(diamond.at(i));
}
return diamond;
}
} // namespace diamond
8 changes: 8 additions & 0 deletions exercises/practice/diamond/.meta/example.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

#include <string>
#include <vector>

namespace diamond {
std::vector<std::string> rows(char middle_letter);
} // namespace diamond
25 changes: 25 additions & 0 deletions exercises/practice/diamond/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[202fb4cc-6a38-4883-9193-a29d5cb92076]
description = "Degenerate case with a single 'A' row"

[bd6a6d78-9302-42e9-8f60-ac1461e9abae]
description = "Degenerate case with no row containing 3 distinct groups of spaces"

[af8efb49-14ed-447f-8944-4cc59ce3fd76]
description = "Smallest non-degenerate case with odd diamond side length"

[e0c19a95-9888-4d05-86a0-fa81b9e70d1d]
description = "Smallest non-degenerate case with even diamond side length"

[82ea9aa9-4c0e-442a-b07e-40204e925944]
description = "Largest possible diamond"
64 changes: 64 additions & 0 deletions exercises/practice/diamond/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Get the exercise name from the current directory
get_filename_component(exercise ${CMAKE_CURRENT_SOURCE_DIR} NAME)

# Basic CMake project
cmake_minimum_required(VERSION 3.5.1)

# Name the project after the exercise
project(${exercise} CXX)

# Get a source filename from the exercise name by replacing -'s with _'s
string(REPLACE "-" "_" file ${exercise})

# Implementation could be only a header
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}.cpp)
set(exercise_cpp ${file}.cpp)
else()
set(exercise_cpp "")
endif()

# Use the common Catch library?
if(EXERCISM_COMMON_CATCH)
# For Exercism track development only
add_executable(${exercise} ${file}_test.cpp ${exercise_cpp} ${file}.h $<TARGET_OBJECTS:catchlib>)
elseif(EXERCISM_TEST_SUITE)
# The Exercism test suite is being run, the Docker image already
# includes a pre-built version of Catch.
find_package(Catch2 REQUIRED)
add_executable(${exercise} ${file}_test.cpp ${exercise_cpp} ${file}.h)
target_link_libraries(${exercise} PRIVATE Catch2::Catch2WithMain)
# When Catch is installed system wide we need to include a different
# header, we need this define to use the correct one.
target_compile_definitions(${exercise} PRIVATE EXERCISM_TEST_SUITE)
else()
# Build executable from sources and headers
add_executable(${exercise} ${file}_test.cpp ${exercise_cpp} ${file}.h test/tests-main.cpp)
endif()

set_target_properties(${exercise} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED OFF
CXX_EXTENSIONS OFF
)

set(CMAKE_BUILD_TYPE Debug)

if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(GNU|Clang)")
set_target_properties(${exercise} PROPERTIES
COMPILE_FLAGS "-Wall -Wextra -Wpedantic -Werror"
)
endif()

# Configure to run all the tests?
if(${EXERCISM_RUN_ALL_TESTS})
target_compile_definitions(${exercise} PRIVATE EXERCISM_RUN_ALL_TESTS)
endif()

# Tell MSVC not to warn us about unchecked iterators in debug builds
if(${MSVC})
set_target_properties(${exercise} PROPERTIES
COMPILE_DEFINITIONS_DEBUG _SCL_SECURE_NO_WARNINGS)
endif()

# Run the tests on every build
add_custom_target(test_${exercise} ALL DEPENDS ${exercise} COMMAND ${exercise})
5 changes: 5 additions & 0 deletions exercises/practice/diamond/diamond.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "diamond.h"

namespace diamond {

} // namespace diamond
5 changes: 5 additions & 0 deletions exercises/practice/diamond/diamond.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once

namespace diamond {

} // namespace diamond
116 changes: 116 additions & 0 deletions exercises/practice/diamond/diamond_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include "diamond.h"
#ifdef EXERCISM_TEST_SUITE
#include <catch2/catch.hpp>
#else
#include "test/catch.hpp"
#endif


/*
The tests contained within this canonical data file are suitable
for value-based testing, in which each test case checks that the
value returned by the function under test is in every way
identical to a given expected value.
This exercise is also amenable to property-based testing, in which
each test case verifies that the value returned by the function
under test exhibits a specific desired property.
Several tracks (notably, C# and Go) forgo the value-based tests
below in favor of property-based tests. If you are feeling
adventurous and would like to use this exercise to introduce the
concept of property-based testing to participants in your track,
please ignore the value-based tests below and instead reference
the test suites in the aforementioned tracks.
*/

TEST_CASE("Degenerate case with a single 'A' row", "[202fb4cc-6a38-4883-9193-a29d5cb92076]") {
std::vector<std::string> expected{"A"};
REQUIRE(diamond::rows('A') == expected);
}



TEST_CASE("Degenerate case with no row containing 3 distinct groups of spaces", "[bd6a6d78-9302-42e9-8f60-ac1461e9abae]") {
std::vector<std::string> expected{" A ",
"B B",
" A "};
REQUIRE(diamond::rows('B') == expected);
}

TEST_CASE("Smallest non-degenerate case with odd diamond side length", "[af8efb49-14ed-447f-8944-4cc59ce3fd76]") {
std::vector<std::string> expected{" A ",
" B B ",
"C C",
" B B ",
" A "};
REQUIRE(diamond::rows('C') == expected);
}

TEST_CASE("Smallest non-degenerate case with even diamond side length", "[e0c19a95-9888-4d05-86a0-fa81b9e70d1d]") {
std::vector<std::string> expected{" A ",
" B B ",
" C C ",
"D D",
" C C ",
" B B ",
" A "};
REQUIRE(diamond::rows('D') == expected);
}

TEST_CASE("Largest possible diamond", "[82ea9aa9-4c0e-442a-b07e-40204e925944]") {
std::vector<std::string> expected{" A ",
" B B ",
" C C ",
" D D ",
" E E ",
" F F ",
" G G ",
" H H ",
" I I ",
" J J ",
" K K ",
" L L ",
" M M ",
" N N ",
" O O ",
" P P ",
" Q Q ",
" R R ",
" S S ",
" T T ",
" U U ",
" V V ",
" W W ",
" X X ",
" Y Y ",
"Z Z",
" Y Y ",
" X X ",
" W W ",
" V V ",
" U U ",
" T T ",
" S S ",
" R R ",
" Q Q ",
" P P ",
" O O ",
" N N ",
" M M ",
" L L ",
" K K ",
" J J ",
" I I ",
" H H ",
" G G ",
" F F ",
" E E ",
" D D ",
" C C ",
" B B ",
" A "};
REQUIRE(diamond::rows('Z') == expected);
}
#if defined(EXERCISM_RUN_ALL_TESTS)
#endif
Loading

0 comments on commit 29458db

Please sign in to comment.