Avatar of mikewalker

mikewalker's solution

to Diamond in the C Track

Published at Mar 02 2019 · 0 comments
Instructions
Test suite
Solution

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':

A

Diamond for letter 'C':

··A··
·B·B·
C···C
·B·B·
··A··

Diamond for letter 'E':

····A····
···B·B···
··C···C··
·D·····D·
E·······E
·D·····D·
··C···C··
···B·B···
····A····

Getting Started

Make sure you have read the "Guides" section of the C track on the Exercism site. This covers the basic information on setting up the development environment expected by the exercises.

Passing the Tests

Get the first test compiling, linking and passing by following the three rules of test-driven development.

The included makefile can be used to create and run the tests using the test task.

make test

Create just the functions you need to satisfy any compiler errors and get the test to fail. Then write just enough code to get the test to pass. Once you've done that, move onto the next test.

As you progress through the tests, take the time to refactor your implementation for readability and expressiveness and then go on to the next test.

Try to use standard C99 facilities in preference to writing your own low-level algorithms or facilities by hand.

Source

Seb Rose http://claysnow.co.uk/recycling-tests-in-tdd/

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

test_diamond.c

#include "vendor/unity.h"
#include "../src/diamond.h"
#include <stdlib.h>

#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))

void setUp(void)
{
}

void tearDown(void)
{
}

static void free_all(char **diamond)
{
   free(diamond[0]);
   free(diamond);
}

static void test_rows_degenerate_case_with_a_single_a_row(void)
{
   const char letter = 'A';
   const char *expected[] = {
      "A"
   };
   char **diamond = make_diamond(letter);
   TEST_ASSERT_EQUAL_STRING_ARRAY(expected, diamond, ARRAY_SIZE(expected));
   free_all(diamond);
}

static void
test_rows_degenerate_case_with_no_row_with_3_distinct_groups_of_spaces(void)
{
   TEST_IGNORE();               // delete this line to run test
   const char letter = 'B';
   const char *expected[] = {
      " A ",
      "B B",
      " A "
   };
   char **diamond = make_diamond(letter);
   TEST_ASSERT_EQUAL_STRING_ARRAY(expected, diamond, ARRAY_SIZE(expected));
   free_all(diamond);
}

static void
test_rows_smallest_non_degenerate_case_with_odd_diamond_side_length(void)
{
   TEST_IGNORE();
   const char letter = 'C';
   const char *expected[] = {
      "  A  ",
      " B B ",
      "C   C",
      " B B ",
      "  A  "
   };
   char **diamond = make_diamond(letter);
   TEST_ASSERT_EQUAL_STRING_ARRAY(expected, diamond, ARRAY_SIZE(expected));
   free_all(diamond);
}

static void
test_rows_smallest_non_degenerate_case_with_even_diamond_side_length(void)
{
   TEST_IGNORE();
   const char letter = 'D';
   const char *expected[] = {
      "   A   ",
      "  B B  ",
      " C   C ",
      "D     D",
      " C   C ",
      "  B B  ",
      "   A   "
   };
   char **diamond = make_diamond(letter);
   TEST_ASSERT_EQUAL_STRING_ARRAY(expected, diamond, ARRAY_SIZE(expected));
   free_all(diamond);
}

static void test_rows_largest_possible_diamond(void)
{
   TEST_IGNORE();
   const char letter = 'Z';
   const char *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                         "
   };
   char **diamond = make_diamond(letter);
   TEST_ASSERT_EQUAL_STRING_ARRAY(expected, diamond, ARRAY_SIZE(expected));
   free_all(diamond);
}

int main(void)
{
   UnityBegin("test/test_diamond.c");

   RUN_TEST(test_rows_degenerate_case_with_a_single_a_row);
   RUN_TEST
       (test_rows_degenerate_case_with_no_row_with_3_distinct_groups_of_spaces);
   RUN_TEST
       (test_rows_smallest_non_degenerate_case_with_odd_diamond_side_length);
   RUN_TEST
       (test_rows_smallest_non_degenerate_case_with_even_diamond_side_length);
   RUN_TEST(test_rows_largest_possible_diamond);

   return UnityEnd();
}

src/diamond.c

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "diamond.h"

char **make_diamond(char letter)
{
    if (!isalpha(letter))
        return NULL;

    int rows = (letter - 'A') * 2 + 1;
    int col_count = rows;

    // Allocate structure and check for memory error.
    char **diamond = malloc(sizeof(char *) * rows);
    if (diamond)
        *diamond = malloc(sizeof(char) * rows * (col_count + 1));

    if (diamond == NULL)
    {
        printf("%s\n", "Memory allocation error.");
        return NULL;
    }
    memset(*diamond, ' ', sizeof(char) * rows * (col_count + 1));

    // update row pointers so that we can index the rows
    for (int i = 0; i < rows; i++)
    {
        diamond[i] = *diamond + i * (rows + 1);
    }

    // Terminate all rows
    for (int i = 0; i < rows; i++)
    {
        diamond[i][col_count] = '\0';
    }

    // Build the diamond
    int middle = rows / 2;
    diamond[0][middle] = 'A';
    diamond[rows - 1][middle] = 'A';

    for (int i = 1; i <= middle; i++)
    {
        char ch = 'A' + i;

        // Top row
        diamond[i][middle - i] = ch;
        diamond[i][middle + i] = ch;

        if (ch == letter)
            break; // Middle, were done

        // Bottom row
        diamond[rows - i - 1][middle + i] = ch;
        diamond[rows - i - 1][middle - i] = ch;
    }
    return diamond;
}

src/diamond.h

#ifndef DIAMOND_H
#define DIAMOND_H

char **make_diamond(char letter);

#endif

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?