From 7e6f14dd50eca186ccec6905fbae6c03d40bcffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nenad=20Misi=C4=87?= Date: Mon, 1 Jul 2024 10:32:17 +0200 Subject: [PATCH] Add clock exercise (#50) --- concepts/printing/.meta/config.json | 7 + concepts/printing/about.md | 1 + concepts/printing/introduction.md | 1 + concepts/printing/links.json | 1 + config.json | 16 + .../practice/clock/.docs/instructions.md | 7 + exercises/practice/clock/.meta/config.json | 19 ++ exercises/practice/clock/.meta/example.cairo | 51 +++ exercises/practice/clock/.meta/tests.toml | 166 +++++++++ exercises/practice/clock/Scarb.toml | 4 + exercises/practice/clock/src/lib.cairo | 20 ++ exercises/practice/clock/src/tests.cairo | 317 ++++++++++++++++++ 12 files changed, 610 insertions(+) create mode 100644 concepts/printing/.meta/config.json create mode 100644 concepts/printing/about.md create mode 100644 concepts/printing/introduction.md create mode 100644 concepts/printing/links.json create mode 100644 exercises/practice/clock/.docs/instructions.md create mode 100644 exercises/practice/clock/.meta/config.json create mode 100644 exercises/practice/clock/.meta/example.cairo create mode 100644 exercises/practice/clock/.meta/tests.toml create mode 100644 exercises/practice/clock/Scarb.toml create mode 100644 exercises/practice/clock/src/lib.cairo create mode 100644 exercises/practice/clock/src/tests.cairo diff --git a/concepts/printing/.meta/config.json b/concepts/printing/.meta/config.json new file mode 100644 index 00000000..52914445 --- /dev/null +++ b/concepts/printing/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "", + "authors": [ + "misicnenad" + ], + "contributors": [] +} diff --git a/concepts/printing/about.md b/concepts/printing/about.md new file mode 100644 index 00000000..bf63b082 --- /dev/null +++ b/concepts/printing/about.md @@ -0,0 +1 @@ +# Printing diff --git a/concepts/printing/introduction.md b/concepts/printing/introduction.md new file mode 100644 index 00000000..e10b99d0 --- /dev/null +++ b/concepts/printing/introduction.md @@ -0,0 +1 @@ +# Introduction diff --git a/concepts/printing/links.json b/concepts/printing/links.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/concepts/printing/links.json @@ -0,0 +1 @@ +[] diff --git a/config.json b/config.json index 757e7942..324306ce 100644 --- a/config.json +++ b/config.json @@ -171,6 +171,17 @@ ], "prerequisites": [], "difficulty": 4 + }, + { + "slug": "clock", + "name": "Clock", + "uuid": "b44728f6-f2ad-4fa4-a20e-5144f9a81e3e", + "practices": [ + "traits", + "printing" + ], + "prerequisites": [], + "difficulty": 3 } ], "foregone": [ @@ -257,6 +268,11 @@ "uuid": "cf49c14b-ed9a-4df6-a43a-3923e8489900", "slug": "error-handling", "name": "Error Handling" + }, + { + "uuid": "fe81e75b-b4d2-4ee8-9aaa-271e76468d0b", + "slug": "printing", + "name": "Printing" } ], "key_features": [ diff --git a/exercises/practice/clock/.docs/instructions.md b/exercises/practice/clock/.docs/instructions.md new file mode 100644 index 00000000..a1efc789 --- /dev/null +++ b/exercises/practice/clock/.docs/instructions.md @@ -0,0 +1,7 @@ +# Instructions + +Implement a clock that handles times without dates. + +You should be able to add and subtract minutes to it. + +Two clocks that represent the same time should be equal to each other. diff --git a/exercises/practice/clock/.meta/config.json b/exercises/practice/clock/.meta/config.json new file mode 100644 index 00000000..9a6501b9 --- /dev/null +++ b/exercises/practice/clock/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "misicnenad" + ], + "files": { + "solution": [ + "src/lib.cairo", + "Scarb.toml" + ], + "test": [ + "src/tests.cairo" + ], + "example": [ + ".meta/example.cairo" + ] + }, + "blurb": "Implement a clock that handles times without dates.", + "source": "Pairing session with Erin Drummond" +} diff --git a/exercises/practice/clock/.meta/example.cairo b/exercises/practice/clock/.meta/example.cairo new file mode 100644 index 00000000..f1939190 --- /dev/null +++ b/exercises/practice/clock/.meta/example.cairo @@ -0,0 +1,51 @@ +use core::fmt::{Display, Formatter, Error}; + +#[derive(Drop, PartialEq, Debug)] +pub struct Clock { + minutes: u32, +} + +impl ClockDisplay of Display { + fn fmt(self: @Clock, ref f: Formatter) -> Result<(), Error> { + let hours = double_digit(*self.minutes / 60); + let mins = double_digit(*self.minutes % 60); + write!(f, "{hours}:{mins}") + } +} + +fn double_digit(n: u32) -> ByteArray { + if n < 10 { + format!("0{n}") + } else { + format!("{n}") + } +} + +#[generate_trait] +impl ClockImpl of ClockTrait { + fn new(hour: i32, minute: i32) -> Clock { + ClockImpl::build(hour * 60 + minute) + } + + fn build(minutes: i32) -> Clock { + let mut mins = minutes; + while mins < 0 { + mins += 1440; + }; + let mins: u32 = mins.try_into().unwrap(); + Clock { minutes: mins % 1440, } + } + + fn add_minutes(ref self: Clock, minutes: i32) -> Clock { + ClockImpl::build(self.minutes.try_into().unwrap() + minutes) + } + + fn to_string(self: @Clock) -> ByteArray { + let mut formatter: Formatter = Default::default(); + Display::fmt(self, ref formatter).unwrap(); + formatter.buffer + } +} + +#[cfg(test)] +mod tests; diff --git a/exercises/practice/clock/.meta/tests.toml b/exercises/practice/clock/.meta/tests.toml new file mode 100644 index 00000000..712c87bc --- /dev/null +++ b/exercises/practice/clock/.meta/tests.toml @@ -0,0 +1,166 @@ +# 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. + +[a577bacc-106b-496e-9792-b3083ea8705e] +description = "Create a new clock with an initial time -> on the hour" + +[b5d0c360-3b88-489b-8e84-68a1c7a4fa23] +description = "Create a new clock with an initial time -> past the hour" + +[473223f4-65f3-46ff-a9f7-7663c7e59440] +description = "Create a new clock with an initial time -> midnight is zero hours" + +[ca95d24a-5924-447d-9a96-b91c8334725c] +description = "Create a new clock with an initial time -> hour rolls over" + +[f3826de0-0925-4d69-8ac8-89aea7e52b78] +description = "Create a new clock with an initial time -> hour rolls over continuously" + +[a02f7edf-dfd4-4b11-b21a-86de3cc6a95c] +description = "Create a new clock with an initial time -> sixty minutes is next hour" + +[8f520df6-b816-444d-b90f-8a477789beb5] +description = "Create a new clock with an initial time -> minutes roll over" + +[c75c091b-47ac-4655-8d40-643767fc4eed] +description = "Create a new clock with an initial time -> minutes roll over continuously" + +[06343ecb-cf39-419d-a3f5-dcbae0cc4c57] +description = "Create a new clock with an initial time -> hour and minutes roll over" + +[be60810e-f5d9-4b58-9351-a9d1e90e660c] +description = "Create a new clock with an initial time -> hour and minutes roll over continuously" + +[1689107b-0b5c-4bea-aad3-65ec9859368a] +description = "Create a new clock with an initial time -> hour and minutes roll over to exactly midnight" + +[d3088ee8-91b7-4446-9e9d-5e2ad6219d91] +description = "Create a new clock with an initial time -> negative hour" + +[77ef6921-f120-4d29-bade-80d54aa43b54] +description = "Create a new clock with an initial time -> negative hour rolls over" + +[359294b5-972f-4546-bb9a-a85559065234] +description = "Create a new clock with an initial time -> negative hour rolls over continuously" + +[509db8b7-ac19-47cc-bd3a-a9d2f30b03c0] +description = "Create a new clock with an initial time -> negative minutes" + +[5d6bb225-130f-4084-84fd-9e0df8996f2a] +description = "Create a new clock with an initial time -> negative minutes roll over" + +[d483ceef-b520-4f0c-b94a-8d2d58cf0484] +description = "Create a new clock with an initial time -> negative minutes roll over continuously" + +[1cd19447-19c6-44bf-9d04-9f8305ccb9ea] +description = "Create a new clock with an initial time -> negative sixty minutes is previous hour" + +[9d3053aa-4f47-4afc-bd45-d67a72cef4dc] +description = "Create a new clock with an initial time -> negative hour and minutes both roll over" + +[51d41fcf-491e-4ca0-9cae-2aa4f0163ad4] +description = "Create a new clock with an initial time -> negative hour and minutes both roll over continuously" + +[d098e723-ad29-4ef9-997a-2693c4c9d89a] +description = "Add minutes -> add minutes" + +[b6ec8f38-e53e-4b22-92a7-60dab1f485f4] +description = "Add minutes -> add no minutes" + +[efd349dd-0785-453e-9ff8-d7452a8e7269] +description = "Add minutes -> add to next hour" + +[749890f7-aba9-4702-acce-87becf4ef9fe] +description = "Add minutes -> add more than one hour" + +[da63e4c1-1584-46e3-8d18-c9dc802c1713] +description = "Add minutes -> add more than two hours with carry" + +[be167a32-3d33-4cec-a8bc-accd47ddbb71] +description = "Add minutes -> add across midnight" + +[6672541e-cdae-46e4-8be7-a820cc3be2a8] +description = "Add minutes -> add more than one day (1500 min = 25 hrs)" + +[1918050d-c79b-4cb7-b707-b607e2745c7e] +description = "Add minutes -> add more than two days" + +[37336cac-5ede-43a5-9026-d426cbe40354] +description = "Subtract minutes -> subtract minutes" + +[0aafa4d0-3b5f-4b12-b3af-e3a9e09c047b] +description = "Subtract minutes -> subtract to previous hour" + +[9b4e809c-612f-4b15-aae0-1df0acb801b9] +description = "Subtract minutes -> subtract more than an hour" + +[8b04bb6a-3d33-4e6c-8de9-f5de6d2c70d6] +description = "Subtract minutes -> subtract across midnight" + +[07c3bbf7-ce4d-4658-86e8-4a77b7a5ccd9] +description = "Subtract minutes -> subtract more than two hours" + +[90ac8a1b-761c-4342-9c9c-cdc3ed5db097] +description = "Subtract minutes -> subtract more than two hours with borrow" + +[2149f985-7136-44ad-9b29-ec023a97a2b7] +description = "Subtract minutes -> subtract more than one day (1500 min = 25 hrs)" + +[ba11dbf0-ac27-4acb-ada9-3b853ec08c97] +description = "Subtract minutes -> subtract more than two days" + +[f2fdad51-499f-4c9b-a791-b28c9282e311] +description = "Compare two clocks for equality -> clocks with same time" + +[5d409d4b-f862-4960-901e-ec430160b768] +description = "Compare two clocks for equality -> clocks a minute apart" + +[a6045fcf-2b52-4a47-8bb2-ef10a064cba5] +description = "Compare two clocks for equality -> clocks an hour apart" + +[66b12758-0be5-448b-a13c-6a44bce83527] +description = "Compare two clocks for equality -> clocks with hour overflow" + +[2b19960c-212e-4a71-9aac-c581592f8111] +description = "Compare two clocks for equality -> clocks with hour overflow by several days" + +[6f8c6541-afac-4a92-b0c2-b10d4e50269f] +description = "Compare two clocks for equality -> clocks with negative hour" + +[bb9d5a68-e324-4bf5-a75e-0e9b1f97a90d] +description = "Compare two clocks for equality -> clocks with negative hour that wraps" + +[56c0326d-565b-4d19-a26f-63b3205778b7] +description = "Compare two clocks for equality -> clocks with negative hour that wraps multiple times" + +[c90b9de8-ddff-4ffe-9858-da44a40fdbc2] +description = "Compare two clocks for equality -> clocks with minute overflow" + +[533a3dc5-59a7-491b-b728-a7a34fe325de] +description = "Compare two clocks for equality -> clocks with minute overflow by several days" + +[fff49e15-f7b7-4692-a204-0f6052d62636] +description = "Compare two clocks for equality -> clocks with negative minute" + +[605c65bb-21bd-43eb-8f04-878edf508366] +description = "Compare two clocks for equality -> clocks with negative minute that wraps" + +[b87e64ed-212a-4335-91fd-56da8421d077] +description = "Compare two clocks for equality -> clocks with negative minute that wraps multiple times" + +[822fbf26-1f3b-4b13-b9bf-c914816b53dd] +description = "Compare two clocks for equality -> clocks with negative hours and minutes" + +[e787bccd-cf58-4a1d-841c-ff80eaaccfaa] +description = "Compare two clocks for equality -> clocks with negative hours and minutes that wrap" + +[96969ca8-875a-48a1-86ae-257a528c44f5] +description = "Compare two clocks for equality -> full clock and zeroed clock" diff --git a/exercises/practice/clock/Scarb.toml b/exercises/practice/clock/Scarb.toml new file mode 100644 index 00000000..fdc2113e --- /dev/null +++ b/exercises/practice/clock/Scarb.toml @@ -0,0 +1,4 @@ +[package] +name = "clock" +version = "0.1.0" +edition = "2023_11" diff --git a/exercises/practice/clock/src/lib.cairo b/exercises/practice/clock/src/lib.cairo new file mode 100644 index 00000000..de1eb045 --- /dev/null +++ b/exercises/practice/clock/src/lib.cairo @@ -0,0 +1,20 @@ +#[derive(Drop, PartialEq, Debug)] +pub struct Clock {} + +#[generate_trait] +impl ClockImpl of ClockTrait { + fn new(hour: i32, minute: i32) -> Clock { + panic!("implement `new`") + } + + fn add_minutes(ref self: Clock, minutes: i32) -> Clock { + panic!("implement `add_minutes`") + } + + fn to_string(self: @Clock) -> ByteArray { + panic!("implement `to_string`") + } +} + +#[cfg(test)] +mod tests; diff --git a/exercises/practice/clock/src/tests.cairo b/exercises/practice/clock/src/tests.cairo new file mode 100644 index 00000000..df2bcd34 --- /dev/null +++ b/exercises/practice/clock/src/tests.cairo @@ -0,0 +1,317 @@ +mod create_a_new_clock_with_an_initial_time { + use clock::{ClockTrait}; + + #[test] + fn on_the_hour() { + let t: ByteArray = "08:00"; + assert_eq!(ClockTrait::new(8, 0).to_string(), t); + } + + #[test] + fn past_the_hour() { + assert_eq!(ClockTrait::new(11, 9).to_string(), "11:09"); + } + + #[test] + fn midnight_is_zero_hours() { + assert_eq!(ClockTrait::new(24, 0).to_string(), "00:00"); + } + + #[test] + fn hour_rolls_over() { + assert_eq!(ClockTrait::new(25, 0).to_string(), "01:00"); + } + + #[test] + fn hour_rolls_over_continuously() { + assert_eq!(ClockTrait::new(100, 0).to_string(), "04:00"); + } + + #[test] + fn sixty_minutes_is_next_hour() { + assert_eq!(ClockTrait::new(1, 60).to_string(), "02:00"); + } + + #[test] + fn minutes_roll_over() { + assert_eq!(ClockTrait::new(0, 160).to_string(), "02:40"); + } + + #[test] + fn minutes_roll_over_continuously() { + assert_eq!(ClockTrait::new(0, 1723).to_string(), "04:43"); + } + + #[test] + fn hours_and_minutes_roll_over() { + assert_eq!(ClockTrait::new(25, 160).to_string(), "03:40"); + } + + #[test] + fn hours_and_minutes_roll_over_continuously() { + assert_eq!(ClockTrait::new(201, 3001).to_string(), "11:01"); + } + + #[test] + fn hours_and_minutes_roll_over_to_exactly_midnight() { + assert_eq!(ClockTrait::new(72, 8640).to_string(), "00:00"); + } + + #[test] + fn negative_hour() { + assert_eq!(ClockTrait::new(-1, 15).to_string(), "23:15"); + } + + #[test] + fn negative_hour_rolls_over() { + assert_eq!(ClockTrait::new(-25, 00).to_string(), "23:00"); + } + + #[test] + fn negative_hour_rolls_over_continuously() { + assert_eq!(ClockTrait::new(-91, 00).to_string(), "05:00"); + } + + #[test] + fn negative_minutes() { + assert_eq!(ClockTrait::new(1, -40).to_string(), "00:20"); + } + + #[test] + fn negative_minutes_roll_over() { + assert_eq!(ClockTrait::new(1, -160).to_string(), "22:20"); + } + + #[test] + fn negative_minutes_roll_over_continuously() { + assert_eq!(ClockTrait::new(1, -4820).to_string(), "16:40"); + } + + #[test] + fn negative_sixty_minutes_is_previous_hour() { + assert_eq!(ClockTrait::new(2, -60).to_string(), "01:00"); + } + + #[test] + fn negative_one_twenty_minutes_is_two_prev_hours() { + assert_eq!(ClockTrait::new(1, -120).to_string(), "23:00"); + } + + #[test] + fn negative_hour_and_minutes_both_roll_over() { + assert_eq!(ClockTrait::new(-25, -160).to_string(), "20:20"); + } + + #[test] + fn negative_hour_and_minutes_both_roll_over_continuously() { + assert_eq!(ClockTrait::new(-121, -5810).to_string(), "22:10"); + } + + #[test] + fn zero_hour_and_negative_minutes() { + assert_eq!(ClockTrait::new(0, -22).to_string(), "23:38"); + } +} + +mod clock_math { + use clock::{ClockTrait}; + + #[test] + fn add_minutes() { + let mut clock = ClockTrait::new(10, 0); + clock = clock.add_minutes(3); + assert_eq!(clock.to_string(), "10:03"); + } + + #[test] + fn add_no_minutes() { + let mut clock = ClockTrait::new(6, 41); + clock = clock.add_minutes(0); + assert_eq!(clock.to_string(), "06:41"); + } + + #[test] + fn add_to_next_hour() { + let mut clock = ClockTrait::new(0, 45); + clock = clock.add_minutes(40); + assert_eq!(clock.to_string(), "01:25"); + } + + #[test] + fn add_more_than_one_hour() { + let mut clock = ClockTrait::new(10, 0); + clock = clock.add_minutes(61); + assert_eq!(clock.to_string(), "11:01"); + } + + #[test] + fn add_more_than_two_hours_with_carry() { + let mut clock = ClockTrait::new(0, 45); + clock = clock.add_minutes(160); + assert_eq!(clock.to_string(), "03:25"); + } + + #[test] + fn add_across_midnight() { + let mut clock = ClockTrait::new(23, 59); + clock = clock.add_minutes(2); + assert_eq!(clock.to_string(), "00:01"); + } + + #[test] + fn add_more_than_one_day() { + let mut clock = ClockTrait::new(5, 32); + clock = clock.add_minutes(1500); + assert_eq!(clock.to_string(), "06:32"); + } + + #[test] + fn add_more_than_two_days() { + let mut clock = ClockTrait::new(1, 1); + clock = clock.add_minutes(3500); + assert_eq!(clock.to_string(), "11:21"); + } + + #[test] + fn subtract_minutes() { + let mut clock = ClockTrait::new(10, 3); + clock = clock.add_minutes(-3); + assert_eq!(clock.to_string(), "10:00"); + } + + #[test] + fn subtract_to_previous_hour() { + let mut clock = ClockTrait::new(10, 3); + clock = clock.add_minutes(-30); + assert_eq!(clock.to_string(), "09:33"); + } + + #[test] + fn subtract_more_than_an_hour() { + let mut clock = ClockTrait::new(10, 3); + clock = clock.add_minutes(-70); + assert_eq!(clock.to_string(), "08:53"); + } + + #[test] + fn subtract_across_midnight() { + let mut clock = ClockTrait::new(0, 3); + clock = clock.add_minutes(-4); + assert_eq!(clock.to_string(), "23:59"); + } + + #[test] + fn subtract_more_than_two_hours() { + let mut clock = ClockTrait::new(0, 0); + clock = clock.add_minutes(-160); + assert_eq!(clock.to_string(), "21:20"); + } + + #[test] + fn subtract_more_than_two_hours_with_borrow() { + let mut clock = ClockTrait::new(6, 15); + clock = clock.add_minutes(-160); + assert_eq!(clock.to_string(), "03:35"); + } + + #[test] + fn subtract_more_than_one_day() { + let mut clock = ClockTrait::new(5, 32); + clock = clock.add_minutes(-1500); + assert_eq!(clock.to_string(), "04:32"); + } + + #[test] + fn subtract_more_than_two_days() { + let mut clock = ClockTrait::new(2, 20); + clock = clock.add_minutes(-3000); + assert_eq!(clock.to_string(), "00:20"); + } +} + +// +// Test Equality +// +mod compare_two_clocks_for_equality { + use clock::{ClockTrait}; + + #[test] + fn clocks_with_same_time() { + assert_eq!(ClockTrait::new(15, 37), ClockTrait::new(15, 37)); + } + + #[test] + fn clocks_a_minute_apart() { + assert_ne!(ClockTrait::new(15, 36), ClockTrait::new(15, 37)); + } + + #[test] + fn clocks_an_hour_apart() { + assert_ne!(ClockTrait::new(14, 37), ClockTrait::new(15, 37)); + } + + #[test] + fn clocks_with_hour_overflow() { + assert_eq!(ClockTrait::new(10, 37), ClockTrait::new(34, 37)); + } + + #[test] + fn clocks_with_hour_overflow_by_several_days() { + assert_eq!(ClockTrait::new(99, 11), ClockTrait::new(3, 11)); + } + + #[test] + fn clocks_with_negative_hour() { + assert_eq!(ClockTrait::new(-2, 40), ClockTrait::new(22, 40)); + } + + #[test] + fn clocks_with_negative_hour_that_wraps() { + assert_eq!(ClockTrait::new(-31, 3), ClockTrait::new(17, 3)); + } + + #[test] + fn clocks_with_negative_hour_that_wraps_multiple_times() { + assert_eq!(ClockTrait::new(-83, 49), ClockTrait::new(13, 49)); + } + + #[test] + fn clocks_with_minute_overflow() { + assert_eq!(ClockTrait::new(0, 1441), ClockTrait::new(0, 1)); + } + + #[test] + fn clocks_with_minute_overflow_by_several_days() { + assert_eq!(ClockTrait::new(2, 4322), ClockTrait::new(2, 2)); + } + + #[test] + fn clocks_with_negative_minute() { + assert_eq!(ClockTrait::new(3, -20), ClockTrait::new(2, 40)); + } + + #[test] + fn clocks_with_negative_minute_that_wraps() { + assert_eq!(ClockTrait::new(5, -1490), ClockTrait::new(4, 10)); + } + + #[test] + fn clocks_with_negative_minute_that_wraps_multiple_times() { + assert_eq!(ClockTrait::new(6, -4305), ClockTrait::new(6, 15)); + } + + #[test] + fn clocks_with_negative_hours_and_minutes() { + assert_eq!(ClockTrait::new(-12, -268), ClockTrait::new(7, 32)); + } + + #[test] + fn clocks_with_negative_hours_and_minutes_that_wrap() { + assert_eq!(ClockTrait::new(-54, -11_513), ClockTrait::new(18, 7)); + } + + #[test] + fn full_clock_and_zeroed_clock() { + assert_eq!(ClockTrait::new(24, 0), ClockTrait::new(0, 0)); + } +}