diff --git a/exercises/practice/all-your-base/.meta/supplements.json b/exercises/practice/all-your-base/.meta/supplements.json new file mode 100644 index 00000000..a29767c5 --- /dev/null +++ b/exercises/practice/all-your-base/.meta/supplements.json @@ -0,0 +1,14 @@ +{ + "cases": [ + { + "description": "empty list - second call returns different memory", + "property": "rebase", + "input": { + "inputBase": 10, + "digits": [], + "outputBase": 2 + }, + "expected": [0] + } + ] +} diff --git a/exercises/practice/all-your-base/test_all_your_base.zig b/exercises/practice/all-your-base/test_all_your_base.zig index 333316e2..b1a2dd06 100644 --- a/exercises/practice/all-your-base/test_all_your_base.zig +++ b/exercises/practice/all-your-base/test_all_your_base.zig @@ -5,84 +5,46 @@ const all_your_base = @import("all_your_base.zig"); const convert = all_your_base.convert; const ConversionError = all_your_base.ConversionError; -test "single bit one to decimal" { - const expected = [_]u32{1}; - const digits = [_]u32{1}; - const input_base = 2; - const output_base = 10; - const actual = try convert(testing.allocator, &digits, input_base, output_base); +fn testConvert(digits: []const u32, input_base: u32, output_base: u32, expected: []const u32) !void { + const actual = try convert(testing.allocator, digits, input_base, output_base); defer testing.allocator.free(actual); - try testing.expectEqualSlices(u32, &expected, actual); + try testing.expectEqualSlices(u32, expected, actual); +} + +fn testConvertError(digits: []const u32, input_base: u32, output_base: u32, expected: ConversionError) !void { + try testing.expectError(expected, convert(testing.allocator, digits, input_base, output_base)); +} + +test "single bit one to decimal" { + try testConvert(&[_]u32{1}, 2, 10, &[_]u32{1}); } test "binary to single decimal" { - const expected = [_]u32{5}; - const digits = [_]u32{ 1, 0, 1 }; - const input_base = 2; - const output_base = 10; - const actual = try convert(testing.allocator, &digits, input_base, output_base); - defer testing.allocator.free(actual); - try testing.expectEqualSlices(u32, &expected, actual); + try testConvert(&[_]u32{ 1, 0, 1 }, 2, 10, &[_]u32{5}); } test "single decimal to binary" { - const expected = [_]u32{ 1, 0, 1 }; - const digits = [_]u32{5}; - const input_base = 10; - const output_base = 2; - const actual = try convert(testing.allocator, &digits, input_base, output_base); - defer testing.allocator.free(actual); - try testing.expectEqualSlices(u32, &expected, actual); + try testConvert(&[_]u32{5}, 10, 2, &[_]u32{ 1, 0, 1 }); } test "binary to multiple decimal" { - const expected = [_]u32{ 4, 2 }; - const digits = [_]u32{ 1, 0, 1, 0, 1, 0 }; - const input_base = 2; - const output_base = 10; - const actual = try convert(testing.allocator, &digits, input_base, output_base); - defer testing.allocator.free(actual); - try testing.expectEqualSlices(u32, &expected, actual); + try testConvert(&[_]u32{ 1, 0, 1, 0, 1, 0 }, 2, 10, &[_]u32{ 4, 2 }); } test "decimal to binary" { - const expected = [_]u32{ 1, 0, 1, 0, 1, 0 }; - const digits = [_]u32{ 4, 2 }; - const input_base = 10; - const output_base = 2; - const actual = try convert(testing.allocator, &digits, input_base, output_base); - defer testing.allocator.free(actual); - try testing.expectEqualSlices(u32, &expected, actual); + try testConvert(&[_]u32{ 4, 2 }, 10, 2, &[_]u32{ 1, 0, 1, 0, 1, 0 }); } test "trinary to hexadecimal" { - const expected = [_]u32{ 2, 10 }; - const digits = [_]u32{ 1, 1, 2, 0 }; - const input_base = 3; - const output_base = 16; - const actual = try convert(testing.allocator, &digits, input_base, output_base); - defer testing.allocator.free(actual); - try testing.expectEqualSlices(u32, &expected, actual); + try testConvert(&[_]u32{ 1, 1, 2, 0 }, 3, 16, &[_]u32{ 2, 10 }); } test "hexadecimal to trinary" { - const expected = [_]u32{ 1, 1, 2, 0 }; - const digits = [_]u32{ 2, 10 }; - const input_base = 16; - const output_base = 3; - const actual = try convert(testing.allocator, &digits, input_base, output_base); - defer testing.allocator.free(actual); - try testing.expectEqualSlices(u32, &expected, actual); + try testConvert(&[_]u32{ 2, 10 }, 16, 3, &[_]u32{ 1, 1, 2, 0 }); } test "15-bit integer" { - const expected = [_]u32{ 6, 10, 45 }; - const digits = [_]u32{ 3, 46, 60 }; - const input_base = 97; - const output_base = 73; - const actual = try convert(testing.allocator, &digits, input_base, output_base); - defer testing.allocator.free(actual); - try testing.expectEqualSlices(u32, &expected, actual); + try testConvert(&[_]u32{ 3, 46, 60 }, 97, 73, &[_]u32{ 6, 10, 45 }); } test "empty list - second call returns different memory" { @@ -103,86 +65,37 @@ test "empty list - second call returns different memory" { } test "empty list" { - const expected = [_]u32{0}; - const digits = [_]u32{}; - const input_base = 2; - const output_base = 10; - const actual = try convert(testing.allocator, &digits, input_base, output_base); - defer testing.allocator.free(actual); - try testing.expectEqualSlices(u32, &expected, actual); + try testConvert(&[_]u32{}, 2, 10, &[_]u32{0}); } test "single zero" { - const expected = [_]u32{0}; - const digits = [_]u32{0}; - const input_base = 10; - const output_base = 2; - const actual = try convert(testing.allocator, &digits, input_base, output_base); - defer testing.allocator.free(actual); - try testing.expectEqualSlices(u32, &expected, actual); + try testConvert(&[_]u32{0}, 10, 2, &[_]u32{0}); } test "multiple zeros" { - const expected = [_]u32{0}; - const digits = [_]u32{ 0, 0, 0 }; - const input_base = 10; - const output_base = 2; - const actual = try convert(testing.allocator, &digits, input_base, output_base); - defer testing.allocator.free(actual); - try testing.expectEqualSlices(u32, &expected, actual); + try testConvert(&[_]u32{ 0, 0, 0 }, 10, 2, &[_]u32{0}); } test "leading zeros" { - const expected = [_]u32{ 4, 2 }; - const digits = [_]u32{ 0, 6, 0 }; - const input_base = 7; - const output_base = 10; - const actual = try convert(testing.allocator, &digits, input_base, output_base); - defer testing.allocator.free(actual); - try testing.expectEqualSlices(u32, &expected, actual); + try testConvert(&[_]u32{ 0, 6, 0 }, 7, 10, &[_]u32{ 4, 2 }); } test "input base is one" { - const expected = ConversionError.InvalidInputBase; - const digits = [_]u32{0}; - const input_base = 1; - const output_base = 10; - const actual = convert(testing.allocator, &digits, input_base, output_base); - try testing.expectError(expected, actual); + try testConvertError(&[_]u32{0}, 1, 10, ConversionError.InvalidInputBase); } test "input base is zero" { - const expected = ConversionError.InvalidInputBase; - const digits = [_]u32{}; - const input_base = 0; - const output_base = 10; - const actual = convert(testing.allocator, &digits, input_base, output_base); - try testing.expectError(expected, actual); + try testConvertError(&[_]u32{}, 0, 10, ConversionError.InvalidInputBase); } test "invalid positive digit" { - const expected = ConversionError.InvalidDigit; - const digits = [_]u32{ 1, 2, 1, 0, 1, 0 }; - const input_base = 2; - const output_base = 10; - const actual = convert(testing.allocator, &digits, input_base, output_base); - try testing.expectError(expected, actual); + try testConvertError(&[_]u32{ 1, 2, 1, 0, 1, 0 }, 2, 10, ConversionError.InvalidDigit); } test "output base is one" { - const expected = ConversionError.InvalidOutputBase; - const digits = [_]u32{ 1, 0, 1, 0, 1, 0 }; - const input_base = 2; - const output_base = 1; - const actual = convert(testing.allocator, &digits, input_base, output_base); - try testing.expectError(expected, actual); + try testConvertError(&[_]u32{ 1, 0, 1, 0, 1, 0 }, 2, 1, ConversionError.InvalidOutputBase); } test "output base is zero" { - const expected = ConversionError.InvalidOutputBase; - const digits = [_]u32{7}; - const input_base = 10; - const output_base = 0; - const actual = convert(testing.allocator, &digits, input_base, output_base); - try testing.expectError(expected, actual); + try testConvertError(&[_]u32{7}, 10, 0, ConversionError.InvalidOutputBase); } diff --git a/exercises/practice/armstrong-numbers/.meta/supplements.json b/exercises/practice/armstrong-numbers/.meta/supplements.json new file mode 100644 index 00000000..34222074 --- /dev/null +++ b/exercises/practice/armstrong-numbers/.meta/supplements.json @@ -0,0 +1,20 @@ +{ + "cases": [ + { + "description": "38-digit number that is not an armstrong number", + "property": "isArmstrongNumber", + "input": { + "number": 99999999999999999999999999999999999999 + }, + "expected": false + }, + { + "description": "the largest 128-bit unsigned integer value is not an armstrong number", + "property": "isArmstrongNumber", + "input": { + "number": 340282366920938463463374607431768211455 + }, + "expected": false + } + ] +} diff --git a/exercises/practice/armstrong-numbers/test_armstrong_numbers.zig b/exercises/practice/armstrong-numbers/test_armstrong_numbers.zig index 4e8f05fe..96ed48de 100644 --- a/exercises/practice/armstrong-numbers/test_armstrong_numbers.zig +++ b/exercises/practice/armstrong-numbers/test_armstrong_numbers.zig @@ -3,54 +3,58 @@ const testing = std.testing; const isArmstrongNumber = @import("armstrong_numbers.zig").isArmstrongNumber; -test "zero is an armstrong number" { - try testing.expect(isArmstrongNumber(0)); +fn testIsArmstrongNumber(number: u128, expected: bool) !void { + try testing.expectEqual(expected, isArmstrongNumber(number)); } -test "single-digit numbers are armstrong numbers" { - try testing.expect(isArmstrongNumber(5)); +test "Zero is an Armstrong number" { + try testIsArmstrongNumber(0, true); } -test "there are no two-digit armstrong numbers" { - try testing.expect(!isArmstrongNumber(10)); +test "Single-digit numbers are Armstrong numbers" { + try testIsArmstrongNumber(5, true); } -test "three-digit number that is an armstrong number" { - try testing.expect(isArmstrongNumber(153)); +test "There are no two-digit Armstrong numbers" { + try testIsArmstrongNumber(10, false); } -test "three-digit number that is not an armstrong number" { - try testing.expect(!isArmstrongNumber(100)); +test "Three-digit number that is an Armstrong number" { + try testIsArmstrongNumber(153, true); } -test "four-digit number that is an armstrong number" { - try testing.expect(isArmstrongNumber(9_474)); +test "Three-digit number that is not an Armstrong number" { + try testIsArmstrongNumber(100, false); } -test "four-digit number that is not an armstrong number" { - try testing.expect(!isArmstrongNumber(9_475)); +test "Four-digit number that is an Armstrong number" { + try testIsArmstrongNumber(9_474, true); } -test "seven-digit number that is an armstrong number" { - try testing.expect(isArmstrongNumber(9_926_315)); +test "Four-digit number that is not an Armstrong number" { + try testIsArmstrongNumber(9_475, false); } -test "seven-digit number that is not an armstrong number" { - try testing.expect(!isArmstrongNumber(9_926_314)); +test "Seven-digit number that is an Armstrong number" { + try testIsArmstrongNumber(9_926_315, true); } -test "33-digit number that is an armstrong number" { - try testing.expect(isArmstrongNumber(186_709_961_001_538_790_100_634_132_976_990)); +test "Seven-digit number that is not an Armstrong number" { + try testIsArmstrongNumber(9_926_314, false); } -test "38-digit number that is not an armstrong number" { - try testing.expect(!isArmstrongNumber(99_999_999_999_999_999_999_999_999_999_999_999_999)); +test "Armstrong number containing seven zeroes" { + try testIsArmstrongNumber(186_709_961_001_538_790_100_634_132_976_990, true); +} + +test "The largest and last Armstrong number" { + try testIsArmstrongNumber(115_132_219_018_763_992_565_095_597_973_971_522_401, true); } -test "the largest and last armstrong number" { - try testing.expect(isArmstrongNumber(115_132_219_018_763_992_565_095_597_973_971_522_401)); +test "38-digit number that is not an armstrong number" { + try testIsArmstrongNumber(99_999_999_999_999_999_999_999_999_999_999_999_999, false); } test "the largest 128-bit unsigned integer value is not an armstrong number" { - try testing.expect(!isArmstrongNumber(340_282_366_920_938_463_463_374_607_431_768_211_455)); + try testIsArmstrongNumber(340_282_366_920_938_463_463_374_607_431_768_211_455, false); } diff --git a/exercises/practice/yacht/.meta/supplements.json b/exercises/practice/yacht/.meta/supplements.json new file mode 100644 index 00000000..7c8465fb --- /dev/null +++ b/exercises/practice/yacht/.meta/supplements.json @@ -0,0 +1,31 @@ +{ + "cases": [ + { + "description": "Full house three small, two big, alternative order", + "property": "score", + "input": { + "dice": [4, 4, 2, 2, 2], + "category": "full house" + }, + "expected": 14 + }, + { + "description": "Full house two small, three big, alternative order", + "property": "score", + "input": { + "dice": [3, 5, 5, 3, 5], + "category": "full house" + }, + "expected": 21 + }, + { + "description": "Four of a Kind alternative order", + "property": "score", + "input": { + "dice": [4, 4, 6, 4, 4], + "category": "four of a kind" + }, + "expected": 16 + } + ] +} diff --git a/exercises/practice/yacht/test_yacht.zig b/exercises/practice/yacht/test_yacht.zig index f09ea2b1..a79e59c5 100644 --- a/exercises/practice/yacht/test_yacht.zig +++ b/exercises/practice/yacht/test_yacht.zig @@ -2,195 +2,136 @@ const std = @import("std"); const testing = std.testing; const score = @import("yacht.zig").score; +const Category = @import("yacht.zig").Category; -test "yacht" { - const expected: u32 = 50; - const actual = score([_]u3{ 5, 5, 5, 5, 5 }, .yacht); - try testing.expectEqual(expected, actual); +fn testScore(dice: [5]u3, category: Category, expected: u32) !void { + try testing.expectEqual(expected, score(dice, category)); } -test "not yacht" { - const expected: u32 = 0; - const actual = score([_]u3{ 1, 3, 3, 2, 5 }, .yacht); - try testing.expectEqual(expected, actual); +test "no ones" { + try testScore([_]u3{ 4, 3, 6, 5, 5 }, .ones, 0); } test "ones" { - const expected: u32 = 3; - const actual = score([_]u3{ 1, 1, 1, 3, 5 }, .ones); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 1, 1, 1, 3, 5 }, .ones, 3); } test "ones, out of order" { - const expected: u32 = 3; - const actual = score([_]u3{ 3, 1, 1, 5, 1 }, .ones); - try testing.expectEqual(expected, actual); -} - -test "no ones" { - const expected: u32 = 0; - const actual = score([_]u3{ 4, 3, 6, 5, 5 }, .ones); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 3, 1, 1, 5, 1 }, .ones, 3); } test "twos" { - const expected: u32 = 2; - const actual = score([_]u3{ 2, 3, 4, 5, 6 }, .twos); - try testing.expectEqual(expected, actual); -} - -test "fours" { - const expected: u32 = 8; - const actual = score([_]u3{ 1, 4, 1, 4, 1 }, .fours); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 2, 3, 4, 5, 6 }, .twos, 2); } test "yacht counted as threes" { - const expected: u32 = 15; - const actual = score([_]u3{ 3, 3, 3, 3, 3 }, .threes); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 3, 3, 3, 3, 3 }, .threes, 15); } -test "yacht of 3s counted as fives" { - const expected: u32 = 0; - const actual = score([_]u3{ 3, 3, 3, 3, 3 }, .fives); - try testing.expectEqual(expected, actual); +test "fours" { + try testScore([_]u3{ 1, 4, 1, 4, 1 }, .fours, 8); } test "fives" { - const expected: u32 = 10; - const actual = score([_]u3{ 1, 5, 3, 5, 3 }, .fives); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 1, 5, 3, 5, 3 }, .fives, 10); } -test "sixes" { - const expected: u32 = 6; - const actual = score([_]u3{ 2, 3, 4, 5, 6 }, .sixes); - try testing.expectEqual(expected, actual); +test "yacht of 3s counted as fives" { + try testScore([_]u3{ 3, 3, 3, 3, 3 }, .fives, 0); } -test "full house two small, three big" { - const expected: u32 = 16; - const actual = score([_]u3{ 2, 2, 4, 4, 4 }, .full_house); - try testing.expectEqual(expected, actual); +test "sixes" { + try testScore([_]u3{ 2, 3, 4, 5, 6 }, .sixes, 6); } -test "full house two small, three big, alternative order" { - const expected: u32 = 14; - const actual = score([_]u3{ 4, 4, 2, 2, 2 }, .full_house); - try testing.expectEqual(expected, actual); +test "four of a kind is not a full house" { + try testScore([_]u3{ 1, 4, 4, 4, 4 }, .full_house, 0); } test "full house three small, two big" { - const expected: u32 = 19; - const actual = score([_]u3{ 5, 3, 3, 5, 3 }, .full_house); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 5, 3, 3, 5, 3 }, .full_house, 19); } test "full house three small, two big, alternative order" { - const expected: u32 = 21; - const actual = score([_]u3{ 3, 5, 5, 3, 5 }, .full_house); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 4, 4, 2, 2, 2 }, .full_house, 14); } -test "two pair is not a full house" { - const expected: u32 = 0; - const actual = score([_]u3{ 2, 2, 4, 4, 5 }, .full_house); - try testing.expectEqual(expected, actual); +test "full house two small, three big" { + try testScore([_]u3{ 2, 2, 4, 4, 4 }, .full_house, 16); } -test "four of a kind is not a full house" { - const expected: u32 = 0; - const actual = score([_]u3{ 1, 4, 4, 4, 4 }, .full_house); - try testing.expectEqual(expected, actual); +test "full house two small, three big, alternative order" { + try testScore([_]u3{ 3, 5, 5, 3, 5 }, .full_house, 21); +} + +test "two pair is not a full house" { + try testScore([_]u3{ 2, 2, 4, 4, 5 }, .full_house, 0); } test "yacht is not a full house" { - const expected: u32 = 0; - const actual = score([_]u3{ 2, 2, 2, 2, 2 }, .full_house); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 2, 2, 2, 2, 2 }, .full_house, 0); } test "four of a kind" { - const expected: u32 = 24; - const actual = score([_]u3{ 6, 6, 4, 6, 6 }, .four_of_a_kind); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 6, 6, 4, 6, 6 }, .four_of_a_kind, 24); } test "four of a kind alternative order" { - const expected: u32 = 16; - const actual = score([_]u3{ 4, 4, 6, 4, 4 }, .four_of_a_kind); - try testing.expectEqual(expected, actual); -} - -test "yacht can be scored as four of a kind" { - const expected: u32 = 12; - const actual = score([_]u3{ 3, 3, 3, 3, 3 }, .four_of_a_kind); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 4, 4, 6, 4, 4 }, .four_of_a_kind, 16); } test "full house is not four of a kind" { - const expected: u32 = 0; - const actual = score([_]u3{ 3, 3, 3, 5, 5 }, .four_of_a_kind); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 3, 3, 3, 5, 5 }, .four_of_a_kind, 0); } -test "little straight" { - const expected: u32 = 30; - const actual = score([_]u3{ 3, 5, 4, 1, 2 }, .little_straight); - try testing.expectEqual(expected, actual); +test "yacht can be scored as four of a kind" { + try testScore([_]u3{ 3, 3, 3, 3, 3 }, .four_of_a_kind, 12); } -test "little straight as big straight" { - const expected: u32 = 0; - const actual = score([_]u3{ 1, 2, 3, 4, 5 }, .big_straight); - try testing.expectEqual(expected, actual); +test "big straight as little straight" { + try testScore([_]u3{ 6, 5, 4, 3, 2 }, .little_straight, 0); } test "four in order but not a little straight" { - const expected: u32 = 0; - const actual = score([_]u3{ 1, 1, 2, 3, 4 }, .little_straight); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 1, 1, 2, 3, 4 }, .little_straight, 0); } -test "no pairs but not a little straight" { - const expected: u32 = 0; - const actual = score([_]u3{ 1, 2, 3, 4, 6 }, .little_straight); - try testing.expectEqual(expected, actual); +test "little straight" { + try testScore([_]u3{ 3, 5, 4, 1, 2 }, .little_straight, 30); } test "minimum is 1, maximum is 5, but not a little straight" { - const expected: u32 = 0; - const actual = score([_]u3{ 1, 1, 3, 4, 5 }, .little_straight); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 1, 1, 3, 4, 5 }, .little_straight, 0); +} + +test "no pairs but not a little straight" { + try testScore([_]u3{ 1, 2, 3, 4, 6 }, .little_straight, 0); } test "big straight" { - const expected: u32 = 30; - const actual = score([_]u3{ 4, 6, 2, 5, 3 }, .big_straight); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 4, 6, 2, 5, 3 }, .big_straight, 30); } -test "big straight as little straight" { - const expected: u32 = 0; - const actual = score([_]u3{ 6, 5, 4, 3, 2 }, .little_straight); - try testing.expectEqual(expected, actual); +test "little straight as big straight" { + try testScore([_]u3{ 1, 2, 3, 4, 5 }, .big_straight, 0); } test "no pairs but not a big straight" { - const expected: u32 = 0; - const actual = score([_]u3{ 6, 5, 4, 3, 1 }, .big_straight); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 6, 5, 4, 3, 1 }, .big_straight, 0); } test "choice" { - const expected: u32 = 23; - const actual = score([_]u3{ 3, 3, 5, 6, 6 }, .choice); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 3, 3, 5, 6, 6 }, .choice, 23); } test "yacht as choice" { - const expected: u32 = 10; - const actual = score([_]u3{ 2, 2, 2, 2, 2 }, .choice); - try testing.expectEqual(expected, actual); + try testScore([_]u3{ 2, 2, 2, 2, 2 }, .choice, 10); +} + +test "not yacht" { + try testScore([_]u3{ 1, 3, 3, 2, 5 }, .yacht, 0); +} + +test "yacht" { + try testScore([_]u3{ 5, 5, 5, 5, 5 }, .yacht, 50); } diff --git a/generators/exercises/all_your_base.py b/generators/exercises/all_your_base.py new file mode 100644 index 00000000..d544d27a --- /dev/null +++ b/generators/exercises/all_your_base.py @@ -0,0 +1,99 @@ +from lib import zarray + +IMPORT_SELF = True + +# `testConvert`/`testConvertError` reduce repetition across the success and error cases, and +# - because they pass the inputs through runtime parameters - also reject a solution that +# declares `convert`'s value parameters `comptime`. (Since the whole test file compiles as a +# unit, a single such runtime path would already be enough to reject it.) +# See https://forum.exercism.org/t/zig-track-needs-tests-against-comptime-abuse-and-other-kinds-of-cheating/59830/ +HEADER = """const convert = all_your_base.convert; +const ConversionError = all_your_base.ConversionError; + +fn testConvert(digits: []const u32, input_base: u32, output_base: u32, expected: []const u32) !void { + const actual = try convert(testing.allocator, digits, input_base, output_base); + defer testing.allocator.free(actual); + try testing.expectEqualSlices(u32, expected, actual); +} + +fn testConvertError(digits: []const u32, input_base: u32, output_base: u32, expected: ConversionError) !void { + try testing.expectError(expected, convert(testing.allocator, digits, input_base, output_base)); +} +""" + +_ERROR_FOR = { + "input base must be >= 2": "ConversionError.InvalidInputBase", + "output base must be >= 2": "ConversionError.InvalidOutputBase", + "all digits must satisfy 0 <= d < input base": "ConversionError.InvalidDigit", +} + +_SECOND_CALL = "empty list - second call returns different memory" + +# The committed target inserts the track-specific "second call" supplement between +# "15-bit integer" and "empty list"; canonical cases otherwise keep their order. +_ORDER = [ + "single bit one to decimal", + "binary to single decimal", + "single decimal to binary", + "binary to multiple decimal", + "decimal to binary", + "trinary to hexadecimal", + "hexadecimal to trinary", + "15-bit integer", + _SECOND_CALL, + "empty list", + "single zero", + "multiple zeros", + "leading zeros", + "input base is one", + "input base is zero", + "invalid positive digit", + "output base is one", + "output base is zero", +] + + +def order_key(case): + try: + return _ORDER.index(case["description"]) + except ValueError: + return len(_ORDER) + + +def _u32_array(values): + return zarray([str(v) for v in values], "u32") + + +def gen_case(case): + inp = case["input"] + digits = _u32_array(inp["digits"]) + input_base = inp["inputBase"] + output_base = inp["outputBase"] + expected = case["expected"] + + if isinstance(expected, dict) and "error" in expected: + err = _ERROR_FOR[expected["error"]] + return f" try testConvertError(&{digits}, {input_base}, {output_base}, {err});\n" + + expected_arr = _u32_array(expected) + + if case["description"] == _SECOND_CALL: + return ( + " // The `convert` doc comment says that the caller owns the returned memory.\n" + " // `convert` must always return memory that can be freed.\n" + " // Test that `convert` does not return a slice that references a global array.\n" + f" const expected = {expected_arr};\n" + f" const digits = {digits};\n" + f" const input_base = {input_base};\n" + f" const output_base = {output_base};\n" + " const actual = try convert(testing.allocator, &digits, input_base, output_base);\n" + " defer testing.allocator.free(actual);\n" + " try testing.expectEqualSlices(u32, &expected, actual);\n" + " actual[0] = 1; // Modify the output!\n" + " const again = try convert(testing.allocator, &digits, input_base, output_base);\n" + " defer testing.allocator.free(again);\n" + " try testing.expectEqualSlices(u32, &expected, again);\n" + ) + + # Single comptime-defended path (see HEADER) - routes all three inputs through runtime. + return f" try testConvert(&{digits}, {input_base}, {output_base}, &{expected_arr});\n" diff --git a/generators/exercises/armstrong_numbers.py b/generators/exercises/armstrong_numbers.py new file mode 100644 index 00000000..fd6a768c --- /dev/null +++ b/generators/exercises/armstrong_numbers.py @@ -0,0 +1,19 @@ +from lib import zint + +IMPORT_SELF = False + +# The number is passed through `testIsArmstrongNumber`'s runtime parameter, so a solution +# that declares `isArmstrongNumber`'s parameter `comptime` is rejected at compile time. +# See https://forum.exercism.org/t/zig-track-needs-tests-against-comptime-abuse-and-other-kinds-of-cheating/59830/ +HEADER = """const isArmstrongNumber = @import("armstrong_numbers.zig").isArmstrongNumber; + +fn testIsArmstrongNumber(number: u128, expected: bool) !void { + try testing.expectEqual(expected, isArmstrongNumber(number)); +} +""" + + +def gen_case(case): + number = zint(case["input"]["number"]) + expected = "true" if case["expected"] else "false" + return f" try testIsArmstrongNumber({number}, {expected});\n" diff --git a/generators/exercises/yacht.py b/generators/exercises/yacht.py new file mode 100644 index 00000000..d77ed179 --- /dev/null +++ b/generators/exercises/yacht.py @@ -0,0 +1,47 @@ +IMPORT_SELF = False + +# Dice and category are passed through `testScore`'s runtime parameters, so a solution that +# declares `score`'s parameters `comptime` is rejected at compile time. +# See https://forum.exercism.org/t/zig-track-needs-tests-against-comptime-abuse-and-other-kinds-of-cheating/59830/ +HEADER = """const score = @import("yacht.zig").score; +const Category = @import("yacht.zig").Category; + +fn testScore(dice: [5]u3, category: Category, expected: u32) !void { + try testing.expectEqual(expected, score(dice, category)); +} +""" + +# Scoring categories, in the order of the `Category` enum in the solution. +CATEGORIES = [ + "ones", + "twos", + "threes", + "fours", + "fives", + "sixes", + "full_house", + "four_of_a_kind", + "little_straight", + "big_straight", + "choice", + "yacht", +] + + +def _category(case): + return case["input"]["category"].replace(" ", "_") + + +def describe(case, parent): + return case["description"].lower() + + +def order_key(case): + return (CATEGORIES.index(_category(case)), case["description"].lower()) + + +def gen_case(case): + dice = ", ".join(str(d) for d in case["input"]["dice"]) + category = _category(case) + expected = case["expected"] + return f" try testScore([_]u3{{ {dice} }}, .{category}, {expected});\n"