From 13b026046e890026ab1d08b89c4c7e21d34ae92c Mon Sep 17 00:00:00 2001 From: Nenad Date: Sat, 9 Aug 2025 14:56:44 +0200 Subject: [PATCH 1/7] add concept exercise: bakery-order-system --- config.json | 13 ++- .../bakery-order-system/.docs/hints.md | 28 +++++++ .../bakery-order-system/.docs/instructions.md | 42 ++++++++++ .../bakery-order-system/.docs/introduction.md | 66 +++++++++++++++ .../bakery-order-system/.meta/config.json | 20 +++++ .../bakery-order-system/.meta/design.md | 19 +++++ .../bakery-order-system/.meta/exemplar.cairo | 50 ++++++++++++ .../concept/bakery-order-system/Scarb.toml | 7 ++ .../concept/bakery-order-system/src/lib.cairo | 30 +++++++ .../tests/bakery_order_system.cairo | 81 +++++++++++++++++++ 10 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 exercises/concept/bakery-order-system/.docs/hints.md create mode 100644 exercises/concept/bakery-order-system/.docs/instructions.md create mode 100644 exercises/concept/bakery-order-system/.docs/introduction.md create mode 100644 exercises/concept/bakery-order-system/.meta/config.json create mode 100644 exercises/concept/bakery-order-system/.meta/design.md create mode 100644 exercises/concept/bakery-order-system/.meta/exemplar.cairo create mode 100644 exercises/concept/bakery-order-system/Scarb.toml create mode 100644 exercises/concept/bakery-order-system/src/lib.cairo create mode 100644 exercises/concept/bakery-order-system/tests/bakery_order_system.cairo diff --git a/config.json b/config.json index e9cd57ec..3168a713 100644 --- a/config.json +++ b/config.json @@ -92,7 +92,6 @@ "name": "RPN Calculator", "uuid": "536d9f09-5910-4a26-93fd-2242667b0b87", "concepts": [ - "control-flow", "enums" ], "prerequisites": [ @@ -100,6 +99,18 @@ ], "status": "wip" }, + { + "slug": "bakery-order-system", + "name": "Bakery Order System", + "uuid": "536d9f09-5910-4a26-93fd-2242667b0b87", + "concepts": [ + "control-flow" + ], + "prerequisites": [ + "match-basics" + ], + "status": "wip" + }, { "slug": "welcome-to-tech-palace", "name": "Welcome To Tech Palace!", diff --git a/exercises/concept/bakery-order-system/.docs/hints.md b/exercises/concept/bakery-order-system/.docs/hints.md new file mode 100644 index 00000000..b9defecc --- /dev/null +++ b/exercises/concept/bakery-order-system/.docs/hints.md @@ -0,0 +1,28 @@ +# Hints + +## General + +- [The Cairo Book - Control Flow][tcb-control-flow] +- [The Cairo Book - Arrays][tcb-arrays] + +## 1. Calculate Order Total + +- Use `match` or `if`/`else if` to handle different pastry types +- Multiply the price per pastry by the quantity +- Each pastry type has a fixed price in coins + +## 2. Apply Volume Discounts + +- Use `if`/`else if` to check discount tiers from highest to lowest +- Remember that discounts should be applied as integer division (rounded down) +- 10% discount means multiply by 90 and divide by 100 + +## 3. Create Baking Schedule + +- Use a `loop` to build the schedule hour by hour +- Each hour, bake up to 5 orders (or whatever remains) +- Continue until all orders are scheduled +- Use `break` when no more orders remain + +[tcb-control-flow]: https://www.starknet.io/cairo-book/ch02-05-control-flow.html +[tcb-arrays]: https://www.starknet.io/cairo-book/ch03-01-arrays.html diff --git a/exercises/concept/bakery-order-system/.docs/instructions.md b/exercises/concept/bakery-order-system/.docs/instructions.md new file mode 100644 index 00000000..6907af0c --- /dev/null +++ b/exercises/concept/bakery-order-system/.docs/instructions.md @@ -0,0 +1,42 @@ +# Instructions + +In this exercise you'll help manage a small bakery's daily operations using Cairo's control flow constructs. + +## 1. Calculate Order Total + +Implement `calculate_total` that calculates the cost of an order based on pastry type and quantity: + +- Croissant: 3 coins each +- Muffin: 2 coins each +- Cookie: 1 coin each + +```rust +calculate_total(Pastry::Croissant, 4) // Returns: 12 (4 * 3) +calculate_total(Pastry::Cookie, 10) // Returns: 10 (10 * 1) +``` + +## 2. Apply Volume Discounts + +Implement `apply_discount` that applies discounts based on order total: + +- Orders ≥ 20 coins: 10% discount +- Orders ≥ 10 coins: 5% discount +- Orders < 10 coins: no discount + +```rust +apply_discount(25) // Returns: 22 (25 - 10% = 22.5, rounded down) +apply_discount(15) // Returns: 14 (15 - 5% = 14.25, rounded down) +apply_discount(8) // Returns: 8 (no discount) +``` + +## 3. Create Baking Schedule + +Implement `baking_schedule` that creates a daily baking plan. The bakery bakes in batches of 5 orders per hour: + +```rust +baking_schedule(18) // Returns: [5, 5, 5, 3] (3 full batches + 3 remaining) +baking_schedule(10) // Returns: [5, 5] (2 full batches) +baking_schedule(3) // Returns: [3] (1 partial batch) +``` + +Use appropriate control flow constructs (`if`/`else`, `loop`) to solve each problem efficiently. diff --git a/exercises/concept/bakery-order-system/.docs/introduction.md b/exercises/concept/bakery-order-system/.docs/introduction.md new file mode 100644 index 00000000..81eb4091 --- /dev/null +++ b/exercises/concept/bakery-order-system/.docs/introduction.md @@ -0,0 +1,66 @@ +# Introduction + +Control flow in Cairo allows you to control the execution path of your programs based on conditions and repetition. + +## Conditional Execution + +Use `if` expressions to execute code based on conditions: + +```rust +let number = 42; +if number > 0 { + println!("Positive number"); +} else if number < 0 { + println!("Negative number"); +} else { + println!("Zero"); +} +``` + +`if` is an expression, so it can return values: + +```rust +let result = if condition { 5 } else { 10 }; +``` + +## Loops + +Use `loop` to repeat code until a condition is met: + +```rust +let mut counter = 0; +loop { + if counter == 5 { + break; + } + println!("Counter: {}", counter); + counter += 1; +} +``` + +Loops can return values using `break`: + +```rust +let result = loop { + counter += 1; + if counter == 10 { + break counter * 2; // Returns 20 + } +}; +``` + +Use `continue` to skip to the next iteration: + +```rust +let mut i = 0; +loop { + i += 1; + if i % 2 == 0 { + continue; // Skip even numbers + } + if i > 10 { + break; + } + println!("{}", i); // Only prints odd numbers +} +``` diff --git a/exercises/concept/bakery-order-system/.meta/config.json b/exercises/concept/bakery-order-system/.meta/config.json new file mode 100644 index 00000000..ff7890e7 --- /dev/null +++ b/exercises/concept/bakery-order-system/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "0xNeshi" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/bakery_order_system.cairo" + ], + "example": [ + ".meta/example.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "Learn Cairo's control flow constructs while managing a bakery's daily operations." +} \ No newline at end of file diff --git a/exercises/concept/bakery-order-system/.meta/design.md b/exercises/concept/bakery-order-system/.meta/design.md new file mode 100644 index 00000000..d14ef88b --- /dev/null +++ b/exercises/concept/bakery-order-system/.meta/design.md @@ -0,0 +1,19 @@ +# Design + +## Learning Objectives + +- Know how to use control flow constructs. + +## Concepts + +- Control flow + +## Prerequisites + +- Match basics + +## Resources to Refer To + +- [Cairo Book - Control Flow][cf] + +[cf]: https://www.starknet.io/cairo-book/ch02-05-control-flow.html diff --git a/exercises/concept/bakery-order-system/.meta/exemplar.cairo b/exercises/concept/bakery-order-system/.meta/exemplar.cairo new file mode 100644 index 00000000..0109fc9d --- /dev/null +++ b/exercises/concept/bakery-order-system/.meta/exemplar.cairo @@ -0,0 +1,50 @@ +#[derive(Drop, PartialEq)] +pub enum Pastry { + Croissant, + Muffin, + Cookie, +} + +pub fn calculate_total(pastry: Pastry, quantity: u32) -> u32 { + let price_per_item = match pastry { + Pastry::Croissant => 3, + Pastry::Muffin => 2, + Pastry::Cookie => 1, + }; + + price_per_item * quantity +} + +pub fn apply_discount(total: u32) -> u32 { + if total >= 20 { + // 10% discount: multiply by 90, divide by 100 + (total * 90) / 100 + } else if total >= 10 { + // 5% discount: multiply by 95, divide by 100 + (total * 95) / 100 + } else { + // No discount + total + } +} + +pub fn baking_schedule(total_orders: u32) -> Array { + let mut schedule = ArrayTrait::new(); + let mut remaining_orders = total_orders; + + loop { + if remaining_orders == 0 { + break; + } + + if remaining_orders >= 5 { + schedule.append(5); + remaining_orders -= 5; + } else { + schedule.append(remaining_orders); + remaining_orders = 0; + } + } + + schedule +} diff --git a/exercises/concept/bakery-order-system/Scarb.toml b/exercises/concept/bakery-order-system/Scarb.toml new file mode 100644 index 00000000..e5ec1ddf --- /dev/null +++ b/exercises/concept/bakery-order-system/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "bakery_order_system" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.9.2" diff --git a/exercises/concept/bakery-order-system/src/lib.cairo b/exercises/concept/bakery-order-system/src/lib.cairo new file mode 100644 index 00000000..9d32699c --- /dev/null +++ b/exercises/concept/bakery-order-system/src/lib.cairo @@ -0,0 +1,30 @@ +/// Types of pastries available in the bakery +#[derive(Drop, PartialEq)] +pub enum Pastry { + Croissant, + Muffin, + Cookie, +} + +/// Calculate the total cost of an order +pub fn calculate_total(pastry: Pastry, quantity: u32) -> u32 { + // Croissant: 3 coins, Muffin: 2 coins, Cookie: 1 coin + // Return total cost for the given quantity + panic!("implement `calculate_total`") +} + +/// Apply discount based on order size +pub fn apply_discount(total: u32) -> u32 { + // Orders >= 20 coins: 10% discount + // Orders >= 10 coins: 5% discount + // Orders < 10 coins: no discount + panic!("implement `apply_discount`") +} + +/// Generate the daily baking schedule +pub fn baking_schedule(total_orders: u32) -> Array { + // Bake in batches of 5 orders each + // Return an array showing how many orders are baked each hour + // Continue until all orders are completed + panic!("implement `baking_schedule`") +} diff --git a/exercises/concept/bakery-order-system/tests/bakery_order_system.cairo b/exercises/concept/bakery-order-system/tests/bakery_order_system.cairo new file mode 100644 index 00000000..128189b3 --- /dev/null +++ b/exercises/concept/bakery-order-system/tests/bakery_order_system.cairo @@ -0,0 +1,81 @@ +use bakery_order_system::{Pastry, calculate_total, apply_discount, baking_schedule}; + +#[test] +fn test_calculate_croissant_total() { + assert_eq!(calculate_total(Pastry::Croissant, 4), 12); + assert_eq!(calculate_total(Pastry::Croissant, 1), 3); +} + +#[test] +#[ignore] +fn test_calculate_muffin_total() { + assert_eq!(calculate_total(Pastry::Muffin, 5), 10); + assert_eq!(calculate_total(Pastry::Muffin, 3), 6); +} + +#[test] +#[ignore] +fn test_calculate_cookie_total() { + assert_eq!(calculate_total(Pastry::Cookie, 10), 10); + assert_eq!(calculate_total(Pastry::Cookie, 7), 7); +} + +#[test] +#[ignore] +fn test_no_discount() { + assert_eq!(apply_discount(5), 5); + assert_eq!(apply_discount(9), 9); +} + +#[test] +#[ignore] +fn test_five_percent_discount() { + assert_eq!(apply_discount(10), 9); // 10 * 0.95 = 9.5, rounded down to 9 + assert_eq!(apply_discount(15), 14); // 15 * 0.95 = 14.25, rounded down to 14 + assert_eq!(apply_discount(19), 18); // 19 * 0.95 = 18.05, rounded down to 18 +} + +#[test] +#[ignore] +fn test_ten_percent_discount() { + assert_eq!(apply_discount(20), 18); // 20 * 0.9 = 18 + assert_eq!(apply_discount(25), 22); // 25 * 0.9 = 22.5, rounded down to 22 + assert_eq!(apply_discount(30), 27); // 30 * 0.9 = 27 +} + +#[test] +#[ignore] +fn test_baking_schedule_multiple_batches() { + let result = baking_schedule(18); + let expected = [5, 5, 5, 3].span(); + + assert_eq!(result.len(), 4); + assert_eq!(result.span(), expected); +} + +#[test] +#[ignore] +fn test_baking_schedule_exact_batches() { + let result = baking_schedule(10); + let expected = [5, 5].span(); + + assert_eq!(result.len(), 2); + assert_eq!(result.span(), expected); +} + +#[test] +#[ignore] +fn test_baking_schedule_single_partial_batch() { + let result = baking_schedule(3); + let expected = [3].span(); + + assert_eq!(result.len(), 1); + assert_eq!(result.span(), expected); +} + +#[test] +#[ignore] +fn test_baking_schedule_zero_orders() { + let result = baking_schedule(0); + assert_eq!(result.len(), 0); +} From ea39920eb3f8fc905c35f481aef9153f3f072cdc Mon Sep 17 00:00:00 2001 From: Nenad Date: Sat, 9 Aug 2025 14:58:05 +0200 Subject: [PATCH 2/7] fmt --- exercises/concept/bakery-order-system/.docs/instructions.md | 3 ++- exercises/concept/bakery-order-system/.meta/config.json | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/exercises/concept/bakery-order-system/.docs/instructions.md b/exercises/concept/bakery-order-system/.docs/instructions.md index 6907af0c..1efbbdaf 100644 --- a/exercises/concept/bakery-order-system/.docs/instructions.md +++ b/exercises/concept/bakery-order-system/.docs/instructions.md @@ -31,7 +31,8 @@ apply_discount(8) // Returns: 8 (no discount) ## 3. Create Baking Schedule -Implement `baking_schedule` that creates a daily baking plan. The bakery bakes in batches of 5 orders per hour: +Implement `baking_schedule` that creates a daily baking plan. +The bakery bakes in batches of 5 orders per hour: ```rust baking_schedule(18) // Returns: [5, 5, 5, 3] (3 full batches + 3 remaining) diff --git a/exercises/concept/bakery-order-system/.meta/config.json b/exercises/concept/bakery-order-system/.meta/config.json index ff7890e7..d7f7583f 100644 --- a/exercises/concept/bakery-order-system/.meta/config.json +++ b/exercises/concept/bakery-order-system/.meta/config.json @@ -9,12 +9,12 @@ "test": [ "tests/bakery_order_system.cairo" ], - "example": [ - ".meta/example.cairo" + "exemplar": [ + ".meta/exemplar.cairo" ], "invalidator": [ "Scarb.toml" ] }, "blurb": "Learn Cairo's control flow constructs while managing a bakery's daily operations." -} \ No newline at end of file +} From 31c8fc52f6bce2c46d6122c555e64f6b4115e0ef Mon Sep 17 00:00:00 2001 From: Nenad Date: Sat, 9 Aug 2025 15:00:17 +0200 Subject: [PATCH 3/7] fix config uuid --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 3168a713..134c3455 100644 --- a/config.json +++ b/config.json @@ -102,7 +102,7 @@ { "slug": "bakery-order-system", "name": "Bakery Order System", - "uuid": "536d9f09-5910-4a26-93fd-2242667b0b87", + "uuid": "a979b9a0-7fd9-46f7-bacf-f0044c3c9bb0", "concepts": [ "control-flow" ], From c030aaf08aa0cac67e3241e1b2071b6743e4856b Mon Sep 17 00:00:00 2001 From: Nenad Date: Sat, 9 Aug 2025 15:01:22 +0200 Subject: [PATCH 4/7] add missing semicolon --- exercises/concept/bakery-order-system/.meta/exemplar.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/concept/bakery-order-system/.meta/exemplar.cairo b/exercises/concept/bakery-order-system/.meta/exemplar.cairo index 0109fc9d..221a3b51 100644 --- a/exercises/concept/bakery-order-system/.meta/exemplar.cairo +++ b/exercises/concept/bakery-order-system/.meta/exemplar.cairo @@ -44,7 +44,7 @@ pub fn baking_schedule(total_orders: u32) -> Array { schedule.append(remaining_orders); remaining_orders = 0; } - } + }; schedule } From c2864fab4d6196d113f837df0f17c97632b802b4 Mon Sep 17 00:00:00 2001 From: Nenad Date: Sun, 10 Aug 2025 10:33:19 +0200 Subject: [PATCH 5/7] wip -> beta --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 134c3455..9e2a089c 100644 --- a/config.json +++ b/config.json @@ -109,7 +109,7 @@ "prerequisites": [ "match-basics" ], - "status": "wip" + "status": "beta" }, { "slug": "welcome-to-tech-palace", From 9127bec9f46658a90d68495045066bcf2c06e264 Mon Sep 17 00:00:00 2001 From: 0xNeshi Date: Sun, 17 Aug 2025 15:53:23 +0200 Subject: [PATCH 6/7] introduce break earlier --- exercises/concept/bakery-order-system/.docs/introduction.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/concept/bakery-order-system/.docs/introduction.md b/exercises/concept/bakery-order-system/.docs/introduction.md index 81eb4091..08a141eb 100644 --- a/exercises/concept/bakery-order-system/.docs/introduction.md +++ b/exercises/concept/bakery-order-system/.docs/introduction.md @@ -25,7 +25,8 @@ let result = if condition { 5 } else { 10 }; ## Loops -Use `loop` to repeat code until a condition is met: +Use `loop` to repeat code. +A loop runs indefinitely unless you stop it; use `break` to exit the loop when a condition is met. ```rust let mut counter = 0; From b2ebd53a867e58e58f9dcc4efab44e91e57d26ba Mon Sep 17 00:00:00 2001 From: 0xNeshi Date: Sun, 17 Aug 2025 15:55:40 +0200 Subject: [PATCH 7/7] introduce round down earlier --- .../concept/bakery-order-system/.docs/instructions.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/exercises/concept/bakery-order-system/.docs/instructions.md b/exercises/concept/bakery-order-system/.docs/instructions.md index 1efbbdaf..920a24c3 100644 --- a/exercises/concept/bakery-order-system/.docs/instructions.md +++ b/exercises/concept/bakery-order-system/.docs/instructions.md @@ -17,16 +17,17 @@ calculate_total(Pastry::Cookie, 10) // Returns: 10 (10 * 1) ## 2. Apply Volume Discounts -Implement `apply_discount` that applies discounts based on order total: +Implement `apply_discount` that applies discounts based on order total. +Apply the percentage discount to the total, then round down to the nearest whole coin (floor). - Orders ≥ 20 coins: 10% discount - Orders ≥ 10 coins: 5% discount - Orders < 10 coins: no discount ```rust -apply_discount(25) // Returns: 22 (25 - 10% = 22.5, rounded down) -apply_discount(15) // Returns: 14 (15 - 5% = 14.25, rounded down) -apply_discount(8) // Returns: 8 (no discount) +apply_discount(25) // Returns: 22 +apply_discount(15) // Returns: 14 +apply_discount(8) // Returns: 8 ``` ## 3. Create Baking Schedule