diff --git a/config.json b/config.json index 403f64a8..d057dd37 100644 --- a/config.json +++ b/config.json @@ -35,6 +35,25 @@ "exercises": { "practice": [ { + "uuid": "fafe82fb-a54a-40d3-b082-4429e080e435", + "slug": "high-scores", + "name": "High scores", + "practices": [ + "integers", + "slices", + "sorting", + "structs" + ], + "prerequisites": [ + "integers", + "slices", + "sorting", + "structs" + ], + "difficulty": 1 + }, + { + "uuid": "25a0331e-612f-48c1-8a3f-9b0e9601a2fa", "slug": "hello-world", "name": "Hello, World!", "uuid": "25a0331e-612f-48c1-8a3f-9b0e9601a2fa", diff --git a/exercises/practice/high-scores/.docs/instructions.md b/exercises/practice/high-scores/.docs/instructions.md new file mode 100644 index 00000000..55802488 --- /dev/null +++ b/exercises/practice/high-scores/.docs/instructions.md @@ -0,0 +1,6 @@ +# Instructions + +Manage a game player's High Score list. + +Your task is to build a high-score component of the classic Frogger game, one of the highest selling and most addictive games of all time, and a classic of the arcade era. +Your task is to write methods that return the highest score from the list, the last added score and the three highest scores. diff --git a/exercises/practice/high-scores/.meta/config.json b/exercises/practice/high-scores/.meta/config.json new file mode 100644 index 00000000..3f7469ba --- /dev/null +++ b/exercises/practice/high-scores/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "ee7" + ], + "files": { + "solution": [ + "high_scores.zig" + ], + "test": [ + "test_high_scores.zig" + ], + "example": [ + ".meta/example.zig" + ] + }, + "blurb": "Manage a player's High Score list.", + "source": "Tribute to the eighties' arcade game Frogger" +} diff --git a/exercises/practice/high-scores/.meta/example.zig b/exercises/practice/high-scores/.meta/example.zig new file mode 100644 index 00000000..a9e91313 --- /dev/null +++ b/exercises/practice/high-scores/.meta/example.zig @@ -0,0 +1,28 @@ +const std = @import("std"); + +pub const HighScores = struct { + scores: []const u32, + + /// Asserts `scores.len > 0`. + pub fn init(scores: []const u32) HighScores { + std.debug.assert(scores.len > 0); + return .{ .scores = scores }; + } + + pub fn latest(self: HighScores) u32 { + return self.scores[self.scores.len - 1]; + } + + pub fn personalBest(self: HighScores) u32 { + return std.mem.max(u32, self.scores); + } + + /// Writes (at most) the three highest scores from `self` into `buffer`. + /// Asserts `buffer.len == self.scores.len`. + pub fn personalTopThree(self: HighScores, buffer: []u32) []u32 { + std.debug.assert(buffer.len == self.scores.len); + std.mem.copy(u32, buffer, self.scores); + std.mem.sort(u32, buffer, {}, std.sort.desc(u32)); + return buffer[0..@min(buffer.len, 3)]; + } +}; diff --git a/exercises/practice/high-scores/.meta/tests.toml b/exercises/practice/high-scores/.meta/tests.toml new file mode 100644 index 00000000..7c946338 --- /dev/null +++ b/exercises/practice/high-scores/.meta/tests.toml @@ -0,0 +1,46 @@ +# 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. + +[1035eb93-2208-4c22-bab8-fef06769a73c] +description = "List of scores" + +[6aa5dbf5-78fa-4375-b22c-ffaa989732d2] +description = "Latest score" + +[b661a2e1-aebf-4f50-9139-0fb817dd12c6] +description = "Personal best" + +[3d996a97-c81c-4642-9afc-80b80dc14015] +description = "Top 3 scores -> Personal top three from a list of scores" + +[1084ecb5-3eb4-46fe-a816-e40331a4e83a] +description = "Top 3 scores -> Personal top highest to lowest" + +[e6465b6b-5a11-4936-bfe3-35241c4f4f16] +description = "Top 3 scores -> Personal top when there is a tie" + +[f73b02af-c8fd-41c9-91b9-c86eaa86bce2] +description = "Top 3 scores -> Personal top when there are less than 3" + +[16608eae-f60f-4a88-800e-aabce5df2865] +description = "Top 3 scores -> Personal top when there is only one" + +[2df075f9-fec9-4756-8f40-98c52a11504f] +description = "Top 3 scores -> Latest score after personal top scores" + +[809c4058-7eb1-4206-b01e-79238b9b71bc] +description = "Top 3 scores -> Scores after personal top scores" + +[ddb0efc0-9a86-4f82-bc30-21ae0bdc6418] +description = "Top 3 scores -> Latest score after personal best" + +[6a0fd2d1-4cc4-46b9-a5bb-2fb667ca2364] +description = "Top 3 scores -> Scores after personal best" diff --git a/exercises/practice/high-scores/high_scores.zig b/exercises/practice/high-scores/high_scores.zig new file mode 100644 index 00000000..ddbdb718 --- /dev/null +++ b/exercises/practice/high-scores/high_scores.zig @@ -0,0 +1,27 @@ +pub const HighScores = struct { + // Please implement the field(s) of this struct. + foo: void, + + /// Asserts `scores.len > 0`. + pub fn init(scores: []const u32) HighScores { + _ = scores; + @compileError("please implement the init method"); + } + + pub fn latest(self: HighScores) u32 { + _ = self; + @compileError("please implement the latest method"); + } + + pub fn personalBest(self: HighScores) u32 { + _ = self; + @compileError("please implement the personalBest method"); + } + + /// Writes (at most) the three highest scores from `self` into `buffer`. + pub fn personalTopThree(self: HighScores, buffer: []u32) []u32 { + _ = self; + _ = buffer; + @compileError("please implement the personalTopThree method"); + } +}; diff --git a/exercises/practice/high-scores/test_high_scores.zig b/exercises/practice/high-scores/test_high_scores.zig new file mode 100644 index 00000000..65d75b17 --- /dev/null +++ b/exercises/practice/high-scores/test_high_scores.zig @@ -0,0 +1,112 @@ +const std = @import("std"); +const testing = std.testing; + +const high_scores = @import("high_scores.zig"); +const HighScores = high_scores.HighScores; + +test "list of scores" { + const expected = [_]u32{ 30, 50, 20, 70 }; + const scores = [_]u32{ 30, 50, 20, 70 }; + const h = HighScores.init(&scores); + const actual = h.scores; + try testing.expectEqualSlices(u32, &expected, actual); +} + +test "latest score" { + const expected: u32 = 30; + const scores = [_]u32{ 100, 0, 90, 30 }; + const h = HighScores.init(&scores); + const actual = h.latest(); + try testing.expectEqual(expected, actual); +} + +test "personal best" { + const expected: u32 = 100; + const scores = [_]u32{ 40, 100, 70 }; + const h = HighScores.init(&scores); + const actual = h.personalBest(); + try testing.expectEqual(expected, actual); +} + +test "personal top three from a list of scores" { + const expected = [_]u32{ 100, 90, 70 }; + const scores = [_]u32{ 10, 30, 90, 30, 100, 20, 10, 0, 30, 40, 40, 70, 70 }; + const h = HighScores.init(&scores); + var buffer: [scores.len]u32 = undefined; + const actual = h.personalTopThree(&buffer); + try testing.expectEqualSlices(u32, &expected, actual); +} + +test "personal top highest to lowest" { + const expected = [_]u32{ 30, 20, 10 }; + const scores = [_]u32{ 20, 10, 30 }; + const h = HighScores.init(&scores); + var buffer: [scores.len]u32 = undefined; + const actual = h.personalTopThree(&buffer); + try testing.expectEqualSlices(u32, &expected, actual); +} + +test "personal top when there is a tie" { + const expected = [_]u32{ 40, 40, 30 }; + const scores = [_]u32{ 40, 20, 40, 30 }; + const h = HighScores.init(&scores); + var buffer: [scores.len]u32 = undefined; + const actual = h.personalTopThree(&buffer); + try testing.expectEqualSlices(u32, &expected, actual); +} + +test "personal top when there are less than 3" { + const expected = [_]u32{ 70, 30 }; + const scores = [_]u32{ 30, 70 }; + const h = HighScores.init(&scores); + var buffer: [scores.len]u32 = undefined; + const actual = h.personalTopThree(&buffer); + try testing.expectEqualSlices(u32, &expected, actual); +} + +test "personal top when there is only one" { + const expected = [_]u32{40}; + const scores = [_]u32{40}; + const h = HighScores.init(&scores); + var buffer: [scores.len]u32 = undefined; + const actual = h.personalTopThree(&buffer); + try testing.expectEqualSlices(u32, &expected, actual); +} + +test "latest score after personal top scores" { + const expected: u32 = 30; + const scores = [_]u32{ 70, 50, 20, 30 }; + const h = HighScores.init(&scores); + var buffer: [scores.len]u32 = undefined; + _ = h.personalTopThree(&buffer); + const actual = h.latest(); + try testing.expectEqual(expected, actual); +} + +test "scores after personal top scores" { + const expected = [_]u32{ 30, 50, 20, 70 }; + const scores = [_]u32{ 30, 50, 20, 70 }; + const h = HighScores.init(&scores); + var buffer: [scores.len]u32 = undefined; + _ = h.personalTopThree(&buffer); + const actual = h.scores; + try testing.expectEqualSlices(u32, &expected, actual); +} + +test "latest score after personal best" { + const expected: u32 = 30; + const scores = [_]u32{ 20, 70, 15, 25, 30 }; + const h = HighScores.init(&scores); + _ = h.personalBest(); + const actual = h.latest(); + try testing.expectEqual(expected, actual); +} + +test "scores after personal best" { + const expected = [_]u32{ 20, 70, 15, 25, 30 }; + const scores = [_]u32{ 20, 70, 15, 25, 30 }; + const h = HighScores.init(&scores); + _ = h.personalBest(); + const actual = h.scores; + try testing.expectEqualSlices(u32, &expected, actual); +}