 # ajborla's solution

## to Diamond in the C Track

Published at Aug 10 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.

## 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))

void setUp(void)
{
}

void tearDown(void)
{
}

static void free_all(char **diamond)
{
free(diamond);
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

``````/* ------------------------------------------------------------------------- */
/* exercism.io                                                               */
/* C Track Exercise: diamond                                                 */
/* Contributed: Anthony J. Borla (ajborla@bigpond.com)                       */
/* ------------------------------------------------------------------------- */

#include "diamond.h"

#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>

// Invariant values (computed once at startup) for building diamond
typedef struct diamond_invariants
{
char character;
int8_t code;

int8_t num_cols_total;
int8_t num_rows_top;
int8_t num_rows_bottom;

} diamond_invariants_t;

static const char CH_SPACE = ' ';
static const char CH_NUL = '\0';
static const char CH_UPPER_A = 'A';

/* ------------------------------------------------------------------------- */
/* Given a character, 'character', returns a structure of invariant values   */
/* needed in building a diamond. Note, a -1-filled structure is returned if  */
/* character is not an uppercase letter                                      */
/* ------------------------------------------------------------------------- */
static diamond_invariants_t make_diamond_invariants(char character)
{
diamond_invariants_t diamond_invariants = { character, -1, -1, -1, -1 };

// Fill structure only if 'character' range is 'A' through 'Z'
if (isalpha(character))
{
// Character
diamond_invariants.character = toupper(character);

// Character as number ('A' -> 0, 'B' -> 1, ... 'Z' -> 25)
diamond_invariants.code = diamond_invariants.character - CH_UPPER_A;

// Number of columns in (or length of) row
diamond_invariants.num_cols_total = 2 * diamond_invariants.code + 1;

// Special case 'A' - always a single row
if (diamond_invariants.character == CH_UPPER_A)
diamond_invariants.num_rows_top = 0;
else
diamond_invariants.num_rows_top =
(diamond_invariants.num_cols_total - 1) / 2;

// Same number of top and bottom rows
diamond_invariants.num_rows_bottom = diamond_invariants.num_rows_top;
}

return diamond_invariants;
}

/* ------------------------------------------------------------------------- */
/* Given a character, 'character', a structure of invariant values, and a    */
/* buffer area, builds a row and returns the buffer address                  */
/* ------------------------------------------------------------------------- */
static char* gen_diamond_row(char character,
diamond_invariants_t* ptr_diamond_invariants,
char* buffer)
{
// Ensure current character is uppercased
char current_character = toupper(character);

// Check for buffer allocation
if (!buffer) return NULL;

// "A" (special case - single letter in row, no additional spacing)
if (ptr_diamond_invariants->character == CH_UPPER_A)
{
buffer = CH_UPPER_A; buffer = CH_NUL;
}

// "C...C" Reference row, marks widest point of diamond. Supplied character
// starts and ends the row, only spaces are intervening ones
else if (ptr_diamond_invariants->character == current_character)
{
// Bounding (first, last) characters
buffer =
buffer[ptr_diamond_invariants->num_cols_total - 1] = current_character;

int8_t i = 1, last_col_idx = ptr_diamond_invariants->num_cols_total - 1;
while (i < last_col_idx) buffer[i++] = CH_SPACE;

// Zero terminator
buffer[ptr_diamond_invariants->num_cols_total] = CH_NUL;
}

// "..C....C.." Non-reference rows, containing leading, intervening, and
// trailing spaces
else
{
// Zero terminator
buffer[ptr_diamond_invariants->num_cols_total] = CH_NUL;

// Fill row with spaces
int8_t i = 0, last_col_idx = ptr_diamond_invariants->num_cols_total;
while (i < last_col_idx) buffer[i++] = CH_SPACE;

// Compute indexes, place the current character in those locations
ptr_diamond_invariants->code - (current_character - CH_UPPER_A);

buffer[ptr_diamond_invariants->num_cols_total - num_pad_spaces - 1] =
current_character;
}

return buffer;
}

char** make_diamond(char letter)
{
// Compute and package runtime values
diamond_invariants_t diamond_invariants = make_diamond_invariants(letter);

// Ensure we have an uppercase caharacter
char character = toupper(letter);

// Compute required rows then allocate and validate memory allocation
int8_t rows =
diamond_invariants.num_rows_bottom + diamond_invariants.num_rows_top + 1;

char** tbl = NULL;

if ((tbl = malloc(rows * sizeof(char*))) != NULL)
{
// Generate top part of diamond
for (int8_t c = CH_UPPER_A; c < character; ++c)
gen_diamond_row(c, &diamond_invariants,
(tbl[--rows] = malloc(diamond_invariants.num_cols_total * sizeof(char))));

// Generate bottom part of diamond
for (int8_t c = character; c >= CH_UPPER_A; --c)
gen_diamond_row(c, &diamond_invariants,
(tbl[--rows] = malloc(diamond_invariants.num_cols_total * sizeof(char))));
}

// Return diamond table - caller will free memory
return tbl;
}``````

### src/diamond.h

``````/* ------------------------------------------------------------------------- */
/* exercism.io                                                               */
/* C Track Exercise: diamond                                                 */
/* Contributed: Anthony J. Borla (ajborla@bigpond.com)                       */
/* ------------------------------------------------------------------------- */

#ifndef DIAMOND_H
#define DIAMOND_H

/* ------------------------------------------------------------------------- */
/* Given a letter, 'letter', returns a table of strings which comprise the   */
/* rows of a 'diamond'. The supplied letter is used to form the reference    */
/* row; it is the longest row, and appears in the middle of the shape. The   */
/* other rows (an equal number above and below the reference row) are formed */
/* with letters of the alphabet counting down from the supplied letter until */
/* the letter 'A'. Examples:                                                 */
/*                                                                           */
/*        A                                                                  */
/*       B B           A                                                     */
/*      C   C         B B         A                                          */
/*     D     D       C   C       B B    A                                    */
/*      C   C         B B         A                                          */
/*       B B           A                                                     */
/*        A                                                                  */
/* ------------------------------------------------------------------------- */
char** make_diamond(char letter);

#endif``````