From 833408081556b7958b55a2b49d84fdc391812f2b Mon Sep 17 00:00:00 2001 From: Eric Willigers Date: Fri, 3 Oct 2025 09:28:51 +1000 Subject: [PATCH] Add diamond exercise --- config.json | 8 + .../practice/diamond/.docs/instructions.md | 52 +++++++ exercises/practice/diamond/.meta/config.json | 19 +++ exercises/practice/diamond/.meta/example.zig | 31 ++++ exercises/practice/diamond/.meta/tests.toml | 25 ++++ exercises/practice/diamond/diamond.zig | 9 ++ exercises/practice/diamond/test_diamond.zig | 137 ++++++++++++++++++ 7 files changed, 281 insertions(+) create mode 100644 exercises/practice/diamond/.docs/instructions.md create mode 100644 exercises/practice/diamond/.meta/config.json create mode 100644 exercises/practice/diamond/.meta/example.zig create mode 100644 exercises/practice/diamond/.meta/tests.toml create mode 100644 exercises/practice/diamond/diamond.zig create mode 100644 exercises/practice/diamond/test_diamond.zig diff --git a/config.json b/config.json index 3f3ea979..fc78b91c 100644 --- a/config.json +++ b/config.json @@ -780,6 +780,14 @@ "prerequisites": [], "difficulty": 4 }, + { + "slug": "diamond", + "name": "Diamond", + "uuid": "7f07006a-eafb-4dc3-87f3-4bbf945f6da7", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, { "slug": "house", "name": "House", diff --git a/exercises/practice/diamond/.docs/instructions.md b/exercises/practice/diamond/.docs/instructions.md new file mode 100644 index 00000000..3034802f --- /dev/null +++ b/exercises/practice/diamond/.docs/instructions.md @@ -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···· +``` diff --git a/exercises/practice/diamond/.meta/config.json b/exercises/practice/diamond/.meta/config.json new file mode 100644 index 00000000..b9853b2d --- /dev/null +++ b/exercises/practice/diamond/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "diamond.zig" + ], + "test": [ + "test_diamond.zig" + ], + "example": [ + ".meta/example.zig" + ] + }, + "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/" +} diff --git a/exercises/practice/diamond/.meta/example.zig b/exercises/practice/diamond/.meta/example.zig new file mode 100644 index 00000000..29d774fb --- /dev/null +++ b/exercises/practice/diamond/.meta/example.zig @@ -0,0 +1,31 @@ +const std = @import("std"); +const mem = std.mem; + +fn free(allocator: mem.Allocator, array_list: *std.ArrayList([]u8)) void { + for (array_list.items) |item| { + allocator.free(item); + } + array_list.deinit(allocator); +} + +pub fn rows(allocator: mem.Allocator, letter: u8) mem.Allocator.Error![][]u8 { + std.debug.assert(letter >= 'A'); + std.debug.assert(letter <= 'Z'); + const mid = letter - 'A'; + const len = 2 * mid + 1; + var array_list = try std.ArrayList([]u8).initCapacity(allocator, len); + errdefer free(allocator, &array_list); + + for (0..len) |i| { + var row = try allocator.alloc(u8, len); + @memset(row, ' '); + const j = if (i <= mid) i else (len - 1 - i); + const ch: u8 = @as(u8, @intCast('A' + j)); + row[mid - j] = ch; + row[mid + j] = ch; + + array_list.appendAssumeCapacity(row); + } + + return array_list.toOwnedSlice(allocator); +} diff --git a/exercises/practice/diamond/.meta/tests.toml b/exercises/practice/diamond/.meta/tests.toml new file mode 100644 index 00000000..4e7802ec --- /dev/null +++ b/exercises/practice/diamond/.meta/tests.toml @@ -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" diff --git a/exercises/practice/diamond/diamond.zig b/exercises/practice/diamond/diamond.zig new file mode 100644 index 00000000..80cd9e82 --- /dev/null +++ b/exercises/practice/diamond/diamond.zig @@ -0,0 +1,9 @@ +const std = @import("std"); +const mem = std.mem; + +pub fn rows(allocator: mem.Allocator, letter: u8) mem.Allocator.Error![][]u8 { + std.debug.assert(letter >= 'A'); + std.debug.assert(letter <= 'Z'); + _ = allocator; + @compileError("please implement the rows function"); +} diff --git a/exercises/practice/diamond/test_diamond.zig b/exercises/practice/diamond/test_diamond.zig new file mode 100644 index 00000000..f95251a9 --- /dev/null +++ b/exercises/practice/diamond/test_diamond.zig @@ -0,0 +1,137 @@ +const std = @import("std"); +const testing = std.testing; + +const rows = @import("diamond.zig").rows; + +fn free(slices: [][]u8) void { + for (slices) |slice| { + testing.allocator.free(slice); + } + testing.allocator.free(slices); +} + +fn testRows(allocator: std.mem.Allocator, expected: []const []const u8, letter: u8) !void { + const actual = try rows(allocator, letter); + defer free(actual); + try testing.expectEqual(expected.len, actual.len); + for (0..expected.len) |i| { + try testing.expectEqualStrings(expected[i], actual[i]); + } +} + +test "Degenerate case with a single 'A' row" { + const expected = [_][]const u8{ + "A", // + }; + try std.testing.checkAllAllocationFailures( + std.testing.allocator, + testRows, + .{ &expected, 'A' }, + ); +} + +test "Degenerate case with no row containing 3 distinct groups of spaces" { + const expected = [_][]const u8{ + " A ", // + "B B", // + " A ", // + }; + try std.testing.checkAllAllocationFailures( + std.testing.allocator, + testRows, + .{ &expected, 'B' }, + ); +} + +test "Smallest non-degenerate case with odd diamond side length" { + const expected = [_][]const u8{ + " A ", // + " B B ", // + "C C", // + " B B ", // + " A ", // + }; + try std.testing.checkAllAllocationFailures( + std.testing.allocator, + testRows, + .{ &expected, 'C' }, + ); +} + +test "Smallest non-degenerate case with even diamond side length" { + const expected = [_][]const u8{ + " A ", // + " B B ", // + " C C ", // + "D D", // + " C C ", // + " B B ", // + " A ", // + }; + try std.testing.checkAllAllocationFailures( + std.testing.allocator, + testRows, + .{ &expected, 'D' }, + ); +} + +test "Largest possible diamond" { + const expected = [_][]const u8{ + " 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 ", // + }; + try std.testing.checkAllAllocationFailures( + std.testing.allocator, + testRows, + .{ &expected, 'Z' }, + ); +}