diff --git a/.github/workflows/lint-markdown.yml b/.github/workflows/lint.yml similarity index 58% rename from .github/workflows/lint-markdown.yml rename to .github/workflows/lint.yml index d6e97faf..abe4fc63 100644 --- a/.github/workflows/lint-markdown.yml +++ b/.github/workflows/lint.yml @@ -17,7 +17,13 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - name: Run markdown lint run: ./bin/lint_markdown.sh + + configlet: + name: configlet lint + fmt + uses: exercism/github-actions/.github/workflows/configlet.yml@main + with: + fmt: true \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f8b57c00..9ccada3d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - name: Run shellcheck uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - name: Fetch origin/main run: git fetch --depth=1 origin main @@ -51,7 +51,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - name: Getting scarb uses: software-mansion/setup-scarb@v1 diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 00000000..12139b11 --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -0,0 +1,24 @@ +globs: + - "concepts/**/*.md" + - "exercises/concept/**/*.md" + - "exercises/practice/*/.docs/{hints.md,*.append.md}" + +config: + MD001: true + MD002: true + MD003: + style: "atx" + MD004: + style: dash + MD013: false + MD033: false + MD034: + severity: error + MD054: + autolink: false + inline: false + shortcut: false + collapsed: false + +customRules: + - "single-sentence-per-line-rule.js" diff --git a/.markdownlint.yaml b/.markdownlint.yaml deleted file mode 100644 index ff7d7cc8..00000000 --- a/.markdownlint.yaml +++ /dev/null @@ -1 +0,0 @@ -MD013: false diff --git a/README.md b/README.md index e6442bcf..5b7908a5 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,36 @@ Basic linting finished successfully: - Required shared exercise docs are present ``` +### Markdown linting + +To check for markdown files' formatting issues, open a bash terminal and run: + +```shell +./bin/lint_markdown.sh +``` + +You can even pass the `--fix` flag to automatically fix those issues that can be fixed automatically: + +```shell +./bin/lint_markdown.sh --fix +``` + +### Exercise formatting + +To format Cairo files, open a bash terminal and run: + +```shell +./bin/format_exercises.sh +``` + +### Format all + +To run all formatting scripts at once, open a bash terminal and run: + +```shell +./bin/format_all.sh +``` +
diff --git a/bin/format_all.sh b/bin/format_all.sh new file mode 100755 index 00000000..1fc0f4f4 --- /dev/null +++ b/bin/format_all.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -eo pipefail + +./bin/lint_markdown.sh --fix +./bin/configlet fmt -uy +./bin/format_exercises.sh diff --git a/bin/format_exercises.sh b/bin/format_exercises.sh index b24565f4..3e14ef8b 100755 --- a/bin/format_exercises.sh +++ b/bin/format_exercises.sh @@ -14,6 +14,11 @@ for exercise_dir in $exercises; do exercise=$(basename "$exercise_dir") + if [ ! -s "$exercise_dir/Scarb.toml" ]; then + echo "Exercise $exercise is just a stub, skipping" + continue + fi + # scarb fmt cannot currently format individual files, so we have to # temporarily move the solution files into the Cairo package, where # 'scarb fmt' can format it as well diff --git a/bin/lint_markdown.sh b/bin/lint_markdown.sh index 51e3f271..b81f0d08 100755 --- a/bin/lint_markdown.sh +++ b/bin/lint_markdown.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash set -eo pipefail -npx markdownlint-cli2 docs/*.md concepts/**/*.md +npx markdownlint-cli2 "$@" diff --git a/bin/verify-exercises b/bin/verify-exercises index 2651b4e0..bdd775ef 100755 --- a/bin/verify-exercises +++ b/bin/verify-exercises @@ -40,6 +40,12 @@ for exercise_path in $exercises; do cd "$repo" exercise=$(basename "$exercise_path") + + if [ ! -s "$exercise_path/Scarb.toml" ]; then + echo "Exercise $exercise is just a stub, skipping" + continue + fi + echo "Checking $exercise exercise..." tmp_dir=$(mktemp -d) diff --git a/concepts/arrays/about.md b/concepts/arrays/about.md index 98121f28..e698659c 100644 --- a/concepts/arrays/about.md +++ b/concepts/arrays/about.md @@ -1,6 +1,9 @@ # Arrays -In Cairo, an array is a data structure that allows you to store a collection of elements of the same type. Arrays in Cairo have limited modification options. You can only append items to the end of an array and remove items from the front. Arrays are, in fact, queues whose values can't be modified. +In Cairo, an array is a data structure that allows you to store a collection of elements of the same type. +Arrays in Cairo have limited modification options. +You can only append items to the end of an array and remove items from the front. +Arrays are, in fact, queues whose values can't be modified. ## Array Construction @@ -30,7 +33,8 @@ arr_implicit_type.append(2); ## Removing Array Elements -The only way to remove elements from an array in Cairo is from the front. This operation is facilitated by the `pop_front()` method, which returns an `Option` that can be unwrapped, containing the removed element, or `Option::None` if the array is empty. +The only way to remove elements from an array in Cairo is from the front. +This operation is facilitated by the `pop_front()` method, which returns an `Option` that can be unwrapped, containing the removed element, or `Option::None` if the array is empty. ```rust fn main() { @@ -42,7 +46,8 @@ fn main() { ## Accessing Values in an Array -To access array elements, you can use `get()` or `at()` array methods that return different types. An equivalent to `arr.at(index)` is the subscripting operator `arr[index]`. +To access array elements, you can use `get()` or `at()` array methods that return different types. +An equivalent to `arr.at(index)` is the subscripting operator `arr[index]`. ### Using get() Method @@ -69,7 +74,9 @@ fn main() { ### Using at() Method -The `at()` method directly gives you a snapshot of the element at a specific index using the `unbox()` operation to extract the stored value from the box. A shorthand for this method is using the subscripting operator `arr[index]`. If you try to access an index that doesn't exist (out-of-bounds), the program will panic with an error. +The `at()` method directly gives you a snapshot of the element at a specific index using the `unbox()` operation to extract the stored value from the box. +A shorthand for this method is using the subscripting operator `arr[index]`. +If you try to access an index that doesn't exist (out-of-bounds), the program will panic with an error. ```rust fn main() { @@ -83,13 +90,16 @@ fn main() { ## Size-related Methods -To determine the number of elements in an array, use the `len()` method. The return value is of type `usize`. +To determine the number of elements in an array, use the `len()` method. +The return value is of type `usize`. If you want to check if an array is empty or not, you can use the `is_empty()` method, which returns `true` if the array is empty and `false` otherwise. ## `Span` -`Span` is a struct that represents a snapshot of an `Array`. It is designed to provide safe and controlled access to the elements of an array without modifying the original array. `Span` is particularly useful for ensuring data integrity and avoiding borrowing issues when passing arrays between functions or when performing read-only operations. +`Span` is a struct that represents a snapshot of an `Array`. +It is designed to provide safe and controlled access to the elements of an array without modifying the original array. +`Span` is particularly useful for ensuring data integrity and avoiding borrowing issues when passing arrays between functions or when performing read-only operations. > All methods provided by `Array` can also be used with `Span`, except for the `append()` method. @@ -102,7 +112,8 @@ let my_span: Span = my_array.span(); ## The Fixed Size `Array` Type -We write the values in a fixed-size array as a comma-separated list inside square brackets. The array’s type is written using square brackets with the type of each element, a semicolon, and then the number of elements in the array. +We write the values in a fixed-size array as a comma-separated list inside square brackets. +The array’s type is written using square brackets with the type of each element, a semicolon, and then the number of elements in the array. ```rust let arr: [u64; 5] = [1, 2, 3, 4, 5]; diff --git a/concepts/arrays/introduction.md b/concepts/arrays/introduction.md index 3700318c..6c508fbd 100644 --- a/concepts/arrays/introduction.md +++ b/concepts/arrays/introduction.md @@ -1,5 +1,12 @@ # Introduction -In Cairo, arrays are fundamental data structures designed to store collections of elements of the same type in a structured manner. Similar to lists in other programming languages, each element in an array is accessed by its index, allowing for efficient retrieval and manipulation. +In Cairo, arrays are fundamental data structures designed to store collections of elements of the same type in a structured manner. +Similar to lists in other programming languages, each element in an array is accessed by its index, allowing for efficient retrieval and manipulation. -Cairo arrays are immutable data structures. Elements can only be appended to the end or removed from the front of the array. This design ensures data integrity and stability, aligning with Cairo's approach to memory management. Arrays are initialized using `ArrayTrait::new()` and support type-specific declarations for element storage. Accessing elements can be done with `get()` or `at()` methods, as well as using the subscripting operator `arr[index]`. You can only remove elements from the front of an array by using the `pop_front()` function. These characteristics make Cairo arrays suitable for structured data storage and retrieval tasks. +Cairo arrays are immutable data structures. +Elements can only be appended to the end or removed from the front of the array. +This design ensures data integrity and stability, aligning with Cairo's approach to memory management. +Arrays are initialized using `ArrayTrait::new()` and support type-specific declarations for element storage. +Accessing elements can be done with `get()` or `at()` methods, as well as using the subscripting operator `arr[index]`. +You can only remove elements from the front of an array by using the `pop_front()` function. +These characteristics make Cairo arrays suitable for structured data storage and retrieval tasks. diff --git a/concepts/booleans/about.md b/concepts/booleans/about.md index b1ef2e63..922ffb81 100644 --- a/concepts/booleans/about.md +++ b/concepts/booleans/about.md @@ -1,8 +1,11 @@ # The Boolean Type -In Cairo, the Boolean type is essential for logical operations and conditional statements. Booleans in Cairo are one `felt252` in size, ensuring efficient use of space while maintaining the simplicity of true/false logic. +In Cairo, the Boolean type is essential for logical operations and conditional statements. +Booleans in Cairo are one `felt252` in size, ensuring efficient use of space while maintaining the simplicity of true/false logic. -The Boolean type in Cairo is specified using the keyword `bool`. This enables the declaration and manipulation of Boolean variables within the language. For instance, consider the following example: +The Boolean type in Cairo is specified using the keyword `bool`. +This enables the declaration and manipulation of Boolean variables within the language. +For instance, consider the following example: ```rust fn main() { @@ -11,9 +14,12 @@ fn main() { } ``` -In this example, two Boolean variables are declared. The variable `t` is assigned the value `true` and declared as a boolean type implicitly, while the variable `f` is explicitly annotated with the `bool` type and assigned the value `false`. +In this example, two Boolean variables are declared. +The variable `t` is assigned the value `true` and declared as a boolean type implicitly, while the variable `f` is explicitly annotated with the `bool` type and assigned the value `false`. -One important aspect to note is that when declaring a boolean variable in Cairo, it is mandatory to use either the `true` or `false` literals as the value. This means that integer literals, such as `0` or `1`, cannot be used as substitutes for `false` or `true`. The strict enforcement of this rule ensures type safety and prevents potential logical errors in smart contract development. +One important aspect to note is that when declaring a boolean variable in Cairo, it is mandatory to use either the `true` or `false` literals as the value. +This means that integer literals, such as `0` or `1`, cannot be used as substitutes for `false` or `true`. +The strict enforcement of this rule ensures type safety and prevents potential logical errors in smart contract development. ## Boolean Operators in Cairo @@ -23,7 +29,8 @@ One important aspect to note is that when declaring a boolean variable in Cairo, ## Example Code -The following code snippet demonstrates how to use boolean types in Cairo. It shows the declaration of boolean variables, assignment of boolean expressions, and usage of assertions to verify the correctness of the boolean logic: +The following code snippet demonstrates how to use boolean types in Cairo. +It shows the declaration of boolean variables, assignment of boolean expressions, and usage of assertions to verify the correctness of the boolean logic: ```rust fn main() { diff --git a/concepts/control-flow/about.md b/concepts/control-flow/about.md index ec9956c3..697dc569 100644 --- a/concepts/control-flow/about.md +++ b/concepts/control-flow/about.md @@ -10,7 +10,8 @@ These constructs are fundamental for implementing logic and iteration in Cairo. ## `if` Expressions -`if` expressions in Cairo allow you to control the flow of execution based on conditional logic. They follow a structure where code is executed depending on whether a boolean condition evaluates to `true` or `false`. +`if` expressions in Cairo allow you to control the flow of execution based on conditional logic. +They follow a structure where code is executed depending on whether a boolean condition evaluates to `true` or `false`. ```rust fn main() { @@ -22,7 +23,9 @@ fn main() { } ``` -This example demonstrates a simple `if` block in Cairo. The message `"It's hot outside!"` is printed only if the condition `temperature > 25` evaluates to `true`. If the condition is `false`, no code is executed, and the program continues. +This example demonstrates a simple `if` block in Cairo. +The message `"It's hot outside!"` is printed only if the condition `temperature > 25` evaluates to `true`. +If the condition is `false`, no code is executed, and the program continues. ```rust fn main() { @@ -81,10 +84,13 @@ fn main() { ## Loops -Loops are a fundamental construct that allow the repetition of code blocks, enabling efficient and controlled execution of tasks. Loops can iterate over a sequence or execute code repeatedly based on a condition. Cairo provides three primary loop flavors: +Loops are a fundamental construct that allow the repetition of code blocks, enabling efficient and controlled execution of tasks. +Loops can iterate over a sequence or execute code repeatedly based on a condition. +Cairo provides three primary loop flavors: - **loop**: A basic loop that runs indefinitely unless explicitly stopped using a `break` statement. -- **while**: Continues to execute as long as a specified condition remains `true`. It stops when the condition becomes `false`. +- **while**: Continues to execute as long as a specified condition remains `true`. + It stops when the condition becomes `false`. - **for**: Iterates over elements in a collection, such as arrays, executing code for each element. ### Repeating Code with `loop` @@ -101,7 +107,10 @@ fn main() { Executing this code will print `"again!"` indefinitely in the console until either the program runs out of gas or we stop it manually. -> Note: Cairo prevents us from running a program with infinite loops by including a gas meter. The gas meter is a mechanism that limits the amount of computation that can be done in a program. Gas is a unit of measurement that expresses the computation cost of an instruction. When the gas meter runs out, the program will stop. +> Note: Cairo prevents us from running a program with infinite loops by including a gas meter. +> The gas meter is a mechanism that limits the amount of computation that can be done in a program. +> Gas is a unit of measurement that expresses the computation cost of an instruction. +> When the gas meter runs out, the program will stop. To break out of a loop, place the `break` keyword within the loop to tell the program when to stop executing the it. @@ -141,7 +150,8 @@ Executing this program will not print the value of `i` when it is equal to `5`. #### Returning Values from `loop` -You can return a value from a `loop` by using the `break` keyword followed by the value you want to return. This allows you to exit the `loop` and pass a result to the rest of your code. +You can return a value from a `loop` by using the `break` keyword followed by the value you want to return. +This allows you to exit the `loop` and pass a result to the rest of your code. ```rust fn main() { diff --git a/concepts/control-flow/introduction.md b/concepts/control-flow/introduction.md index 8e1fe534..25315bdc 100644 --- a/concepts/control-flow/introduction.md +++ b/concepts/control-flow/introduction.md @@ -1,3 +1,5 @@ # Introduction -Control flow in Cairo, through constructs like `if` expressions and loops, allows developers to direct the execution of their programs based on conditional logic or repetitive tasks. `if` expressions enable branching based on whether conditions evaluate to `true` or `false`, while loops (`loop`, `while`, `for`) facilitate repeated execution of code blocks until specific criteria are met. These constructs are crucial for implementing logic, iteration, and handling multiple conditions concisely, making them essential tools for writing efficient and dynamic code in Cairo. +Control flow in Cairo, through constructs like `if` expressions and loops, allows developers to direct the execution of their programs based on conditional logic or repetitive tasks. +`if` expressions enable branching based on whether conditions evaluate to `true` or `false`, while loops (`loop`, `while`, `for`) facilitate repeated execution of code blocks until specific criteria are met. +These constructs are crucial for implementing logic, iteration, and handling multiple conditions concisely, making them essential tools for writing efficient and dynamic code in Cairo. diff --git a/concepts/dictionaries/.meta/config.json b/concepts/dictionaries/.meta/config.json index 25336151..35b1d32b 100644 --- a/concepts/dictionaries/.meta/config.json +++ b/concepts/dictionaries/.meta/config.json @@ -1,7 +1,7 @@ { "blurb": "", "authors": [ - "0xNeshi" + "" ], "contributors": [] } diff --git a/concepts/enums/about.md b/concepts/enums/about.md index 5371377c..37731d4f 100644 --- a/concepts/enums/about.md +++ b/concepts/enums/about.md @@ -1,6 +1,7 @@ # Enums -Enums are Cairo data type used to define a set of predefined variants for the purpose of code readability and safety. In Cairo, `enum` variants can be declared to hold different data types (integers, structs, tuples, other enums, etc.). +Enums are Cairo data type used to define a set of predefined variants for the purpose of code readability and safety. +In Cairo, `enum` variants can be declared to hold different data types (integers, structs, tuples, other enums, etc.). The code snippet below shows how enums could be defined: @@ -14,7 +15,10 @@ enum Direction { } ``` -The `Direction` enum declared above is a simple enum with four variants: `North`, `East`, `South`, and `West`. The naming convention is to use PascalCase for enum variants. Each variant represents a distinct value of the `Direction` enum type. Each variant in this particular enum example has no associated value and can be instantiated using this syntax: +The `Direction` enum declared above is a simple enum with four variants: `North`, `East`, `South`, and `West`. +The naming convention is to use PascalCase for enum variants. +Each variant represents a distinct value of the `Direction` enum type. +Each variant in this particular enum example has no associated value and can be instantiated using this syntax: ```rust let direction = Direction::North; @@ -32,7 +36,8 @@ enum Direction { } ``` -It is evident from this example that each variant in the `Direction` enum has an associated value of `u128`. This kind of enum can be instantiated as follows: +It is evident from this example that each variant in the `Direction` enum has an associated value of `u128`. +This kind of enum can be instantiated as follows: ```rust let direction = Direction::North(10); @@ -40,7 +45,8 @@ let direction = Direction::North(10); ## Complex Enum Example with Custom Types -An enum can be declared with a combination of different data types as its variants. In the example below, `Action` is a complex enum that as a collection of `Quit`, `Direction`, `SendMessage`, `ChangeAvatar`, and `ProfileState` as variants. +An enum can be declared with a combination of different data types as its variants. +In the example below, `Action` is a complex enum that as a collection of `Quit`, `Direction`, `SendMessage`, `ChangeAvatar`, and `ProfileState` as variants. ```rust #[derive(Drop)] @@ -70,7 +76,9 @@ enum Action { ## Trait Implementations for Enums -In Cairo, traits can be defined and implemented on custom enums. This allows for you to define methods and attributes associated with an enum. Below is an example of this where we define a `Message` enum and implement a `Processing` trait for it: +In Cairo, traits can be defined and implemented on custom enums. +This allows for you to define methods and attributes associated with an enum. +Below is an example of this where we define a `Message` enum and implement a `Processing` trait for it: ```rust #[derive(Drop)] @@ -102,11 +110,14 @@ let msg: Message = Message::Quit; msg.process(); // prints "quitting" ``` -In many situations, enums can come in handy especially when used with the `match` flow as used above in the traits implementation. Read more about the `match` flow in [The Cairo Book](https://book.cairo-lang.org/ch06-02-the-match-control-flow-construct.html). +In many situations, enums can come in handy especially when used with the `match` flow as used above in the traits implementation. +Read more about the `match` flow in [The Cairo Book][match]. ## The `Option` Enum and Its Advantages -The `Option` enum is a standard Cairo enum that represents the concept of an optional value. It has two variants: `Some: T` and `None`. `Some` variant has an associated value of type `T`, while `None` represents the absence of an associated value. +The `Option` enum is a standard Cairo enum that represents the concept of an optional value. +It has two variants: `Some: T` and `None`. +`Some` variant has an associated value of type `T`, while `None` represents the absence of an associated value. ```rust enum Option { @@ -115,7 +126,8 @@ enum Option { } ``` -The `Option` enum is helpful because it allows you to explicitly represent the possibility of a value being absent, making your code more expressive and easier to reason about. Using `Option` can also help prevent bugs caused by using uninitialized or unexpected `null` values. +The `Option` enum is helpful because it allows you to explicitly represent the possibility of a value being absent, making your code more expressive and easier to reason about. +Using `Option` can also help prevent bugs caused by using uninitialized or unexpected `null` values. The function below shows how the `Option` enum is used to return the index of the first element of an array with a given value, or return `None` if the element is absent. @@ -185,4 +197,8 @@ fn main() { } ``` -There are other native enums, one of which is the `Result` enum, which allows for graceful error handling. Read more about the `Result` enum in [The Cairo Book](https://book.cairo-lang.org/ch09-02-recoverable-errors.html#the-result-enum). +There are other native enums, one of which is the `Result` enum, which allows for graceful error handling. +Read more about the `Result` enum in [The Cairo Book][results]. + +[match]: https://book.cairo-lang.org/ch06-02-the-match-control-flow-construct.html +[results]: https://book.cairo-lang.org/ch09-02-recoverable-errors.html#the-result-enum diff --git a/concepts/enums/introduction.md b/concepts/enums/introduction.md index 1cbb72b0..c12c020a 100644 --- a/concepts/enums/introduction.md +++ b/concepts/enums/introduction.md @@ -1,3 +1,4 @@ # Introduction -Similar to other programming languages, enums, short for `enumerations`, are used in Cairo to define custom data types that hold a collection of fixed set of named values called _variants_. Each variant that makes up an enum is distinct and has a specific meaning. +Similar to other programming languages, enums, short for `enumerations`, are used in Cairo to define custom data types that hold a collection of fixed set of named values called _variants_. +Each variant that makes up an enum is distinct and has a specific meaning. diff --git a/concepts/error-handling/about.md b/concepts/error-handling/about.md index 51e454ed..5a617597 100644 --- a/concepts/error-handling/about.md +++ b/concepts/error-handling/about.md @@ -9,7 +9,9 @@ There are two types of errors in Cairo: ## Unrecoverable Errors with `panic` -In Cairo, unexpected issues may arise during program execution, resulting in runtime errors called "panics". This will stop the program execution, which is helpful in the case where continuing wouldn't make any sense. A panic can occur either inadvertently, through actions causing the code to panic (e.g., accessing an array beyond its bounds), or deliberately, by invoking the `panic` function. +In Cairo, unexpected issues may arise during program execution, resulting in runtime errors called "panics". +This will stop the program execution, which is helpful in the case where continuing wouldn't make any sense. +A panic can occur either inadvertently, through actions causing the code to panic (e.g., accessing an array beyond its bounds), or deliberately, by invoking the `panic` function. ```rust fn main() { @@ -98,7 +100,8 @@ fn main() { ### The `?` Operator -The `?` operator is used for concise and implicit error handling, letting the calling function deal with any errors that might occur. When you use the `?` operator on a `Result` or `Option` type, it will do the following: +The `?` operator is used for concise and implicit error handling, letting the calling function deal with any errors that might occur. +When you use the `?` operator on a `Result` or `Option` type, it will do the following: - If the value is `Result::Ok(x)` or `Option::Some(x)`, it will return the inner value `x` directly. - If the value is `Result::Err(e)` or `Option::None`, it will propagate the error or `None` by immediately returning from the function. diff --git a/concepts/felts/about.md b/concepts/felts/about.md index a47573b4..8d34ba9c 100644 --- a/concepts/felts/about.md +++ b/concepts/felts/about.md @@ -1,22 +1,30 @@ # Felt Type -In Cairo, the default type for a variable or argument, if not explicitly specified, is a **felt**, also called a **field element**. This is one of the [scalar types](https://book.cairo-lang.org/ch02-02-data-types.html#scalar-types) represented by the keyword `felt252`. A field element is an integer within the range $0 ≤ x < P$, where $P$ is a very large prime number currently equal to $2^{251} + 17 \cdot 2^{192} + 1$. +In Cairo, the default type for a variable or argument, if not explicitly specified, is a **felt**, also called a **field element**. +This is one of the [scalar types][scalars] represented by the keyword `felt252`. +A field element is an integer within the range $0 ≤ x < P$, where $P$ is a very large prime number currently equal to $2^{251} + 17 \cdot 2^{192} + 1$. ## Arithmetic Operations When performing arithmetic operations (addition, subtraction, multiplication) with field elements, if the result falls outside the range of the prime number $P$, an overflow or underflow occurs. -To handle this, Cairo adjusts the result by adding or subtracting an appropriate multiple of $P$ to bring the result back within the valid range. Essentially, this means the result is computed modulo $P$. +To handle this, Cairo adjusts the result by adding or subtracting an appropriate multiple of $P$ to bring the result back within the valid range. +Essentially, this means the result is computed modulo $P$. ## Division in Cairo -A critical distinction between integers and field elements is how division is handled. Unlike regular CPU integer division, where the result of $x$ divided by $y$ is defined as the integer part of the quotient (returning the floor value), Cairo ensures that the division of field elements always satisfies the equation: +A critical distinction between integers and field elements is how division is handled. +Unlike regular CPU integer division, where the result of $x$ divided by $y$ is defined as the integer part of the quotient (returning the floor value), Cairo ensures that the division of field elements always satisfies the equation: $\frac{x}{y} \cdot y = x$ This means: -- If $y$ divides $x$ as integers, Cairo will provide the expected result. For instance, $\frac{6}{2}$ will yield $3$. -- If $y$ does not divide $x$, the result can be surprising. For example, since $2 \cdot \left(\frac{P+1}{2}\right) = P + 1 \equiv 1 \mod P$, the value of $\frac{1}{2}$ in Cairo is $\frac{P + 1}{2}$ (not $0$ or $0.5$), as it satisfies the above equation. +- If $y$ divides $x$ as integers, Cairo will provide the expected result. + For instance, $\frac{6}{2}$ will yield $3$. +- If $y$ does not divide $x$, the result can be surprising. + For example, since $2 \cdot \left(\frac{P+1}{2}\right) = P + 1 \equiv 1 \mod P$, the value of $\frac{1}{2}$ in Cairo is $\frac{P + 1}{2}$ (not $0$ or $0.5$), as it satisfies the above equation. Understanding these nuances in field element operations is crucial when working with Cairo, especially when dealing with division and ensuring your calculations remain within the valid range defined by the prime number $P$. + +[scalars]: https://book.cairo-lang.org/ch02-02-data-types.html#scalar-types diff --git a/concepts/felts/introduction.md b/concepts/felts/introduction.md index 0f5b2a21..c2f50799 100644 --- a/concepts/felts/introduction.md +++ b/concepts/felts/introduction.md @@ -1,6 +1,8 @@ # Introduction -Felt is one of the scalar types in Cairo, just like integers and booleans, and is represented by the keyword `felt252`. `felt252` is a fundamental data type in Cairo from which all other data types are derived. `felt252` can also be used to store [short string representations](https://starknet-by-example.voyager.online/getting-started/basics/bytearrays-strings.html#short-strings) with a maximum length of 31 characters. +Felt is one of the scalar types in Cairo, just like integers and booleans, and is represented by the keyword `felt252`. +`felt252` is a fundamental data type in Cairo from which all other data types are derived. +`felt252` can also be used to store [short string representations][felts] with a maximum length of 31 characters. For example: @@ -10,3 +12,5 @@ For example: let _felt = felt + felt_as_str; ``` + +[felts]: https://starknet-by-example.voyager.online/getting-started/basics/bytearrays-strings.html#short-strings diff --git a/concepts/functions/.meta/config.json b/concepts/functions/.meta/config.json index 25336151..35b1d32b 100644 --- a/concepts/functions/.meta/config.json +++ b/concepts/functions/.meta/config.json @@ -1,7 +1,7 @@ { "blurb": "", "authors": [ - "0xNeshi" + "" ], "contributors": [] } diff --git a/concepts/generics/.meta/config.json b/concepts/generics/.meta/config.json index 25336151..35b1d32b 100644 --- a/concepts/generics/.meta/config.json +++ b/concepts/generics/.meta/config.json @@ -1,7 +1,7 @@ { "blurb": "", "authors": [ - "0xNeshi" + "" ], "contributors": [] } diff --git a/concepts/integers/about.md b/concepts/integers/about.md index 95c843de..d604c0b9 100644 --- a/concepts/integers/about.md +++ b/concepts/integers/about.md @@ -1,10 +1,14 @@ # Integer Types -An `integer` is a number without a fractional component. In Cairo, the type declaration for integers indicates the number of bits the programmer can use to store the value. +An `integer` is a number without a fractional component. +In Cairo, the type declaration for integers indicates the number of bits the programmer can use to store the value. Unsigned integers can only represent positive numbers and the number 0 (zero), which can be beneficial in certain programming contexts, such as array indexing and memory management. -Cairo also provides support for signed integers, starting with the prefix `i`. These integers can represent both positive and negative values, with sizes ranging from `i8` to `i128`. Each signed variant can store numbers from $-2^{n-1}$ to $2^{n-1} - 1$ inclusive, where $n$ is the number of bits that variant uses. For example, an `i8` can store numbers from $-2^7$ to $2^7 - 1$, which equals $-128$ to $127$. +Cairo also provides support for signed integers, starting with the prefix `i`. +These integers can represent both positive and negative values, with sizes ranging from `i8` to `i128`. +Each signed variant can store numbers from $-2^{n-1}$ to $2^{n-1} - 1$ inclusive, where $n$ is the number of bits that variant uses. +For example, an `i8` can store numbers from $-2^7$ to $2^7 - 1$, which equals $-128$ to $127$. | Length | Unsigned | Signed | |----------|----------|----------| @@ -15,11 +19,14 @@ Cairo also provides support for signed integers, starting with the prefix `i`. T | 128-bit | u128 | i128 | | 256-bit | u256 | ------ | -There is also a special integer type called `usize`, which is currently just an alias for `u32`. This type might play a more distinct role in the future, once Cairo can be compiled to MLIR (Multi-Level Intermediate Representation). +There is also a special integer type called `usize`, which is currently just an alias for `u32`. +This type might play a more distinct role in the future, once Cairo can be compiled to MLIR (Multi-Level Intermediate Representation). ## Integer Literals in Cairo -You can write integer literals in any of the forms shown in the below table. Note that number literals that can be multiple numeric types allow a type suffix, such as `57_u8`, to designate the type. It is also possible to use a visual separator `_` for number literals, in order to improve code readability. +You can write integer literals in any of the forms shown in the below table. +Note that number literals that can be multiple numeric types allow a type suffix, such as `57_u8`, to designate the type. +It is also possible to use a visual separator `_` for number literals, in order to improve code readability. | Numeric Literals | Example | | ---------------- | ------- | @@ -28,11 +35,15 @@ You can write integer literals in any of the forms shown in the below table. Not | Octal | 0o04321 | | Binary | 0b01 | -So how do you know which type of integer to use? Try to estimate the max value your int can have and choose the appropriate size. The primary situation in which you’d use `usize` is when indexing some sort of collection. +So how do you know which type of integer to use? +Try to estimate the max value your int can have and choose the appropriate size. +The primary situation in which you’d use `usize` is when indexing some sort of collection. ## Numeric Operations -Cairo supports the basic mathematical operations you’d expect for all the integer types: addition, subtraction, multiplication, division, and remainder. Integer division truncates toward zero to the nearest integer. The following code shows how you’d use each numeric operation in a `let` statement: +Cairo supports the basic mathematical operations you’d expect for all the integer types: addition, subtraction, multiplication, division, and remainder. +Integer division truncates toward zero to the nearest integer. +The following code shows how you’d use each numeric operation in a `let` statement: ```rust fn main() { @@ -56,4 +67,6 @@ fn main() { Each expression in these statements uses a mathematical operator and evaluates to a single value, which is then bound to a variable. -List of all operators that Cairo provides can be found [here](https://book.cairo-lang.org/appendix-02-operators-and-symbols.html#operators) +List of all operators that Cairo provides can be found [here][operators]. + +[operators]: https://book.cairo-lang.org/appendix-02-operators-and-symbols.html#operators diff --git a/concepts/match-basics/about.md b/concepts/match-basics/about.md index d845d719..511a8330 100644 --- a/concepts/match-basics/about.md +++ b/concepts/match-basics/about.md @@ -1,6 +1,7 @@ # Match Basics -The `match` expression allows for easy comparison of a value against a series of patterns and afterwards running the code based on the matched pattern. Literal values, variable names, wildcards, and many other things are what makes up a pattern. +The `match` expression allows for easy comparison of a value against a series of patterns and afterwards running the code based on the matched pattern. +Literal values, variable names, wildcards, and many other things are what makes up a pattern. ```rust #[derive(Drop)] @@ -46,7 +47,8 @@ fn specified_color(color: Color) -> felt252 { } ``` -The `match` expression also possesses the feature of binding to values that match the pattern. This makes it possible to extract the associated value of an enum variant. +The `match` expression also possesses the feature of binding to values that match the pattern. +This makes it possible to extract the associated value of an enum variant. ```rust #[derive(Drop, Debug)] @@ -79,13 +81,16 @@ fn main() { } ``` -Extending the `Red` variant of the `Color` enum to have an associated enum. With the help of the `match` expression, we could extract the associated value; `state`. This is simply a variable name which could be used afterwards in the code arm. +Extending the `Red` variant of the `Color` enum to have an associated enum. +With the help of the `match` expression, we could extract the associated value; `state`. +This is simply a variable name which could be used afterwards in the code arm. ## Matching the `Option` Enum Type The built-in `Option` enum can also be handled with the `match` expression. -Let's design a function that takes in an `Option` parameter. If there is a value from the provided parameter, add one to this value else the function should return the `None` value with no operation. +Let's design a function that takes in an `Option` parameter. +If there is a value from the provided parameter, add one to this value else the function should return the `None` value with no operation. ```rust fn add_one(x: Option) -> Option { @@ -106,9 +111,11 @@ fn main() { } ``` -> Note: `{:?}` is a special formatting syntax that allows to print a debug form of the parameter passed to the `println!` macro. You can find more information about it in [The Cairo Book](https://book.cairo-lang.org/appendix-03-derivable-traits.html#debug-for-printing-and-debugging). +> Note: `{:?}` is a special formatting syntax that allows to print a debug form of the parameter passed to the `println!` macro. +> You can find more information about it in [The Cairo Book][debugging]. -One benefit of using the `match` control flow is its exhaustive nature: the arms’ patterns must cover _all_ possibilities. Consider the version of the previous example that does not handle the `None` case: +One benefit of using the `match` control flow is its exhaustive nature: the arms’ patterns must cover _all_ possibilities. +Consider the version of the previous example that does not handle the `None` case: ```rust fn add_one(x: Option) -> Option { @@ -121,7 +128,8 @@ fn add_one(x: Option) -> Option { ## Catch-all Pattern with `_` Wildcard -Using enums, we can also take special actions for a few particular values, but for all other values perform one default action by adding a new arm with `_` as the pattern for the last arm of the match expression. The `_` pattern is a special pattern that matches any value and does not bind to that value. +Using enums, we can also take special actions for a few particular values, but for all other values perform one default action by adding a new arm with `_` as the pattern for the last arm of the match expression. +The `_` pattern is a special pattern that matches any value and does not bind to that value. The `is_red` function receives a parameter and return `true` if the color is `Red` and `false` for any other color: @@ -136,7 +144,8 @@ fn is_red(color: Color) -> bool { ## Pattern Matching with the `|` Operator -The Cairo `match` control flow construct allows for matching a value against multiple patterns using the `|` (_or_) operator. This helps simplify the code. +The Cairo `match` control flow construct allows for matching a value against multiple patterns using the `|` (_or_) operator. +This helps simplify the code. For example, using the `Color` enum, we want to write a function that returns `true` for `Red` or `Green` colors and `false` for other colors: @@ -151,13 +160,18 @@ fn is_red_or_green(color: Color) -> bool { ## Pattern Matching with `felt252` and Integer Variables -It is possible to match `felt252` and integer variables against a set of values. However, there are some constraints; +It is possible to match `felt252` and integer variables against a set of values. +However, there are some constraints; - Only support integers that fit into a single `felt252` (i.e `u256` is not supported). - 0 must be the first arm. - Each arm must follow a sequential segment, contiguously with other arms. -Let's implement a six-sided die tossing game. When this die is tossed, we get a number between 0 and 5. For a toss that yields 0, 1 or 2, you win. You roll again if you have 3. You loss for other values: +Let's implement a six-sided die tossing game. +When this die is tossed, we get a number between 0 and 5. +For a toss that yields 0, 1 or 2, you win. +You roll again if you have 3. +You loss for other values: ```rust fn toss(value: u8) { @@ -177,7 +191,8 @@ fn main() { ## Matching Tuples -Tuples can also be matched against a set of patterns. These patterns are designed similarly as a tuple. +Tuples can also be matched against a set of patterns. +These patterns are designed similarly as a tuple. ```rust #[derive(Drop)] @@ -204,3 +219,5 @@ fn vending_machine_accept(c: (DayType, Coin)) -> bool { ``` In the last match arm, it doesn't matter if we use `(_, _)` or just `_` wildcard to match any day and color combination, because they're functionally the same. + +[debugging]: https://book.cairo-lang.org/appendix-03-derivable-traits.html#debug-for-printing-and-debugging diff --git a/concepts/method-syntax/.meta/config.json b/concepts/method-syntax/.meta/config.json index 25336151..35b1d32b 100644 --- a/concepts/method-syntax/.meta/config.json +++ b/concepts/method-syntax/.meta/config.json @@ -1,7 +1,7 @@ { "blurb": "", "authors": [ - "0xNeshi" + "" ], "contributors": [] } diff --git a/concepts/mutability/about.md b/concepts/mutability/about.md index f3cceb89..3b560527 100644 --- a/concepts/mutability/about.md +++ b/concepts/mutability/about.md @@ -1,15 +1,21 @@ # Mutability -Cairo, with its blockchain-oriented design, implements an immutable memory model by default. This means that once a value is written into memory, it cannot be overwritten, only read. However, Cairo provides a mechanism to create mutable variables using the `mut` keyword. When a variable is declared as mutable, and the value is changed, the new value is written to a new memory cell, and the variable is updated to point to the new cell. This mechanism allows for flexibility while maintaining the benefits of immutability. +Cairo, with its blockchain-oriented design, implements an immutable memory model by default. +This means that once a value is written into memory, it cannot be overwritten, only read. +However, Cairo provides a mechanism to create mutable variables using the `mut` keyword. +When a variable is declared as mutable, and the value is changed, the new value is written to a new memory cell, and the variable is updated to point to the new cell. +This mechanism allows for flexibility while maintaining the benefits of immutability. -In the example below, `x` is declared as an immutable variable. If the value of `x` is changed, the compiler will throw an error. +In the example below, `x` is declared as an immutable variable. +If the value of `x` is changed, the compiler will throw an error. ```rust let x = 5; x = 2; // Error: cannot assign twice to immutable variable ``` -We can declare `x` as a mutable variable using the `mut` keyword. This allows the value of `x` to be changed after it is declared. +We can declare `x` as a mutable variable using the `mut` keyword. +This allows the value of `x` to be changed after it is declared. ```rust let mut x = 5; @@ -53,7 +59,9 @@ const ONE_HOUR_IN_SECONDS: u32 = consteval_int!(60 * 60); ## Shadowing -It's possible, and often idiomatic, to reuse a variable name as a particular value is transformed, by re-declaring it with multiple `let` invocations. This is known as _shadowing_. In effect, the second variable overshadows the first, taking any uses of the variable name to itself until either it itself is shadowed or the scope ends. +It's possible, and often idiomatic, to reuse a variable name as a particular value is transformed, by re-declaring it with multiple `let` invocations. +This is known as _shadowing_. +In effect, the second variable overshadows the first, taking any uses of the variable name to itself until either it itself is shadowed or the scope ends. ```rust fn main() { diff --git a/concepts/mutability/introduction.md b/concepts/mutability/introduction.md index b08fbf33..d0b7592f 100644 --- a/concepts/mutability/introduction.md +++ b/concepts/mutability/introduction.md @@ -1,3 +1,6 @@ # Introduction -Mutability is a property of a particular binding of a value to a variable. A variable is either immutable or mutable. An immutable variable's value may not be changed; a mutable variable's value can be changed. All variables are immutable by default. +Mutability is a property of a particular binding of a value to a variable. +A variable is either immutable or mutable. +An immutable variable's value may not be changed; a mutable variable's value can be changed. +All variables are immutable by default. diff --git a/concepts/operator-overloading/.meta/config.json b/concepts/operator-overloading/.meta/config.json index 25336151..35b1d32b 100644 --- a/concepts/operator-overloading/.meta/config.json +++ b/concepts/operator-overloading/.meta/config.json @@ -1,7 +1,7 @@ { "blurb": "", "authors": [ - "0xNeshi" + "" ], "contributors": [] } diff --git a/concepts/options/.meta/config.json b/concepts/options/.meta/config.json new file mode 100644 index 00000000..35b1d32b --- /dev/null +++ b/concepts/options/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "", + "authors": [ + "" + ], + "contributors": [] +} diff --git a/concepts/options/about.md b/concepts/options/about.md new file mode 100644 index 00000000..f607e58e --- /dev/null +++ b/concepts/options/about.md @@ -0,0 +1 @@ +# Options diff --git a/concepts/options/introduction.md b/concepts/options/introduction.md new file mode 100644 index 00000000..e10b99d0 --- /dev/null +++ b/concepts/options/introduction.md @@ -0,0 +1 @@ +# Introduction diff --git a/concepts/options/links.json b/concepts/options/links.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/concepts/options/links.json @@ -0,0 +1 @@ +[] diff --git a/concepts/ownership/about.md b/concepts/ownership/about.md index f43dbc7d..081c8a6c 100644 --- a/concepts/ownership/about.md +++ b/concepts/ownership/about.md @@ -2,7 +2,9 @@ In Cairo, the concept of ownership is attached to _variables_ rather than the _values_ they hold. -Values in Cairo are immutable, this immutability ensures that even if many variables refer to the same value, the value itself remains constant and unaltered. So the compiler must ensure their correct usage by the programmer. This is the meaning of variable _ownership_: the owner is the code that can read (and write if mutable) the variable. +Values in Cairo are immutable, this immutability ensures that even if many variables refer to the same value, the value itself remains constant and unaltered. +So the compiler must ensure their correct usage by the programmer. +This is the meaning of variable _ownership_: the owner is the code that can read (and write if mutable) the variable. ## Variable Ownership Rules @@ -14,7 +16,8 @@ Values in Cairo are immutable, this immutability ensures that even if many varia ## Variable Scope -A scope is the range within a program in which a variable is valid. Let's look at the following example that declares a variable `x` of unsigned integer type, with comments annotating where the variable is valid: +A scope is the range within a program in which a variable is valid. +Let's look at the following example that declares a variable `x` of unsigned integer type, with comments annotating where the variable is valid: ```rust fn main() { @@ -34,7 +37,8 @@ Notes: A move occurs when a value is passed to another function, destroying the original variable that referred to the value (thus making the variable useless), and creating a new variable to hold the same value. -Arrays are an example of a complex type that is moved when passing it to another function. Here is a short reminder of what an array looks like: +Arrays are an example of a complex type that is moved when passing it to another function. +Here is a short reminder of what an array looks like: ```rust let mut arr: Array = array![]; @@ -42,7 +46,8 @@ Arrays are an example of a complex type that is moved when passing it to another arr.append(2); ``` -How does the type system ensure that the Cairo program never tries to write to the same memory cell twice? Consider the following code, where we try to remove the front of the array twice: +How does the type system ensure that the Cairo program never tries to write to the same memory cell twice? +Consider the following code, where we try to remove the front of the array twice: ```rust fn foo(mut arr: Array) { @@ -56,13 +61,19 @@ fn main() { } ``` -In this case, we try to pass the same value (the array in the `arr` variable) to both function calls. This means our code tries to remove the first element twice, which would try to write to the same memory cell twice - which is forbidden by the Cairo VM, leading to a runtime error. Thankfully, this code does not actually compile. Once we have passed the array to the `foo` function, the variable `arr` is no longer usable. We get this compile-time error, telling us that we would need `Array` to implement the `Copy` trait: +In this case, we try to pass the same value (the array in the `arr` variable) to both function calls. +This means our code tries to remove the first element twice, which would try to write to the same memory cell twice - which is forbidden by the Cairo VM, leading to a runtime error. +Thankfully, this code does not actually compile. +Once we have passed the array to the `foo` function, the variable `arr` is no longer usable. +We get this compile-time error, telling us that we would need `Array` to implement the `Copy` trait: ## The Copy Trait -If a type implements the `Copy` trait, passing it to a function doesn't move the value but creates a new variable referring to the same value. This is a free operation because variables are a Cairo abstraction and values are always immutable. +If a type implements the `Copy` trait, passing it to a function doesn't move the value but creates a new variable referring to the same value. +This is a free operation because variables are a Cairo abstraction and values are always immutable. -To implement the `Copy` trait on your type, add the `#[derive(Copy)]` annotation to the type definition. However, Cairo won't allow this if the type or any of its components doesn't implement the `Copy` trait. +To implement the `Copy` trait on your type, add the `#[derive(Copy)]` annotation to the type definition. +However, Cairo won't allow this if the type or any of its components doesn't implement the `Copy` trait. ```rust #[derive(Copy, Drop)] @@ -83,7 +94,8 @@ fn foo(p: Point) { // do something with p ## Destroying Values -Linear types can also be utilized by being destroyed, which ensures that the associated resource is correctly released. This is accomplished by implementing or deriving the `Drop` trait, which signals to the compiler that the type can safely be destroyed once it's no longer useful. +Linear types can also be utilized by being destroyed, which ensures that the associated resource is correctly released. +This is accomplished by implementing or deriving the `Drop` trait, which signals to the compiler that the type can safely be destroyed once it's no longer useful. ```rust #[derive(Drop)] @@ -94,7 +106,9 @@ fn main() { } ``` -At the moment, the `Drop` implementation can be derived for all types, except for dictionaries (`Felt252Dict`) and types containing dictionaries. This is because dictionaries must be "squashed" when they are destroyed. For this, we derive the `Destruct` trait: +At the moment, the `Drop` implementation can be derived for all types, except for dictionaries (`Felt252Dict`) and types containing dictionaries. +This is because dictionaries must be "squashed" when they are destroyed. +For this, we derive the `Destruct` trait: ```rust #[derive(Destruct)] @@ -109,7 +123,8 @@ fn main() { ## Return Values and Scope -Returning values is equivalent to moving them. The example below shows an example of a function that returns some value: +Returning values is equivalent to moving them. +The example below shows an example of a function that returns some value: ```rust #[derive(Drop)] diff --git a/concepts/ownership/introduction.md b/concepts/ownership/introduction.md index 277e368a..11eecaf8 100644 --- a/concepts/ownership/introduction.md +++ b/concepts/ownership/introduction.md @@ -1,6 +1,7 @@ # Introduction -In Cairo, the concept of ownership applies to _variables_ and defines the rules governing who can use those variables. Each declared variable can only be used once, meaning that once a variable is used, it will either be destroyed or moved, depending on the type of usage. +In Cairo, the concept of ownership applies to _variables_ and defines the rules governing who can use those variables. +Each declared variable can only be used once, meaning that once a variable is used, it will either be destroyed or moved, depending on the type of usage. _Destruction_ can happen in several ways: diff --git a/concepts/packages-crates-modules/.meta/config.json b/concepts/packages-crates-modules/.meta/config.json index 25336151..35b1d32b 100644 --- a/concepts/packages-crates-modules/.meta/config.json +++ b/concepts/packages-crates-modules/.meta/config.json @@ -1,7 +1,7 @@ { "blurb": "", "authors": [ - "0xNeshi" + "" ], "contributors": [] } diff --git a/concepts/printing/.meta/config.json b/concepts/printing/.meta/config.json index 25336151..35b1d32b 100644 --- a/concepts/printing/.meta/config.json +++ b/concepts/printing/.meta/config.json @@ -1,7 +1,7 @@ { "blurb": "", "authors": [ - "0xNeshi" + "" ], "contributors": [] } diff --git a/concepts/references-and-snapshots/about.md b/concepts/references-and-snapshots/about.md index 8dc0198b..1e27952e 100644 --- a/concepts/references-and-snapshots/about.md +++ b/concepts/references-and-snapshots/about.md @@ -1,6 +1,7 @@ # References and Snapshots -When you pass a variable to a function, it's ownership is usually transferred, making the original variable unusable, this can be a bit tedious when accessing a state more than once. Cairo has two features for passing a value without destroying or moving it, called `references` and `snapshots`. +When you pass a variable to a function, it's ownership is usually transferred, making the original variable unusable, this can be a bit tedious when accessing a state more than once. +Cairo has two features for passing a value without destroying or moving it, called `references` and `snapshots`. ## Snapshots @@ -28,7 +29,8 @@ fn main() { ## Desnap Operator -To convert a snapshot back into a regular variable, you can use the desnap operator `*`, which serves as the opposite of the `@` operator. Only those types that implement the `Copy` trait can be desnapped. +To convert a snapshot back into a regular variable, you can use the desnap operator `*`, which serves as the opposite of the `@` operator. +Only those types that implement the `Copy` trait can be desnapped. ```rust #[derive(Drop)] @@ -52,7 +54,8 @@ fn calculate_area(rec: @Rectangle) -> u64 { ## Mutable References -Mutable references allow you to pass mutable values to a function and have them implicitly returned, maintaining ownership in the calling context. This can be achieved using the `ref` modifier, provided the variable is declared as mutable with the `mut` keyword. +Mutable references allow you to pass mutable values to a function and have them implicitly returned, maintaining ownership in the calling context. +This can be achieved using the `ref` modifier, provided the variable is declared as mutable with the `mut` keyword. ```rust fn foo(ref arr: Array) { // using `ref` marks the argument as a mutable reference diff --git a/concepts/references-and-snapshots/introduction.md b/concepts/references-and-snapshots/introduction.md index ccfc5316..0c71b0c3 100644 --- a/concepts/references-and-snapshots/introduction.md +++ b/concepts/references-and-snapshots/introduction.md @@ -1,3 +1,4 @@ # Introduction -When you pass a variable to a function, its ownership is transferred, making the original variable unusable. This can be a bit tedious when accessing a state more than once, but luckily Cairo has two features for passing values without destroying or moving them, called `references` and `snapshots`. +When you pass a variable to a function, its ownership is transferred, making the original variable unusable. +This can be a bit tedious when accessing a state more than once, but luckily Cairo has two features for passing values without destroying or moving them, called `references` and `snapshots`. diff --git a/concepts/smart-pointers/.meta/config.json b/concepts/smart-pointers/.meta/config.json index 25336151..35b1d32b 100644 --- a/concepts/smart-pointers/.meta/config.json +++ b/concepts/smart-pointers/.meta/config.json @@ -1,7 +1,7 @@ { "blurb": "", "authors": [ - "0xNeshi" + "" ], "contributors": [] } diff --git a/concepts/strings/about.md b/concepts/strings/about.md index edd9f96c..c6c23442 100644 --- a/concepts/strings/about.md +++ b/concepts/strings/about.md @@ -1,10 +1,11 @@ # String Types -Cairo does not have a native data type for strings. However, there are two ways to represent a string type: short strings using a single `felt252` and arbitrary length strings using `ByteArray`. +Cairo does not have a native data type for strings. +However, there are two ways to represent a string type: short strings using a single `felt252` and arbitrary length strings using `ByteArray`. ## Short strings -A short string is formed with single quotes where each character that makes up the string is encoded on one byte following the [ASCII standard](https://www.asciitable.com/). +A short string is formed with single quotes where each character that makes up the string is encoded on one byte following the [ASCII standard][ascii-table]. As an ASCII string, for instance, `'abc'` will be: @@ -13,7 +14,8 @@ As an ASCII string, for instance, `'abc'` will be: - `'c'` is equivalent to `0x63` - `'abc'` is equivalent to `0x616263` -The `felt252` data type holds short strings type in Cairo. As `felt252` contains 251 bits, a short string is limited to 31 characters (31 \* 8 = 248 bits, which is the maximum multiple of 8 that fits in 251 bits). +The `felt252` data type holds short strings type in Cairo. +As `felt252` contains 251 bits, a short string is limited to 31 characters (31 \* 8 = 248 bits, which is the maximum multiple of 8 that fits in 251 bits). Examples of short string declarations: @@ -27,13 +29,15 @@ Examples of short string declarations: ### ByteArray -The ByteArray struct was introduced in Cairo 2.4.0. The ByteArray strings are written in double quotes as follows: +The ByteArray struct was introduced in Cairo 2.4.0. +The ByteArray strings are written in double quotes as follows: ```rust let long_string: ByteArray = "this is a string which has more than 31 characters"; ``` -Technically, the update also introduced the `bytes31` type which holds 31 bytes and which fits into a felt. The `ByteArray` struct basically represents long string using an array of `bytes31` that hold the "whole words" (i.e. words that fill the whole felt) and a single felt field that holds an "incomplete word" (the number of bytes within are specified in separate length field). +Technically, the update also introduced the `bytes31` type which holds 31 bytes and which fits into a felt. +The `ByteArray` struct basically represents long string using an array of `bytes31` that hold the "whole words" (i.e. words that fill the whole felt) and a single felt field that holds an "incomplete word" (the number of bytes within are specified in separate length field). ```rust #[derive(Drop, Clone, PartialEq, Serde, Default)] @@ -43,3 +47,5 @@ struct ByteArray { pending_word_len: usize, } ``` + +[ascii-table]: https://www.asciitable.com/ diff --git a/concepts/strings/introduction.md b/concepts/strings/introduction.md index 65363258..3776101c 100644 --- a/concepts/strings/introduction.md +++ b/concepts/strings/introduction.md @@ -1,6 +1,6 @@ # Introduction -In Cairo, there's no native type for strings. Instead, you can use a single -`felt252` to store a short string of up to 31 characters, or a `ByteArray` -for strings of arbitrary length. Short strings use single quotes and -`ByteArray` uses double quotes. All characters must follow the ASCII standard. +In Cairo, there's no native type for strings. +Instead, you can use a single `felt252` to store a short string of up to 31 characters, or a `ByteArray` for strings of arbitrary length. +Short strings use single quotes and `ByteArray` uses double quotes. +All characters must follow the ASCII standard. diff --git a/concepts/structs/about.md b/concepts/structs/about.md index 6f1297eb..9f9d09a3 100644 --- a/concepts/structs/about.md +++ b/concepts/structs/about.md @@ -1,6 +1,7 @@ # Structs -Structs are custom data types that let you name and package together multiple related values. They are similar to tuples, but more flexible and let you name each piece of data, making your code clearer. +Structs are custom data types that let you name and package together multiple related values. +They are similar to tuples, but more flexible and let you name each piece of data, making your code clearer. Structs in Cairo have these properties: - Every struct type has a particular name. @@ -22,7 +23,8 @@ struct User { } ``` -To create an instance of a `struct`, specify the `struct` name and use curly braces with key-value pairs for the fields. Note that you can list the fields in any order when creating an instance: +To create an instance of a `struct`, specify the `struct` name and use curly braces with key-value pairs for the fields. +Note that you can list the fields in any order when creating an instance: ```rust let user1 = User { @@ -36,7 +38,8 @@ let user2 = User { ## Interacting with an Instance of a Struct -Use dot notation to access or modify the fields of a `struct` instance. Note that to modify a field, the instance must be mutable: +Use dot notation to access or modify the fields of a `struct` instance. +Note that to modify a field, the instance must be mutable: ```rust let mut user1 = User { @@ -60,7 +63,8 @@ fn build_user(email: ByteArray, username: ByteArray) -> User { ## Field Init Shorthand -In the last example it made sense to name the `email` and `username` function parameters with the same names as the fields in the `struct`, but having to repeat them when assigning them to struct fields is a bit tedious. Luckily, there is a convenient shorthand for initializing struct fields from variables with the same names: +In the last example it made sense to name the `email` and `username` function parameters with the same names as the fields in the `struct`, but having to repeat them when assigning them to struct fields is a bit tedious. +Luckily, there is a convenient shorthand for initializing struct fields from variables with the same names: ```rust fn build_user_short(email: ByteArray, username: ByteArray) -> User { @@ -70,7 +74,8 @@ fn build_user_short(email: ByteArray, username: ByteArray) -> User { ## Creating Instances from Other Instances with Struct Update Syntax -Use struct update syntax to create a new instance from an existing one, changing some of the fields while keeping the rest the same. This is achieved using the `..` operator. +Use struct update syntax to create a new instance from an existing one, changing some of the fields while keeping the rest the same. +This is achieved using the `..` operator. Without update syntax: @@ -89,4 +94,5 @@ With update syntax: let user2 = User { email: "another@example.com", ..user1 }; ``` -The `..user1` syntax copies the remaining fields from `user1` to `user2`. Note that this moves the data from `user1` to `user2`, so `user1` cannot be used afterwards if it contains non-copiable types. +The `..user1` syntax copies the remaining fields from `user1` to `user2`. +Note that this moves the data from `user1` to `user2`, so `user1` cannot be used afterwards if it contains non-copiable types. diff --git a/concepts/structs/introduction.md b/concepts/structs/introduction.md index 37ccb658..f9e05b14 100644 --- a/concepts/structs/introduction.md +++ b/concepts/structs/introduction.md @@ -1,3 +1,4 @@ # Introduction -A `struct`, or structure, is a custom data type that lets you package and name multiple related values that make up a meaningful group. If you’re familiar with an object-oriented language, a struct is like an object’s data attributes. +A `struct`, or structure, is a custom data type that lets you package and name multiple related values that make up a meaningful group. +If you’re familiar with an object-oriented language, a struct is like an object’s data attributes. diff --git a/concepts/traits/about.md b/concepts/traits/about.md index ed1434c1..68d62fc8 100644 --- a/concepts/traits/about.md +++ b/concepts/traits/about.md @@ -1,12 +1,17 @@ # Traits -A trait specifies a set of methods that a type can implement. Traits are useful for defining methods that should be shared between types. When traits are used with generic types, the defined methods can be called on particular type but also possible to call them on other data type. In an abstract manner, with the use of _trait bounds_, we can specify that the generic type must share certain behavior. +A trait specifies a set of methods that a type can implement. +Traits are useful for defining methods that should be shared between types. +When traits are used with generic types, the defined methods can be called on particular type but also possible to call them on other data type. +In an abstract manner, with the use of _trait bounds_, we can specify that the generic type must share certain behavior. > **Note:** Traits share similar feature to what is known as interfaces in other programming languages with some differences. ## Trait definition -Defining a trait helps identify the function signature that would make up the set of behaviors a type can implement in order to achieve a result. These methods form the behavior of a type. If we can call the same methods on different types, this implies that the involved types have same behavior. +Defining a trait helps identify the function signature that would make up the set of behaviors a type can implement in order to achieve a result. +These methods form the behavior of a type. +If we can call the same methods on different types, this implies that the involved types have same behavior. ```rust #[derive(Drop)] @@ -23,9 +28,14 @@ pub trait Summary { } ``` -Going by the `Summary` trait definition above, the `summarize` method is a behavior that can only be called by the `NewsArticle` struct type. If invoked on other type instances, it throws a compilation error. Declaring the trait with the `pub` keyword exposes the trait in this crate to other crates that intend to make use of the trait too. Notice also that we use a semicolon after declaring method signatures in traits. +Going by the `Summary` trait definition above, the `summarize` method is a behavior that can only be called by the `NewsArticle` struct type. +If invoked on other type instances, it throws a compilation error. +Declaring the trait with the `pub` keyword exposes the trait in this crate to other crates that intend to make use of the trait too. +Notice also that we use a semicolon after declaring method signatures in traits. -A trait could be defined with a generic argument which allows for a function to take in argument of any data type. This introduces a mode of flexibility to function signature to allow them handle varieties of data type. In the case of the `Summary` trait below, this has been redesigned to work for any data type so that when `summarize` method is invoked on them, it returns a ByteArray. +A trait could be defined with a generic argument which allows for a function to take in argument of any data type. +This introduces a mode of flexibility to function signature to allow them handle varieties of data type. +In the case of the `Summary` trait below, this has been redesigned to work for any data type so that when `summarize` method is invoked on them, it returns a ByteArray. ```rust // A trait for a generic type @@ -38,7 +48,9 @@ A trait can have more than one method in its body; each declared method signatur ## Trait implementation on a Type -After defining the function signatures that make up a trait, the compiler will enforce that an implementation is as well defined for types that adopt the trait. We have written a media `aggregator` library crate below that summarizes some data from instances of `NewsArticle` and `Tweet`. For the `NewsArticle` struct, we would use the headline, the author, and the location types to return the value for the `summarize` method from the `Summary` trait, while for the `Tweet` struct, we would use the username and the content, assuming that the tweet content has a 280 characters limitation. +After defining the function signatures that make up a trait, the compiler will enforce that an implementation is as well defined for types that adopt the trait. +We have written a media `aggregator` library crate below that summarizes some data from instances of `NewsArticle` and `Tweet`. +For the `NewsArticle` struct, we would use the headline, the author, and the location types to return the value for the `summarize` method from the `Summary` trait, while for the `Tweet` struct, we would use the username and the content, assuming that the tweet content has a 280 characters limitation. ```rust mod aggregator { @@ -99,7 +111,9 @@ fn main() { ## Default Implementations -Sometimes, a trait can be defined with a default implementation for some or all of the methods that makes up the trait. This is to avoid requiring an implementation of all methods on every type and better code readability. If any particular type requires a special implementation of a trait's method, it can override the default implementation. +Sometimes, a trait can be defined with a default implementation for some or all of the methods that makes up the trait. +This is to avoid requiring an implementation of all methods on every type and better code readability. +If any particular type requires a special implementation of a trait's method, it can override the default implementation. ```rust mod aggregator { @@ -159,7 +173,9 @@ fn main() { ## How to Use and Manage External Traits -Traits and implementations can be defined in an external module and used in other modules, as long as they are properly imported. Let's say we have a `ShapeGeometry` trait with the `boundary` method that must be implemented for both the `Circle` and `Rectangle` structs. Below is an example of how the trait and implementation is imported, managed and used across modules. +Traits and implementations can be defined in an external module and used in other modules, as long as they are properly imported. +Let's say we have a `ShapeGeometry` trait with the `boundary` method that must be implemented for both the `Circle` and `Rectangle` structs. +Below is an example of how the trait and implementation is imported, managed and used across modules. ```rust // Here T is an alias type which will be provided during implementation @@ -229,7 +245,10 @@ The implementation visibility for both the `CircleGeometry` and `RectangleGeomet ## Impl Aliases -Implementations can be aliased when imported, using the `impl` keyword. This helps when instantiating a generic implementation for concrete types. Let's define a trivial implementation of trait `Two` for all types that implement the `One` trait by simply adding twice the value of `One` and returning the summation. We can afterwards expose the `Two` implementation that works only for `u8` and `u128` types. +Implementations can be aliased when imported, using the `impl` keyword. +This helps when instantiating a generic implementation for concrete types. +Let's define a trivial implementation of trait `Two` for all types that implement the `One` trait by simply adding twice the value of `One` and returning the summation. +We can afterwards expose the `Two` implementation that works only for `u8` and `u128` types. ```rust trait Two { @@ -250,4 +269,6 @@ pub impl U8Two = one_based::TwoImpl; pub impl U128Two = one_based::TwoImpl; ``` -The generic implementation can be written in a private module and with the use of impl alias, we can instantiate the generic implementation of these two concrete types. The benefit of using a private and unexposed generic implementation is that it prevents code duplication across files. This as well keeps the public API simple and clean. +The generic implementation can be written in a private module and with the use of impl alias, we can instantiate the generic implementation of these two concrete types. +The benefit of using a private and unexposed generic implementation is that it prevents code duplication across files. +This as well keeps the public API simple and clean. diff --git a/concepts/traits/introduction.md b/concepts/traits/introduction.md index 6ab84303..f1a1d8d4 100644 --- a/concepts/traits/introduction.md +++ b/concepts/traits/introduction.md @@ -1,3 +1,5 @@ # Introduction -In Cairo, a trait is a series of methods that can be implemented by a data type. When a trait is implemented for a type, the methods defined in that trait can be called on instances of that type. A trait can be defined with generic types, defining behavior shared by multiple data types. +In Cairo, a trait is a series of methods that can be implemented by a data type. +When a trait is implemented for a type, the methods defined in that trait can be called on instances of that type. +A trait can be defined with generic types, defining behavior shared by multiple data types. diff --git a/concepts/tuples/about.md b/concepts/tuples/about.md index d0812202..12da13e6 100644 --- a/concepts/tuples/about.md +++ b/concepts/tuples/about.md @@ -1,10 +1,12 @@ # The Tuple Type -A tuple is a versatile and efficient compound data type in Cairo, allowing you to group multiple values of different types into a single entity. Tuples have a fixed length, meaning once they are declared, their size cannot change. +A tuple is a versatile and efficient compound data type in Cairo, allowing you to group multiple values of different types into a single entity. +Tuples have a fixed length, meaning once they are declared, their size cannot change. ## Creating Tuples -To create a tuple, you write a comma-separated list of values inside parentheses. Each position in the tuple has a specific type, and these types can differ from one another. +To create a tuple, you write a comma-separated list of values inside parentheses. +Each position in the tuple has a specific type, and these types can differ from one another. ```rust fn main() { @@ -12,7 +14,8 @@ fn main() { } ``` -A tuple can hold multiple values of different data types, including other tuples. For example: +A tuple can hold multiple values of different data types, including other tuples. +For example: ```rust fn main() { @@ -24,7 +27,8 @@ Here, `nested_tup` variable is a tuple containing two tuples, demonstrating that ## Destructuring Tuples -To access the individual values within a tuple, you can use pattern matching to destructure the tuple. This breaks the tuple into its constituent parts. +To access the individual values within a tuple, you can use pattern matching to destructure the tuple. +This breaks the tuple into its constituent parts. ```rust fn main() { @@ -50,9 +54,12 @@ fn main() { ## The Unit Type `()` -The unit type is a special type in Cairo that has only one value: `()`. It is represented by a tuple with no elements. The size of the unit type is always zero, and it is guaranteed not to exist in the compiled code. +The unit type is a special type in Cairo that has only one value: `()`. +It is represented by a tuple with no elements. +The size of the unit type is always zero, and it is guaranteed not to exist in the compiled code. -In Cairo, every expression returns a value, and when an expression returns nothing, it implicitly returns `()`. This can be useful for functions that need to return a value but have no meaningful value to return. +In Cairo, every expression returns a value, and when an expression returns nothing, it implicitly returns `()`. +This can be useful for functions that need to return a value but have no meaningful value to return. ## Named Tuple Type Declaration diff --git a/concepts/tuples/introduction.md b/concepts/tuples/introduction.md index ba06ad87..0351a64a 100644 --- a/concepts/tuples/introduction.md +++ b/concepts/tuples/introduction.md @@ -1,6 +1,8 @@ # Introduction -Tuples are a lightweight way to group a fixed set of arbitrary types of data together. Tuples can have an arbitrary number of elements. You can access the elements by destructuring. +Tuples are a lightweight way to group a fixed set of arbitrary types of data together. +Tuples can have an arbitrary number of elements. +You can access the elements by destructuring. ```rust fn main() { diff --git a/concepts/type-conversion/.meta/config.json b/concepts/type-conversion/.meta/config.json index 25336151..35b1d32b 100644 --- a/concepts/type-conversion/.meta/config.json +++ b/concepts/type-conversion/.meta/config.json @@ -1,7 +1,7 @@ { "blurb": "", "authors": [ - "0xNeshi" + "" ], "contributors": [] } diff --git a/config.json b/config.json index 1f49f496..05ec8a42 100644 --- a/config.json +++ b/config.json @@ -28,6 +28,7 @@ "exercises": { "concept": [ { +<<<<<<< HEAD "slug": "low-power-embedded-game", "name": "Low-power Embedded Game", "uuid": "f3b7ce44-1667-42b4-b792-401d36aee2f1", @@ -40,6 +41,15 @@ "name": "Lucian's Luscious Lasagna", "uuid": "f120ab92-f277-434c-ae38-a3bb86cb67bf", "concepts": ["functions"], +======= + "slug": "lucians-luscious-lasagna", + "name": "Lucian's Luscious Lasagna", + "uuid": "f120ab92-f277-434c-ae38-a3bb86cb67bf", + "concepts": [ + "functions", + "mutability" + ], +>>>>>>> refs/remotes/origin/main "prerequisites": [], "status": "beta" }, @@ -50,6 +60,217 @@ "concepts": ["booleans"], "prerequisites": ["functions"], "status": "wip" + }, + { + "slug": "cryptographer", + "name": "Cryptographer", + "uuid": "7d399f77-4b68-4fc8-9421-43fd24bd4dbe", + "concepts": [ + "felts" + ], + "prerequisites": [ + "functions" + ], + "status": "wip" + }, + { + "slug": "cars-assemble", + "name": "Cars Assemble", + "uuid": "57845c17-15e5-4c62-bbac-29ad633e9ebb", + "concepts": [ + "integers" + ], + "prerequisites": [ + "functions" + ], + "status": "wip" + }, + { + "slug": "welcome-to-tech-palace", + "name": "Welcome To Tech Palace!", + "uuid": "f8108950-9819-4540-8a3a-880d0778806c", + "concepts": [ + "strings" + ], + "prerequisites": [ + "functions" + ], + "status": "wip" + }, + { + "slug": "magician-in-training", + "name": "Magician in Training", + "uuid": "b1f29e1f-adb7-4e37-8101-a1819f1179fa", + "concepts": [ + "arrays" + ], + "prerequisites": [ + "integers" + ], + "status": "wip" + }, + { + "slug": "rpn-calculator", + "name": "RPN Calculator", + "uuid": "536d9f09-5910-4a26-93fd-2242667b0b87", + "concepts": [ + "control-flow" + ], + "prerequisites": [ + "arrays", + "enums" + ], + "status": "wip" + }, + { + "slug": "gross-store", + "name": "Gross Store", + "uuid": "e2ae76c7-379e-430d-92d6-9eaaa823acca", + "concepts": [ + "dictionaries" + ], + "prerequisites": [ + "control-flow" + ], + "status": "wip" + }, + { + "slug": "election-day", + "name": "Election Day", + "uuid": "29bc1a4a-f295-44db-be19-d8e63dc526dc", + "concepts": [ + "ownership", + "structs", + "references-and-snapshots" + ], + "prerequisites": [ + "dictionaries" + ], + "status": "wip" + }, + { + "slug": "red-vs-blue-darwin-style", + "name": "Red vs. Blue: Darwin Style", + "uuid": "30761b1c-0c9a-4c91-8d16-1adf8bc4a0dd", + "concepts": [ + "packages-crates-modules" + ], + "prerequisites": [ + "structs" + ], + "status": "wip" + }, + { + "slug": "health-statistics", + "name": "Health Statistics", + "uuid": "69389197-e1dc-43bf-865d-d9b58d59b4e7", + "concepts": [ + "method-syntax" + ], + "prerequisites": [ + "structs", + "references-and-snapshots" + ], + "status": "wip" + }, + { + "slug": "airport-robot", + "name": "Airport Robot", + "uuid": "f0c994d7-d183-4dfa-9048-b20be6f3bf2e", + "concepts": [ + "generics", + "traits" + ], + "prerequisites": [ + "method-syntax" + ], + "status": "wip" + }, + { + "slug": "low-power-embedded-game", + "name": "Low-power Embedded Game", + "uuid": "f3b7ce44-1667-42b4-b792-401d36aee2f1", + "concepts": [ + "tuples" + ], + "prerequisites": [ + "traits" + ], + "status": "wip" + }, + { + "slug": "the-realm-of-echoes", + "name": "the-realm-of-echoes", + "uuid": "c8fd3bd0-290e-4472-b967-9a201dfd3041", + "concepts": [ + "printing" + ], + "prerequisites": [ + "traits" + ], + "status": "wip" + }, + { + "slug": "role-playing-game", + "name": "Role Playing Game", + "uuid": "1b014752-c3a4-4250-9483-89696864461b", + "concepts": [ + "options" + ], + "prerequisites": [ + "structs", + "method-syntax" + ], + "status": "wip" + }, + { + "slug": "magical-measurements", + "name": "Magical Measurements", + "uuid": "590fc9b0-2b5d-4dab-81ca-b43f9987b1f5", + "concepts": [ + "type-conversion" + ], + "prerequisites": [ + "options", + "printing" + ], + "status": "wip" + }, + { + "slug": "the-farm", + "name": "The Farm", + "uuid": "e167e30c-84b1-44da-b21c-70bc46688f20", + "concepts": [ + "error-handling" + ], + "prerequisites": [ + "structs" + ], + "status": "wip" + }, + { + "slug": "chrono-realms", + "name": "Chrono Realms", + "uuid": "eb15461a-ffdc-4bdd-acc1-e95bd7a1ac91", + "concepts": [ + "operator-overloading" + ], + "prerequisites": [ + "generics" + ], + "status": "wip" + }, + { + "slug": "chrono-realms-time-tree", + "name": "Chrono Realms Time Tree", + "uuid": "a72139a1-825e-4349-a6b5-400b665fbe07", + "concepts": [ + "smart-pointers" + ], + "prerequisites": [ + "structs" + ], + "status": "wip" } ], "practice": [ @@ -474,6 +695,70 @@ "uuid": "78d31713-0d88-4821-a586-4b36c46eaa44", "practices": [], "prerequisites": [], + "difficulty": 1 + }, + { + "slug": "high-scores", + "name": "High Scores", + "uuid": "993e43d8-e31b-4dd5-a73d-f5e99b499207", + "practices": [], + "prerequisites": [], + "difficulty": 3 + }, + { + "slug": "sum-of-multiples", + "name": "Sum of Multiples", + "uuid": "60b963b1-5c4a-4eb5-8f8e-a5e1ba9faa2f", + "practices": [], + "prerequisites": [], + "difficulty": 1 + }, + { + "slug": "space-age", + "name": "Space Age", + "uuid": "2305b855-30e5-4a24-a1d2-abfc50db3394", + "practices": [], + "prerequisites": [], + "difficulty": 3 + }, + { + "slug": "series", + "name": "Series", + "uuid": "b7a0428f-af52-49a5-a768-65197b9dfaa9", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "minesweeper", + "name": "Minesweeper", + "uuid": "393d862e-2736-49e7-ad7a-cda819b897c9", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, + { + "slug": "kindergarten-garden", + "name": "Kindergarten Garden", + "uuid": "6e241be1-28ba-4658-a6de-a29f3471725d", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, + { + "slug": "luhn", + "name": "Luhn", + "uuid": "8b85fe9d-0964-453f-85d7-92acd2c81723", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "all-your-base", + "name": "All Your Base", + "uuid": "102e5a3f-c5bb-4b8a-acab-7bdbacec38fa", + "practices": [], + "prerequisites": [], "difficulty": 4 } ], @@ -564,6 +849,11 @@ "slug": "operator-overloading", "name": "Operator Overload" }, + { + "uuid": "0bd2a321-f169-49a9-83d6-7d91193db915", + "slug": "options", + "name": "Options" + }, { "uuid": "c11a8aa5-cda4-4d02-a477-3db3d3ca5acf", "slug": "ownership", diff --git a/docs/LEARNING.md b/docs/LEARNING.md index 0dfe9095..f4a49f7b 100644 --- a/docs/LEARNING.md +++ b/docs/LEARNING.md @@ -6,9 +6,9 @@ We think the best way to learn is to play and to practice, either by creating yo Below are some resources that might help you jumpstart your Cairo journey: -* [Cairo Programming Language](https://www.cairo-lang.org/) introduces the Cairo language and lists many relevant resources to learn and use Cairo. -* [The Cairo Book](https://book.cairo-lang.org/) is a great resource for learning Cairo as well as diving deeper into specific features of Cairo. -* [Cairo Documentation](https://docs.cairo-lang.org/) explains the language's constructs and semantics. -* [Cairo by Example](https://cairo-by-example.com/) shows you examples of the most common things you will be writing in Cairo. -* [Node Guardians](https://nodeguardians.io/campaigns?f=3%3D2) "A fantasy themed learning platform for Smart Contract developers." -* The [Cairo Community Forum](https://community.cairo-lang.org/) answers questions of all levels. +- [Cairo Programming Language](https://www.cairo-lang.org/) introduces the Cairo language and lists many relevant resources to learn and use Cairo. +- [The Cairo Book](https://book.cairo-lang.org/) is a great resource for learning Cairo as well as diving deeper into specific features of Cairo. +- [Cairo Documentation](https://docs.cairo-lang.org/) explains the language's constructs and semantics. +- [Cairo by Example](https://cairo-by-example.com/) shows you examples of the most common things you will be writing in Cairo. +- [Node Guardians](https://nodeguardians.io/campaigns?f=3%3D2) "A fantasy themed learning platform for Smart Contract developers." +- The [Cairo Community Forum](https://community.cairo-lang.org/) answers questions of all levels. diff --git a/docs/RESOURCES.md b/docs/RESOURCES.md index 55b19555..958a11b0 100644 --- a/docs/RESOURCES.md +++ b/docs/RESOURCES.md @@ -2,8 +2,8 @@ Cairo is particularly well-suited for use in a permissionless network like [Starknet](https://www.starknet.io/), where it can provide robust DoS protection and censorship resistance. It might be interesting to learn about this aspect of the language. Learning to create Starknet smart contracts will improve your mastery of Cairo as you would be solving real world problems with the language, and it would also introduce you to the world of Web3. -* [Starknet Documentation](https://docs.starknet.io/) learn what Starknet is, how it works and how to develop smart contracts on it. -* [Starknet by Example](https://starknet-by-example.voyager.online/) a collection of examples of how to use the Cairo programming language to create smart contracts on Starknet. -* [Starknet Basecamp](https://www.youtube.com/@starknet_foundation) "Basecamp is a free coding bootcamp specifically designed to teach developers how to create web3 apps on Starknet using Cairo." -* [Awesome Starknet](https://github.com/keep-starknet-strange/awesome-starknet) "A curated list of awesome StarkNet resources, libraries, tools and more". -* [Starklings](https://starklings.app/) "A web-based interactive tutorial to learn Cairo and Starknet." +- [Starknet Documentation](https://docs.starknet.io/) learn what Starknet is, how it works and how to develop smart contracts on it. +- [Starknet by Example](https://starknet-by-example.voyager.online/) a collection of examples of how to use the Cairo programming language to create smart contracts on Starknet. +- [Starknet Basecamp](https://www.youtube.com/@starknet_foundation) "Basecamp is a free coding bootcamp specifically designed to teach developers how to create web3 apps on Starknet using Cairo." +- [Awesome Starknet](https://github.com/keep-starknet-strange/awesome-starknet) "A curated list of awesome StarkNet resources, libraries, tools and more". +- [Starklings](https://starklings.app/) "A web-based interactive tutorial to learn Cairo and Starknet." diff --git a/exercises/concept/airport-robot/.docs/hints.md b/exercises/concept/airport-robot/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/airport-robot/.docs/instructions.md b/exercises/concept/airport-robot/.docs/instructions.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/airport-robot/.docs/introduction.md b/exercises/concept/airport-robot/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/airport-robot/.meta/config.json b/exercises/concept/airport-robot/.meta/config.json new file mode 100644 index 00000000..3eea6c7c --- /dev/null +++ b/exercises/concept/airport-robot/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/airport_robot.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "forked_from": [ + "go/airport-robot" + ], + "blurb": "" +} diff --git a/exercises/concept/airport-robot/.meta/design.md b/exercises/concept/airport-robot/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/airport-robot/.meta/exemplar.cairo b/exercises/concept/airport-robot/.meta/exemplar.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/airport-robot/Scarb.toml b/exercises/concept/airport-robot/Scarb.toml new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/airport-robot/src/lib.cairo b/exercises/concept/airport-robot/src/lib.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/airport-robot/tests/airport_robot.cairo b/exercises/concept/airport-robot/tests/airport_robot.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/annalyns-infiltration/.docs/hints.md b/exercises/concept/annalyns-infiltration/.docs/hints.md index f9893e08..a4495a86 100644 --- a/exercises/concept/annalyns-infiltration/.docs/hints.md +++ b/exercises/concept/annalyns-infiltration/.docs/hints.md @@ -2,7 +2,7 @@ ## General -- There are three logical operators '!` (logical NOT), '||` (logical OR), and `&&` (logical AND) when working with Boolean values. +- There are three logical operators '!`(logical NOT), '||` (logical OR), and `&&` (logical AND) when working with Boolean values. - Multiple operators can be combined in a single expression. ## 1. Check if a fast attack can be made diff --git a/exercises/concept/annalyns-infiltration/.docs/instructions.md b/exercises/concept/annalyns-infiltration/.docs/instructions.md index 9623f508..8bb10b90 100644 --- a/exercises/concept/annalyns-infiltration/.docs/instructions.md +++ b/exercises/concept/annalyns-infiltration/.docs/instructions.md @@ -11,7 +11,8 @@ It turns out there are two kidnappers: a mighty knight and a cunning archer. Having found the kidnappers, Annalyn considers which of the following actions she can engage in: - _Fast attack_: a fast attack can be made if the knight is sleeping, as it takes time for him to get his armor on, so he will be vulnerable. -- _Spy_: the group can be spied upon if at least one of them is awake. Otherwise, spying is a waste of time. +- _Spy_: the group can be spied upon if at least one of them is awake. + Otherwise, spying is a waste of time. - _Signal prisoner_: the prisoner can be signaled using bird sounds if the prisoner is awake and the archer is sleeping, as archers are trained in bird signaling so they could intercept the message. - _Free prisoner_: Annalyn can try sneaking into the camp to free the prisoner. This is a risky thing to do and can only succeed in one of two ways: @@ -72,4 +73,4 @@ let archer_awake = true; let prisoner_awake = false; let dog_present = false; can_free_prisoner(knight_awake, archer_awake, prisoner_awake, dog_present); // false -``` \ No newline at end of file +``` diff --git a/exercises/concept/annalyns-infiltration/.docs/introduction.md b/exercises/concept/annalyns-infiltration/.docs/introduction.md index 4af9f92b..94b90a00 100644 --- a/exercises/concept/annalyns-infiltration/.docs/introduction.md +++ b/exercises/concept/annalyns-infiltration/.docs/introduction.md @@ -13,4 +13,4 @@ Cairo supports three logical operators that can evaluate expressions down to Boo | ----------- | --------------------------------------------- | | `&&` (and) | It is true if both statements are true. | | `\|\|` (or) | It is true if at least one statement is true. | -| `!` (not) | It is true only if the statement is false. | \ No newline at end of file +| `!` (not) | It is true only if the statement is false. | diff --git a/exercises/concept/annalyns-infiltration/.meta/design.md b/exercises/concept/annalyns-infiltration/.meta/design.md new file mode 100644 index 00000000..324f54f1 --- /dev/null +++ b/exercises/concept/annalyns-infiltration/.meta/design.md @@ -0,0 +1,21 @@ +# Design + +## Learning objectives + +- Know of the existence of the `bool` type and its two values. +- Know about boolean operators and how to build logical expressions with them. +- Know of the boolean operator precedence rules. + +## Out of scope + +- Pattern matching on booleans. + +## Concepts + +- `booleans`: know of the existence of the `bool` type and its two values; know about boolean operators and how to build logical expressions with them; know of the boolean operator precedence rules. + +## Prerequisites + +This exercise's prerequisites Concepts are: + +- `functions` diff --git a/exercises/concept/cars-assemble/.docs/hints.md b/exercises/concept/cars-assemble/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/cars-assemble/.docs/instructions.md b/exercises/concept/cars-assemble/.docs/instructions.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/cars-assemble/.docs/introduction.md b/exercises/concept/cars-assemble/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/cars-assemble/.meta/config.json b/exercises/concept/cars-assemble/.meta/config.json new file mode 100644 index 00000000..84e6ee64 --- /dev/null +++ b/exercises/concept/cars-assemble/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/cars_assemble.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "forked_from": [ + "go/cars-assemble" + ], + "blurb": "Learn about numbers and type conversion by analyzing an assembly line in a car factory." +} diff --git a/exercises/concept/cars-assemble/.meta/design.md b/exercises/concept/cars-assemble/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/cars-assemble/.meta/exemplar.cairo b/exercises/concept/cars-assemble/.meta/exemplar.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/cars-assemble/Scarb.toml b/exercises/concept/cars-assemble/Scarb.toml new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/cars-assemble/src/lib.cairo b/exercises/concept/cars-assemble/src/lib.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/cars-assemble/tests/cars_assemble.cairo b/exercises/concept/cars-assemble/tests/cars_assemble.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/chrono-realms-time-tree/.docs/hints.md b/exercises/concept/chrono-realms-time-tree/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/chrono-realms-time-tree/.docs/instructions.md b/exercises/concept/chrono-realms-time-tree/.docs/instructions.md new file mode 100644 index 00000000..9522b7f4 --- /dev/null +++ b/exercises/concept/chrono-realms-time-tree/.docs/instructions.md @@ -0,0 +1,46 @@ +# Instructions + +In **ChronoRealms**, TimeKeepers often deal with not just trees of timelines, but **ChronoChains**-sequences of linked **TimeNodes**, each representing a specific moment in time. +A **ChronoChain** is a straight path of sequential moments, where each **TimeNode** connects to the next. +These **ChronoChains** are useful when traveling through a series of specific events, as they allow TimeKeepers to follow a single timeline. + +However, to handle these potentially long **ChronoChains**, TimeKeepers use **Smart Pointers (Box)** to safely manage and traverse these lists of moments without causing unnecessary memory duplication or overflow. +Each **TimeNode** holds a reference to the next node, forming a recursive structure. + +Your task as an apprentice is to implement a **ChronoChain** as a recursive list structure using smart pointers. + +In this exercise, you will: + +1. Create a recursive `ChronoChain` enum, representing a list of moments. +2. Use the `Box` smart pointer to store the recursive nodes. +3. Implement a function to create a **ChronoChain** from an array of `u32` values. +4. Implement a function to traverse the **ChronoChain** and sum up the values stored in the list. + +## 1. Define the Recursive `ChronoChain` Enum + +Create a recursive enum `ChronoChain` with two variants: + +- `End`: Represents the end of the list. +- `Link`: Contains a `u32` value and a boxed reference to the next node in the chain. + +## 2. Create a Function to Build a ChronoChain + +Write a function `build_chrono_chain` that takes an array of `u32` values and returns a **ChronoChain**, linking the values sequentially using smart pointers. + +## 3. Implement the Sum Function + +Write a function `sum_chain` to recursively traverse the **ChronoChain** and sum the values of all nodes. + +## Example Usage + +```rust +fn main() { + // Create a ChronoChain from an array of values + let chrono_chain = build_chrono_chain([10, 20, 30]); + + // Sum the values in the ChronoChain + let total_sum = sum_chain(&chrono_chain); + + println!("Total Time Power: {}", total_sum); +} +``` diff --git a/exercises/concept/chrono-realms-time-tree/.docs/introduction.md b/exercises/concept/chrono-realms-time-tree/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/chrono-realms-time-tree/.meta/config.json b/exercises/concept/chrono-realms-time-tree/.meta/config.json new file mode 100644 index 00000000..edd96af6 --- /dev/null +++ b/exercises/concept/chrono-realms-time-tree/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "0xNeshi" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/chrono_realms_time_tree.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "" +} diff --git a/exercises/concept/chrono-realms-time-tree/.meta/design.md b/exercises/concept/chrono-realms-time-tree/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/chrono-realms-time-tree/.meta/exemplar.cairo b/exercises/concept/chrono-realms-time-tree/.meta/exemplar.cairo new file mode 100644 index 00000000..f47658eb --- /dev/null +++ b/exercises/concept/chrono-realms-time-tree/.meta/exemplar.cairo @@ -0,0 +1,27 @@ +// Define the recursive ChronoChain enum +#[derive(Copy, Drop)] +pub enum ChronoChain { + End, + Link: (u32, Box), +} + +// Function to build a ChronoChain from an array of u32 values +pub fn build_chrono_chain(arr: Array) -> Box { + let mut chain = ChronoChain::End; + + // Iterate in reverse to build the chain from the end to the beginning + let mut span = arr.span(); + while let Option::Some(value) = span.pop_back() { + chain = ChronoChain::Link((*value, BoxTrait::new(chain))); + }; + + BoxTrait::new(chain) +} + +// Function to sum the values in the ChronoChain +pub fn sum_chain(chain: @ChronoChain) -> u32 { + match chain { + ChronoChain::End => 0, + ChronoChain::Link((value, next)) => *value + sum_chain(@(*next).unbox()), + } +} diff --git a/exercises/concept/chrono-realms-time-tree/Scarb.toml b/exercises/concept/chrono-realms-time-tree/Scarb.toml new file mode 100644 index 00000000..36f69878 --- /dev/null +++ b/exercises/concept/chrono-realms-time-tree/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "chrono_realms_time_tree" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.8.2" diff --git a/exercises/concept/chrono-realms-time-tree/src/lib.cairo b/exercises/concept/chrono-realms-time-tree/src/lib.cairo new file mode 100644 index 00000000..3a558df7 --- /dev/null +++ b/exercises/concept/chrono-realms-time-tree/src/lib.cairo @@ -0,0 +1,16 @@ +// Define the recursive ChronoChain enum +#[derive(Copy, Drop)] +pub enum ChronoChain { + End, + Link: (u32, Box), +} + +// Function to build a ChronoChain from an array of u32 values +pub fn build_chrono_chain(arr: Array) -> Box { + panic!("implement 'build_chrono_chain'") +} + +// Function to sum the values in the ChronoChain +pub fn sum_chain(chain: @ChronoChain) -> u32 { + panic!("implement 'sum_chain'") +} diff --git a/exercises/concept/chrono-realms-time-tree/tests/chrono_realms_time_tree.cairo b/exercises/concept/chrono-realms-time-tree/tests/chrono_realms_time_tree.cairo new file mode 100644 index 00000000..f5dbe78a --- /dev/null +++ b/exercises/concept/chrono-realms-time-tree/tests/chrono_realms_time_tree.cairo @@ -0,0 +1,38 @@ +use chrono_realms_time_tree::{ChronoChain, build_chrono_chain, sum_chain}; + +#[test] +fn test_build_chrono_chain() { + let chrono_chain = build_chrono_chain(array![10, 20, 30]); + // Verify the structure of the chain + if let ChronoChain::Link((first, next)) = chrono_chain.unbox() { + assert_eq!(first, 10); + + if let ChronoChain::Link((second, next)) = next.unbox() { + assert_eq!(second, 20); + + if let ChronoChain::Link((third, next)) = next.unbox() { + assert_eq!(third, 30); + + if let ChronoChain::Link(_) = next.unbox() { + panic!("Expected 4th elements to be ChronoChain::End"); + } + } + } + } +} + +#[test] +fn test_sum_chain() { + let chrono_chain = build_chrono_chain(array![10, 20, 30]); + let total_sum = sum_chain(@chrono_chain.unbox()); + assert_eq!(total_sum, 60); // 10 + 20 + 30 = 60 +} + +#[test] +fn test_empty_chain() { + // Empty chain should sum to 0 + let empty_chain = BoxTrait::new(ChronoChain::End); + let total_sum = sum_chain(@empty_chain.unbox()); + assert_eq!(total_sum, 0); +} + diff --git a/exercises/concept/chrono-realms/.docs/hints.md b/exercises/concept/chrono-realms/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/chrono-realms/.docs/instructions.md b/exercises/concept/chrono-realms/.docs/instructions.md new file mode 100644 index 00000000..ebb30269 --- /dev/null +++ b/exercises/concept/chrono-realms/.docs/instructions.md @@ -0,0 +1,38 @@ +# Instructions + +Welcome to the world of *ChronoRealms*, a magical land where powerful Timekeepers control the flow of time. +In this realm, Timekeepers manage "TimeShards," special units that can be added, subtracted, compared, and even checked for equality. +Your task is to help the Timekeepers by implementing custom operators for manipulating these TimeShards. + +The Timekeepers need you to create a custom struct `TimeShard` that supports the following operations: + +1. **Adding and Subtracting TimeShards**: Timekeepers need to be able to add and subtract TimeShards to control the timeline. +2. **Comparing TimeShards**: Sometimes, TimeShards need to be compared to see which one is larger, or if they are equal. +3. **Checking Equality**: Timekeepers need to check if two TimeShards are identical in value. + +Your mission is to implement the following traits for the `TimeShard` struct: + +1. **Addition (`Add`)**: Enable adding two `TimeShard` instances together. +2. **Subtraction (`Sub`)**: Enable subtracting one `TimeShard` from another. +3. **Equality (`PartialEq`)**: Allow Timekeepers to check if two `TimeShard` instances are the same. +4. **Comparison (`PartialOrd`)**: Allow comparison of two `TimeShard` instances to see which is greater or smaller. + +A `TimeShard` will be represented by a struct that holds an integer value (`i32`). +Implement the necessary traits to make the Timekeepers' job easier. + +```rust +let shard1 = TimeShard { value: 5 }; +let shard2 = TimeShard { value: 3 }; + +// Adding TimeShards +let sum = shard1 + shard2; // TimeShard { value: 8 } + +// Subtracting TimeShards +let difference = shard1 - shard2; // TimeShard { value: 2 } + +// Checking equality +let are_equal = shard1 == shard2; // false + +// Comparing TimeShards +let is_greater = shard1 > shard2; // true +``` diff --git a/exercises/concept/chrono-realms/.docs/introduction.md b/exercises/concept/chrono-realms/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/chrono-realms/.meta/config.json b/exercises/concept/chrono-realms/.meta/config.json new file mode 100644 index 00000000..f6c0413f --- /dev/null +++ b/exercises/concept/chrono-realms/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "0xNeshi" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/chrono_realms.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "" +} diff --git a/exercises/concept/chrono-realms/.meta/design.md b/exercises/concept/chrono-realms/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/chrono-realms/.meta/exemplar.cairo b/exercises/concept/chrono-realms/.meta/exemplar.cairo new file mode 100644 index 00000000..25643339 --- /dev/null +++ b/exercises/concept/chrono-realms/.meta/exemplar.cairo @@ -0,0 +1,28 @@ +use core::traits::{Add, Sub, PartialOrd}; + +// Define the TimeShard struct +#[derive(Drop, Debug, PartialEq)] +pub struct TimeShard { + pub value: i32, +} + +// Implement Add for TimeShard +impl TimeShardAdd of Add { + fn add(lhs: TimeShard, rhs: TimeShard) -> TimeShard { + TimeShard { value: lhs.value + rhs.value } + } +} + +// Implement Sub for TimeShard +impl TimeShardSub of Sub { + fn sub(lhs: TimeShard, rhs: TimeShard) -> TimeShard { + TimeShard { value: lhs.value - rhs.value } + } +} + +// Implement PartialEq is already derived, so no need for manual implementation +impl TimeShardPartialOrd of PartialOrd { + fn lt(lhs: TimeShard, rhs: TimeShard) -> bool { + lhs.value < rhs.value + } +} diff --git a/exercises/concept/chrono-realms/Scarb.toml b/exercises/concept/chrono-realms/Scarb.toml new file mode 100644 index 00000000..9819dcc7 --- /dev/null +++ b/exercises/concept/chrono-realms/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "chrono_realms" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.8.2" diff --git a/exercises/concept/chrono-realms/src/lib.cairo b/exercises/concept/chrono-realms/src/lib.cairo new file mode 100644 index 00000000..a16a9ec7 --- /dev/null +++ b/exercises/concept/chrono-realms/src/lib.cairo @@ -0,0 +1,28 @@ +use core::traits::{Add, Sub, PartialOrd}; + +// Define the TimeShard struct +#[derive(Drop, Debug, PartialEq)] +pub struct TimeShard { + pub value: i32, +} + +// Implement Add for TimeShard +impl TimeShardAdd of Add { + fn add(lhs: TimeShard, rhs: TimeShard) -> TimeShard { + panic!("implement 'Add::::add'") + } +} + +// Implement Sub for TimeShard +impl TimeShardSub of Sub { + fn sub(lhs: TimeShard, rhs: TimeShard) -> TimeShard { + panic!("implement 'Sub::::sub'") + } +} + +// Implement PartialEq is already derived, so no need for manual implementation +impl TimeShardPartialOrd of PartialOrd { + fn lt(lhs: TimeShard, rhs: TimeShard) -> bool { + panic!("implement 'PartialOrd::::lt'") + } +} diff --git a/exercises/concept/chrono-realms/tests/chrono_realms.cairo b/exercises/concept/chrono-realms/tests/chrono_realms.cairo new file mode 100644 index 00000000..ca0ea4ab --- /dev/null +++ b/exercises/concept/chrono-realms/tests/chrono_realms.cairo @@ -0,0 +1,82 @@ +use chrono_realms::TimeShard; + +#[test] +fn test_add_time_shards() { + let shard1 = TimeShard { value: 5 }; + let shard2 = TimeShard { value: 3 }; + let result = shard1 + shard2; + assert_eq!(result, TimeShard { value: 8 }); +} + +#[test] +#[ignore] +fn test_subtract_time_shards() { + let shard1 = TimeShard { value: 5 }; + let shard2 = TimeShard { value: 3 }; + let result = shard1 - shard2; + assert_eq!(result, TimeShard { value: 2 }); +} + +#[test] +#[ignore] +fn test_time_shard_equality() { + let shard1 = TimeShard { value: 5 }; + let shard2 = TimeShard { value: 5 }; + assert_eq!(shard1, shard2); +} + +#[test] +#[ignore] +fn test_time_shard_inequality() { + let shard1 = TimeShard { value: 5 }; + let shard2 = TimeShard { value: 3 }; + assert_ne!(shard1, shard2); +} + +#[test] +#[ignore] +fn test_time_shard_greater_than() { + let shard1 = TimeShard { value: 5 }; + let shard2 = TimeShard { value: 3 }; + assert!(shard1 > shard2); +} + +#[test] +#[ignore] +fn test_time_shard_greater_than_equal() { + let shard1 = TimeShard { value: 5 }; + let shard2 = TimeShard { value: 3 }; + assert!(shard1 >= shard2); +} + +#[test] +#[ignore] +fn test_time_shard_greater_than_equal_same_values() { + let shard1 = TimeShard { value: 5 }; + let shard2 = TimeShard { value: 5 }; + assert!(shard1 >= shard2); +} + +#[test] +#[ignore] +fn test_time_shard_less_than() { + let shard1 = TimeShard { value: 3 }; + let shard2 = TimeShard { value: 5 }; + assert!(shard1 < shard2); +} + +#[test] +#[ignore] +fn test_time_shard_less_than_equal() { + let shard1 = TimeShard { value: 3 }; + let shard2 = TimeShard { value: 5 }; + assert!(shard1 <= shard2); +} + +#[test] +#[ignore] +fn test_time_shard_less_than_equal_same_values() { + let shard1 = TimeShard { value: 5 }; + let shard2 = TimeShard { value: 5 }; + assert!(shard1 <= shard2); +} diff --git a/exercises/concept/cryptographer/.docs/hints.md b/exercises/concept/cryptographer/.docs/hints.md new file mode 100644 index 00000000..0d693f22 --- /dev/null +++ b/exercises/concept/cryptographer/.docs/hints.md @@ -0,0 +1,5 @@ +# Hints + +- Remember that [`felt252` operations are performed modulo $P$][felts]. + +[felts]: https://book.cairo-lang.org/ch02-02-data-types.html#felt-type diff --git a/exercises/concept/cryptographer/.docs/instructions.md b/exercises/concept/cryptographer/.docs/instructions.md new file mode 100644 index 00000000..93e314c3 --- /dev/null +++ b/exercises/concept/cryptographer/.docs/instructions.md @@ -0,0 +1,46 @@ +# Instructions + +In this exercise, you'll be assisting a cryptographer in managing and verifying a secure communication system. +The system relies heavily on field elements, specifically the `felt252` type in Cairo, to ensure that all arithmetic operations remain within a defined range. + +## 1. Calculate the Sum of Two Messages + +Two encrypted messages, each represented as a `felt252`, need to be added together to create a combined message. +However, because of the encryption, any result that exceeds the maximum value $P - 1$ must be wrapped around using modulo $P$. + +Implement a function that takes in two `felt252` values and returns their sum, ensuring the result stays within the valid range: + +```rust +add_messages(3618502788666131213697322783095070105623107215331596699973092056135007822949, 987654321) +// => 123456789 +``` + +> **Note:** The return value should be a `felt252`. + +## 2. Calculate the Difference Between Two Messages + +Sometimes, messages need to be subtracted to retrieve the original data. +If the result of the subtraction is negative, the system automatically adjusts the result by adding $P$ to bring it back within the range of a valid `felt252` value. + +Implement a function that takes in two `felt252` values and returns their difference, ensuring the result stays within the valid range: + +```rust +subtract_messages(123456789, 987654321) +// => 3618502788666131213697322783095070105623107215331596699973092056135007822949 +``` + +> **Note:** The return value should be a `felt252`. + +## 3. Verify the Integrity of a Multiplication Operation + +To ensure the integrity of encrypted data, the cryptographic system often multiplies two field elements (`felt252`). +The result must be computed modulo $P$ to ensure it remains within the valid range. + +Implement a function that takes in two `felt252` values and returns their product, ensuring the result stays within the valid range: + +```rust +verify_multiplication(382927772233671078546147897004315551461400594453673204174697055647611, 9449570) +// => 123456789 +``` + +> **Note:** The return value should be a `felt252`. diff --git a/exercises/concept/cryptographer/.docs/introduction.md b/exercises/concept/cryptographer/.docs/introduction.md new file mode 100644 index 00000000..0f863f67 --- /dev/null +++ b/exercises/concept/cryptographer/.docs/introduction.md @@ -0,0 +1,17 @@ +# Introduction + +In Cairo, one of the core concepts you'll encounter is the `felt252` type, also known as a **field element**. +This type represents integers within a specific range defined by a large prime number $P$. +In this exercise, you'll work exclusively with the `felt252` type, which is an essential part of Cairo programming. + +The `felt252` type in Cairo is a scalar type used to represent integers from 0 up to $P - 1$, where $P$ is a large prime number currently set to $( 2^{251} + 17 \cdot 2^{192} + 1 )$. +This type ensures that all arithmetic operations are performed modulo $P$, making it different from standard integer types you might encounter in other programming languages. + +Cairo provides support for basic arithmetic operations on `felt252` values: + +- **Addition (`+`)**: Adds two `felt252` values. + If the result exceeds $P$, it's reduced modulo $P$. +- **Subtraction (`-`)**: Subtracts one `felt252` value from another. + If the result is negative, it's adjusted to stay within the range by adding a multiple of $P$. +- **Multiplication (`*`)**: Multiplies two `felt252` values, with the result computed modulo $P$. +- **Division (`/`)**: Divides one `felt252` value by another, ensuring the result satisfies the equation $( \frac{x}{y} \cdot y = x \mod P)$. diff --git a/exercises/concept/cryptographer/.meta/config.json b/exercises/concept/cryptographer/.meta/config.json new file mode 100644 index 00000000..9e0b03c8 --- /dev/null +++ b/exercises/concept/cryptographer/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/cryptographer.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "" +} diff --git a/exercises/concept/cryptographer/.meta/design.md b/exercises/concept/cryptographer/.meta/design.md new file mode 100644 index 00000000..3e9ee7a9 --- /dev/null +++ b/exercises/concept/cryptographer/.meta/design.md @@ -0,0 +1,26 @@ +# Design + +## Goal + +The goal of this exercise is to teach the student how to work with `felt252` (field elements) in Cairo, with a focus on basic arithmetic operations and division. + +## Learning Objectives + +- Understand how addition and subtraction with field elements are performed within the range of a large prime number $P$. +- Learn how division works in the context of finite fields, ensuring the result satisfies the equation $(\frac{x}{y} \cdot y = x)$. + +## Out of Scope + +- Multiplication beyond basic field element multiplication. +- Handling more complex operations or edge cases involving $P$. + +## Concepts + +The Concepts this exercise unlocks are: + +- `felts` + +## Prerequisites + +- Basic understanding of data types in Cairo. +- Familiarity with modular arithmetic. diff --git a/exercises/concept/cryptographer/.meta/exemplar.cairo b/exercises/concept/cryptographer/.meta/exemplar.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/cryptographer/Scarb.toml b/exercises/concept/cryptographer/Scarb.toml new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/cryptographer/src/lib.cairo b/exercises/concept/cryptographer/src/lib.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/cryptographer/tests/cryptographer.cairo b/exercises/concept/cryptographer/tests/cryptographer.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/election-day/.docs/hints.md b/exercises/concept/election-day/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/election-day/.docs/instructions.md b/exercises/concept/election-day/.docs/instructions.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/election-day/.docs/introduction.md b/exercises/concept/election-day/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/election-day/.meta/config.json b/exercises/concept/election-day/.meta/config.json new file mode 100644 index 00000000..5fab9bf3 --- /dev/null +++ b/exercises/concept/election-day/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/election_day.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "forked_from": [ + "go/election-day" + ], + "blurb": "" +} diff --git a/exercises/concept/election-day/.meta/design.md b/exercises/concept/election-day/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/election-day/.meta/exemplar.cairo b/exercises/concept/election-day/.meta/exemplar.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/election-day/Scarb.toml b/exercises/concept/election-day/Scarb.toml new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/election-day/src/lib.cairo b/exercises/concept/election-day/src/lib.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/election-day/tests/election_day.cairo b/exercises/concept/election-day/tests/election_day.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/gross-store/.docs/hints.md b/exercises/concept/gross-store/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/gross-store/.docs/instructions.md b/exercises/concept/gross-store/.docs/instructions.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/gross-store/.docs/introduction.md b/exercises/concept/gross-store/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/gross-store/.meta/config.json b/exercises/concept/gross-store/.meta/config.json new file mode 100644 index 00000000..ecb5dee1 --- /dev/null +++ b/exercises/concept/gross-store/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/gross_store.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "forked_from": [ + "go/gross-store" + ], + "blurb": "" +} diff --git a/exercises/concept/gross-store/.meta/design.md b/exercises/concept/gross-store/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/gross-store/.meta/exemplar.cairo b/exercises/concept/gross-store/.meta/exemplar.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/gross-store/Scarb.toml b/exercises/concept/gross-store/Scarb.toml new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/gross-store/src/lib.cairo b/exercises/concept/gross-store/src/lib.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/gross-store/tests/gross_store.cairo b/exercises/concept/gross-store/tests/gross_store.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/health-statistics/.docs/hints.md b/exercises/concept/health-statistics/.docs/hints.md new file mode 100644 index 00000000..fc3c09fd --- /dev/null +++ b/exercises/concept/health-statistics/.docs/hints.md @@ -0,0 +1,36 @@ +# Hints + +## General + +## 1. Implement the `new()` method + +- The `new()` method receives the arguments we want to instantiate a `User` instance with. + It should return an instance of `User` with the specified name, age, and weight. + +- See [here][structs] for additional examples on defining and instantiating structs. + +## 2. Implement the getter methods + +- The `name()`, `age()`, and `weight()` methods are getters. + In other words, they are responsible for returning the corresponding field from a struct instance. + +- There's no need to use a `return` statement in Cairo unless you expressly want a function or method to return early. + Otherwise, it's more idiomatic to utilize an _implicit_ return by omitting the semicolon for the result we want a function or method to return. + It's not _wrong_ to use an explicit return, but it's cleaner to take advantage of implicit returns where possible. + +```rust +fn foo() -> i32 { + 1 +} +``` + +- See [here][methods] for some more examples of defining methods on structs. + +## 3. Implement the setter methods + +- The `set_age()` and `set_weight()` methods are setters, responsible for updating the corresponding field on a struct instance with the input argument. + +- As the signatures of these methods specify, the setter methods shouldn't return anything. + +[structs]: https://book.cairo-lang.org/ch05-01-defining-and-instantiating-structs.html +[methods]: https://book.cairo-lang.org/ch05-03-method-syntax.html diff --git a/exercises/concept/health-statistics/.docs/instructions.md b/exercises/concept/health-statistics/.docs/instructions.md new file mode 100644 index 00000000..acfeb949 --- /dev/null +++ b/exercises/concept/health-statistics/.docs/instructions.md @@ -0,0 +1,39 @@ +# Instructions + +You're working on implementing a health-monitoring system. +As part of that, you need to keep track of users' health statistics. + +You'll start with some stubbed functions in an `impl` block as well as the following struct definition: + +```rust +pub struct User { + name: String, + age: u32, + weight: u32, +} +``` + +Your goal is to implement the stubbed out methods on the `User` `struct` defined in the `impl` block. + +For example, the `new` method should return an instance of the `User` struct with the specified name, age, and weight values. + +```rust +let mut bob = User::new("Bob", 32, 72); +// Returns: a User with name "Bob", age 32, and weight 72 +``` + +The `weight` method should return the weight of the `User`. + +```rust +bob.weight(); +// Returns: 72 +``` + +The `set_age` method should set the age of the `User`. + +```rust +bob.set_age(33); +// Updates Bob's age to 33; happy birthday Bob! +``` + +Have fun! diff --git a/exercises/concept/health-statistics/.docs/introduction.md b/exercises/concept/health-statistics/.docs/introduction.md new file mode 100644 index 00000000..1c25f2fd --- /dev/null +++ b/exercises/concept/health-statistics/.docs/introduction.md @@ -0,0 +1,56 @@ +# Introduction + +It is often useful to group a collection of items together, and handle those groups as units. +In Cairo, we call such a group a struct, and each item one of the struct's fields. +A struct defines the general set of fields available, but a particular example of a struct is called an instance. + +Furthermore, structs can have methods defined on them, which have access to the fields. +The struct itself in that case is referred to as `self`. +When a method uses `ref self: SomeStruct`, the fields can be changed, or mutated. +When a method uses `self: SomeStruct` or `self: @SomeStruct`, the fields cannot be changed: they are immutable. +Controlling mutability helps the borrow-checker ensure that entire classes of concurrency bugs just don't happen in Cairo. + +In this exercise, you'll be implementing two kinds of methods on a struct. +The first are generally known as getters: they expose the struct's fields to the world, without letting anyone else mutate that value. + +You'll also be implementing methods of another type, generally known as setters. +These change the value of the field. +Setters aren't very common in Cairo - if a field can be freely modified, it is more common to just make it public - but they're useful if updating the field should have side effects. + +Structs are defined using the `struct` keyword, followed by the capitalized name of the type the struct is describing: + +```rust +struct Item {} +``` + +Additional types are then brought into the struct body as _fields_ of the struct, each with their own type: + +```rust +struct Item { + name: String, + weight: f32, + worth: u32, +} +``` + +A trait defines a set of methods that can be implemented by a type (we'll be focusing on structs here, but traits can be implemetned on enums as well). +These methods can be called on instances of the type when this trait is implemented. +Traits are defined using the `trait` keyword and within traits we define the method signatures we want our type to implement. + +```rust +trait ImplTrait { + // Define the method signature + fn new() -> Item; +} +``` + +Lastly, methods can be defined on structs inside of an `impl` block, which implements the defined trait: + +```rust +impl ItemImpl of ImplTrait { + // initializes and returns a new instance of our Item struct + fn new() -> Item { + Item {} + } +} +``` diff --git a/exercises/concept/health-statistics/.meta/config.json b/exercises/concept/health-statistics/.meta/config.json new file mode 100644 index 00000000..93f41f00 --- /dev/null +++ b/exercises/concept/health-statistics/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/health_statistics.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "forked_from": [ + "rust/health-statistics" + ], + "blurb": "Learn structs to store statistics for a health-monitoring system." +} diff --git a/exercises/concept/health-statistics/.meta/design.md b/exercises/concept/health-statistics/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/health-statistics/.meta/exemplar.cairo b/exercises/concept/health-statistics/.meta/exemplar.cairo new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/exercises/concept/health-statistics/.meta/exemplar.cairo @@ -0,0 +1 @@ + diff --git a/exercises/concept/health-statistics/Scarb.toml b/exercises/concept/health-statistics/Scarb.toml new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/health-statistics/src/lib.cairo b/exercises/concept/health-statistics/src/lib.cairo new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/exercises/concept/health-statistics/src/lib.cairo @@ -0,0 +1 @@ + diff --git a/exercises/concept/health-statistics/tests/health_statistics.cairo b/exercises/concept/health-statistics/tests/health_statistics.cairo new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/exercises/concept/health-statistics/tests/health_statistics.cairo @@ -0,0 +1 @@ + diff --git a/exercises/concept/low-power-embedded-game/.docs/hints.md b/exercises/concept/low-power-embedded-game/.docs/hints.md index 9f43c75c..1a762fb9 100644 --- a/exercises/concept/low-power-embedded-game/.docs/hints.md +++ b/exercises/concept/low-power-embedded-game/.docs/hints.md @@ -2,8 +2,8 @@ ## General -- [The Cairo Book: The Tuple Type](https://book.cairo-lang.org/ch02-02-data-types.html?highlight=tuple#the-tuple-type) -- [Starknet By Example: Tuples](https://starknet-by-example.voyager.online/getting-started/cairo_cheatsheet/tuples.html) +- [The Cairo Book: The Tuple Type][book-tuples] +- [Starknet By Example: Tuples][sbe-tuples] ## 1. Write a function `divmod` which returns the quotient and remainder of a division @@ -13,17 +13,15 @@ - Just chain together the suggested methods and everything will work out - A number `n` is even if `n % 2 == 0` -- A closure is a function with abbreviated syntax: the argument name(s) go - within a pair of `|` symbols, and the expression follows. Unlike normal - functions, it is not always necessary to explicitly state the type of each - argument, just the name. For example, here is how you can construct an iterator - of odd squares: `(0..).map(|n| 2 * n + 1).map(|n| n * n)`. +- A closure is a function with abbreviated syntax: the argument name(s) go within a pair of `|` symbols, and the expression follows. + Unlike normal functions, it is not always necessary to explicitly state the type of each argument, just the name. + For example, here is how you can construct an iterator of odd squares: `(0..).map(|n| 2 * n + 1).map(|n| n * n)`. ## 3. Implement a `manhattan` method on a `Position` tuple struct -- Don't worry about method syntax; just replacing the `todo` portion within the - `impl Position` block will do the right thing. -- Consider that some values within a `Position` may be negative, but a distance - is never negative. -- Calculating the absolute value will be supported natively in Cairo at some - point, we provided a helper trait you can use in the meantime +- Don't worry about method syntax; just replacing the `todo` portion within the `impl Position` block will do the right thing. +- Consider that some values within a `Position` may be negative, but a distance is never negative. +- Calculating the absolute value will be supported natively in Cairo at some point, we provided a helper trait you can use in the meantime + +[book-tuples]: https://book.cairo-lang.org/ch02-02-data-types.html?highlight=tuple#the-tuple-type +[sbe-tuples]: https://starknet-by-example.voyager.online/getting-started/cairo_cheatsheet/tuples.html diff --git a/exercises/concept/low-power-embedded-game/.docs/instructions.md b/exercises/concept/low-power-embedded-game/.docs/instructions.md index 2528b10f..5eec9fa0 100644 --- a/exercises/concept/low-power-embedded-game/.docs/instructions.md +++ b/exercises/concept/low-power-embedded-game/.docs/instructions.md @@ -50,7 +50,7 @@ type Position = (i16, i16); ``` You need to implement a method `manhattan` on `Position` which returns the -[manhattan distance](https://en.wikipedia.org/wiki/Taxicab_geometry) of that position from the origin (`let p: Position = (0, 0)`). +[manhattan distance][taxicab-geometry] of that position from the origin (`let p: Position = (0, 0)`). ```rust trait PositionTrait { @@ -64,3 +64,5 @@ Example: let p: Position = (3, 4); assert_eq!(p.manhattan(), 7); ``` + +[taxicab-geometry]: https://en.wikipedia.org/wiki/Taxicab_geometry diff --git a/exercises/concept/low-power-embedded-game/.docs/introduction.md b/exercises/concept/low-power-embedded-game/.docs/introduction.md index 6787d570..a21e3f51 100644 --- a/exercises/concept/low-power-embedded-game/.docs/introduction.md +++ b/exercises/concept/low-power-embedded-game/.docs/introduction.md @@ -1,8 +1,8 @@ # Introduction -Tuples are a lightweight way to group a fixed set of arbitrary types of data together. A tuple doesn't have -a particular name; naming a data structure turns it into a `struct`. A tuple's fields don't have names; -they are accessed by means of destructuring or by position. +Tuples are a lightweight way to group a fixed set of arbitrary types of data together. +A tuple doesn't have a particular name; naming a data structure turns it into a `struct`. +A tuple's fields don't have names; they are accessed by means of destructuring or by position. ## Creation @@ -21,8 +21,8 @@ Tuples can have an arbitrary number of elements. ## Access by destructuring -It is possible to access the elements of a tuple by destructuring. This just means assigning variable -names to the individual elements of the tuple, consuming it. +It is possible to access the elements of a tuple by destructuring. +This just means assigning variable names to the individual elements of the tuple, consuming it. ```rust let (elem1, _elem2) = two_element; diff --git a/exercises/concept/low-power-embedded-game/.meta/design.md b/exercises/concept/low-power-embedded-game/.meta/design.md index f9a85b5f..727e8a1b 100644 --- a/exercises/concept/low-power-embedded-game/.meta/design.md +++ b/exercises/concept/low-power-embedded-game/.meta/design.md @@ -21,6 +21,6 @@ Introduce the student to tuples and how to work with them. ## Resources to refer to -### Hints +- [Cairo Book - The Tuple Type][tuples] -- +[tuples]: https://book.cairo-lang.org/ch02-02-data-types.html#the-tuple-type diff --git a/exercises/concept/low-power-embedded-game/tests/low_power_embedded_game.cairo b/exercises/concept/low-power-embedded-game/tests/low_power_embedded_game.cairo index 922c26ee..e73de209 100644 --- a/exercises/concept/low-power-embedded-game/tests/low_power_embedded_game.cairo +++ b/exercises/concept/low-power-embedded-game/tests/low_power_embedded_game.cairo @@ -37,7 +37,6 @@ mod divmod { } mod evens { - use core::byte_array::ByteArrayTrait; use low_power_embedded_game::EvensTrait; #[test] diff --git a/exercises/concept/lucians-luscious-lasagna/.docs/hints.md b/exercises/concept/lucians-luscious-lasagna/.docs/hints.md index 947ff7f9..bdf8d651 100644 --- a/exercises/concept/lucians-luscious-lasagna/.docs/hints.md +++ b/exercises/concept/lucians-luscious-lasagna/.docs/hints.md @@ -3,7 +3,8 @@ ## General - An [integer value][integers] can be defined as one or more consecutive digits. -- If you see a `panic` error when running the tests, this is because you have not implemented one of the functions (it should say which one) or you have left the boilerplate in place. You need to remove the `panic!(...)` line from the supplied code and replace it with a real implementation. +- If you see a `panic` error when running the tests, this is because you have not implemented one of the functions (it should say which one) or you have left the boilerplate in place. + You need to remove the `panic!(...)` line from the supplied code and replace it with a real implementation. ## 1. Define the expected oven time in minutes diff --git a/exercises/concept/lucians-luscious-lasagna/.docs/instructions.md b/exercises/concept/lucians-luscious-lasagna/.docs/instructions.md index 0068e266..c03fb616 100644 --- a/exercises/concept/lucians-luscious-lasagna/.docs/instructions.md +++ b/exercises/concept/lucians-luscious-lasagna/.docs/instructions.md @@ -8,8 +8,8 @@ You have four tasks, all related to the time spent cooking the lasagna. ## 1. Define the expected oven time in minutes Define the `expected_minutes_in_oven` binding to check how many minutes the -lasagna should be in the oven. According to the cooking book, the expected -oven time in minutes is 40: +lasagna should be in the oven. +According to the cooking book, the expected oven time in minutes is 40: ```rust expected_minutes_in_oven() diff --git a/exercises/concept/lucians-luscious-lasagna/.docs/introduction.md b/exercises/concept/lucians-luscious-lasagna/.docs/introduction.md index daccd9a3..da3c534c 100644 --- a/exercises/concept/lucians-luscious-lasagna/.docs/introduction.md +++ b/exercises/concept/lucians-luscious-lasagna/.docs/introduction.md @@ -1,10 +1,12 @@ # Introduction -A function in Cairo allows you to group code into a reusable unit, making your programs more modular and easier to understand. Functions in Cairo consist of the `fn` keyword, followed by the function name, a list of parameters in parentheses, and a code block that defines what the function does. +A function in Cairo allows you to group code into a reusable unit, making your programs more modular and easier to understand. +Functions in Cairo consist of the `fn` keyword, followed by the function name, a list of parameters in parentheses, and a code block that defines what the function does. ## Function Parameters -In Cairo, all function parameters must be explicitly typed. Unlike some languages, Cairo does not infer types, and there are no default parameter values, so all parameters are required. +In Cairo, all function parameters must be explicitly typed. +Unlike some languages, Cairo does not infer types, and there are no default parameter values, so all parameters are required. ```rust // Function with no parameters @@ -35,7 +37,7 @@ print_greeting_name(123, 456) Function bodies are made up of a series of statements optionally ending in an expression. - Statements are instructions that perform some action and do not return a value. -- Expressions evaluate to a resultant value. +- Expressions evaluate to a resultant value. Creating a variable and assigning a value to it with the `let` keyword is a statement. @@ -49,7 +51,10 @@ Calling a function is an expression since it always evaluates to a value: the fu ## Return Values -Cairo functions can return values to the code that calls them. Return values are listed after the parameters, separated by an arrow `->`. In Cairo, the return value of the function is synonymous with the value of the final expression in the block of the body of a function. You can return early from a function by using the `return` keyword and specifying a value. +Cairo functions can return values to the code that calls them. +Return values are listed after the parameters, separated by an arrow `->`. +In Cairo, the return value of the function is synonymous with the value of the final expression in the block of the body of a function. +You can return early from a function by using the `return` keyword and specifying a value. ```rust // Function returning the last (and only) expression diff --git a/exercises/concept/lucians-luscious-lasagna/.meta/exemplar.cairo b/exercises/concept/lucians-luscious-lasagna/.meta/exemplar.cairo index 37c7a24c..2ec1331d 100644 --- a/exercises/concept/lucians-luscious-lasagna/.meta/exemplar.cairo +++ b/exercises/concept/lucians-luscious-lasagna/.meta/exemplar.cairo @@ -1,15 +1,21 @@ +const EXPECTED_MINUTES_IN_OVEN: u32 = 40; + pub fn expected_minutes_in_oven() -> u32 { - 40 + EXPECTED_MINUTES_IN_OVEN } pub fn remaining_minutes_in_oven(actual_minutes_in_oven: u32) -> u32 { - expected_minutes_in_oven() - actual_minutes_in_oven + let remaining_minutes = expected_minutes_in_oven() - actual_minutes_in_oven; + remaining_minutes } pub fn preparation_time_in_minutes(number_of_layers: u32) -> u32 { - number_of_layers * 2 + let number_of_layers = number_of_layers * 2; + number_of_layers } pub fn elapsed_time_in_minutes(number_of_layers: u32, actual_minutes_in_oven: u32) -> u32 { - preparation_time_in_minutes(number_of_layers) + actual_minutes_in_oven + let mut elapsed_time = preparation_time_in_minutes(number_of_layers); + elapsed_time += actual_minutes_in_oven; + elapsed_time } diff --git a/exercises/concept/magical-measurements/.docs/hints.md b/exercises/concept/magical-measurements/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magical-measurements/.docs/instructions.md b/exercises/concept/magical-measurements/.docs/instructions.md new file mode 100644 index 00000000..21ee292a --- /dev/null +++ b/exercises/concept/magical-measurements/.docs/instructions.md @@ -0,0 +1,60 @@ +# Instructions + +Astrid, an adventurous alchemist, is trying to standardize the magical units of measurement she collects from different realms. +Some realms use signed measurements (which can be negative), while others use unsigned ones (which are always positive). +On top of that, Astrid has two custom types for certain rare ingredients, and she needs a way to easily convert between these types. + +Your mission is to help Astrid convert between signed and unsigned integers, and create custom type conversions for her special ingredient types using Cairo’s `Into` and `TryInto` traits. + +## 1. Converting Magical Units Between Signed and Unsigned + +Astrid has measurements that sometimes come in positive or negative values (signed integers), but for certain tasks, she needs to ensure the values are always positive (unsigned integers). + +Help Astrid by writing a function that converts: + +1. A signed integer (`i32`) to an unsigned integer (`u16`). +2. An unsigned integer (`u16`) to a signed integer (`i32`). + +```rust +let signed_value: i32 = -42; +match convert_signed_to_unsigned(signed_value) { + Option::Some(v) => println!("Converted to unsigned: {}", v), + Option::None => println!("Failed to convert: {}", signed_value), +} // prints "Failed to convert: -42" + +let unsigned_value: u16 = 42; +let signed_value: i32 = convert_unsigned_to_signed(unsigned_value); +println!("Converted to signed: {}", signed_value); // prints "Converted to signed: 42" +``` + +## 2. Custom Ingredient Types Conversion + +Astrid works with two rare ingredients, **Essence** and **Elixir**, which are represented by custom types. +She wants to be able to convert **Essence** to **Elixir**, but not the other way around. + +Help Astrid by creating two custom types, `Essence` and `Elixir`, and implementing the `Into` trait to convert from `Essence` to `Elixir`. + +Write a function that accepts an `Essence` and converts it into an `Elixir` using `.into()`. + +```rust +#[derive(Drop)] +struct Essence { + potency: u16, +} + +#[derive(Drop)] +struct Elixir { + strength: u16, +} + +fn convert_essence_to_elixir(essence: Essence) -> Elixir { + // Convert the Essence to Elixir using the Into trait. + essence.into() +} + +fn main() { + let essence = Essence { potency: 100 }; + let elixir: Elixir = convert_essence_to_elixir(essence); + println!("Elixir strength: {}", elixir.strength); // Elixir strength: 100 +} +``` diff --git a/exercises/concept/magical-measurements/.docs/introduction.md b/exercises/concept/magical-measurements/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magical-measurements/.meta/config.json b/exercises/concept/magical-measurements/.meta/config.json new file mode 100644 index 00000000..616e633c --- /dev/null +++ b/exercises/concept/magical-measurements/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/magical_measurements.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "" +} diff --git a/exercises/concept/magical-measurements/.meta/design.md b/exercises/concept/magical-measurements/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magical-measurements/.meta/exemplar.cairo b/exercises/concept/magical-measurements/.meta/exemplar.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magical-measurements/Scarb.toml b/exercises/concept/magical-measurements/Scarb.toml new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magical-measurements/src/lib.cairo b/exercises/concept/magical-measurements/src/lib.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magical-measurements/tests/magical_measurements.cairo b/exercises/concept/magical-measurements/tests/magical_measurements.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magician-in-training/.docs/hints.md b/exercises/concept/magician-in-training/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magician-in-training/.docs/instructions.md b/exercises/concept/magician-in-training/.docs/instructions.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magician-in-training/.docs/introduction.md b/exercises/concept/magician-in-training/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magician-in-training/.meta/config.json b/exercises/concept/magician-in-training/.meta/config.json new file mode 100644 index 00000000..6c45a885 --- /dev/null +++ b/exercises/concept/magician-in-training/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/magician_in_training.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "forked_from": [ + "gleam/magician-in-training" + ], + "blurb": "" +} diff --git a/exercises/concept/magician-in-training/.meta/design.md b/exercises/concept/magician-in-training/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magician-in-training/.meta/exemplar.cairo b/exercises/concept/magician-in-training/.meta/exemplar.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magician-in-training/Scarb.toml b/exercises/concept/magician-in-training/Scarb.toml new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magician-in-training/src/lib.cairo b/exercises/concept/magician-in-training/src/lib.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/magician-in-training/tests/magician_in_training.cairo b/exercises/concept/magician-in-training/tests/magician_in_training.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/red-vs-blue-darwin-style/.docs/hints.md b/exercises/concept/red-vs-blue-darwin-style/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/red-vs-blue-darwin-style/.docs/instructions.md b/exercises/concept/red-vs-blue-darwin-style/.docs/instructions.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/red-vs-blue-darwin-style/.docs/introduction.md b/exercises/concept/red-vs-blue-darwin-style/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/red-vs-blue-darwin-style/.meta/config.json b/exercises/concept/red-vs-blue-darwin-style/.meta/config.json new file mode 100644 index 00000000..5e83bb87 --- /dev/null +++ b/exercises/concept/red-vs-blue-darwin-style/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/red_vs_blue_darwin_style.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "forked_from": [ + "csharp/red-vs-blue-darwin-style" + ], + "blurb": "" +} diff --git a/exercises/concept/red-vs-blue-darwin-style/.meta/design.md b/exercises/concept/red-vs-blue-darwin-style/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/red-vs-blue-darwin-style/.meta/exemplar.cairo b/exercises/concept/red-vs-blue-darwin-style/.meta/exemplar.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/red-vs-blue-darwin-style/Scarb.toml b/exercises/concept/red-vs-blue-darwin-style/Scarb.toml new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/red-vs-blue-darwin-style/src/lib.cairo b/exercises/concept/red-vs-blue-darwin-style/src/lib.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/red-vs-blue-darwin-style/tests/red_vs_blue_darwin_style.cairo b/exercises/concept/red-vs-blue-darwin-style/tests/red_vs_blue_darwin_style.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/role-playing-game/.docs/hints.md b/exercises/concept/role-playing-game/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/role-playing-game/.docs/instructions.md b/exercises/concept/role-playing-game/.docs/instructions.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/role-playing-game/.docs/introduction.md b/exercises/concept/role-playing-game/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/role-playing-game/.meta/config.json b/exercises/concept/role-playing-game/.meta/config.json new file mode 100644 index 00000000..0c8070dc --- /dev/null +++ b/exercises/concept/role-playing-game/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/role_playing_game.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "forked_from": [ + "rust/role-playing-game" + ], + "blurb": "" +} diff --git a/exercises/concept/role-playing-game/.meta/design.md b/exercises/concept/role-playing-game/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/role-playing-game/.meta/exemplar.cairo b/exercises/concept/role-playing-game/.meta/exemplar.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/role-playing-game/Scarb.toml b/exercises/concept/role-playing-game/Scarb.toml new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/role-playing-game/src/lib.cairo b/exercises/concept/role-playing-game/src/lib.cairo new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/exercises/concept/role-playing-game/src/lib.cairo @@ -0,0 +1 @@ + diff --git a/exercises/concept/role-playing-game/tests/role_playing_game.cairo b/exercises/concept/role-playing-game/tests/role_playing_game.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/rpn-calculator/.docs/hints.md b/exercises/concept/rpn-calculator/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/rpn-calculator/.docs/instructions.md b/exercises/concept/rpn-calculator/.docs/instructions.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/rpn-calculator/.docs/introduction.md b/exercises/concept/rpn-calculator/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/rpn-calculator/.meta/config.json b/exercises/concept/rpn-calculator/.meta/config.json new file mode 100644 index 00000000..b8399302 --- /dev/null +++ b/exercises/concept/rpn-calculator/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/rpn_calculator.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "forked_from": [ + "rust/rpn-calculator" + ], + "blurb": "Use some of `Array`'s methods to evaluate Reverse Polish notation" +} diff --git a/exercises/concept/rpn-calculator/.meta/design.md b/exercises/concept/rpn-calculator/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/rpn-calculator/.meta/exemplar.cairo b/exercises/concept/rpn-calculator/.meta/exemplar.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/rpn-calculator/Scarb.toml b/exercises/concept/rpn-calculator/Scarb.toml new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/rpn-calculator/src/lib.cairo b/exercises/concept/rpn-calculator/src/lib.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/rpn-calculator/tests/rpn_calculator.cairo b/exercises/concept/rpn-calculator/tests/rpn_calculator.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/the-farm/.docs/hints.md b/exercises/concept/the-farm/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/the-farm/.docs/instructions.md b/exercises/concept/the-farm/.docs/instructions.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/the-farm/.docs/introduction.md b/exercises/concept/the-farm/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/the-farm/.meta/config.json b/exercises/concept/the-farm/.meta/config.json new file mode 100644 index 00000000..2f288353 --- /dev/null +++ b/exercises/concept/the-farm/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/the_farm.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "forked_from": [ + "go/the-farm" + ], + "blurb": "" +} diff --git a/exercises/concept/the-farm/.meta/design.md b/exercises/concept/the-farm/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/the-farm/.meta/exemplar.cairo b/exercises/concept/the-farm/.meta/exemplar.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/the-farm/Scarb.toml b/exercises/concept/the-farm/Scarb.toml new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/the-farm/src/lib.cairo b/exercises/concept/the-farm/src/lib.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/the-farm/tests/the_farm.cairo b/exercises/concept/the-farm/tests/the_farm.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/the-realm-of-echoes/.docs/hints.md b/exercises/concept/the-realm-of-echoes/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/the-realm-of-echoes/.docs/instructions.md b/exercises/concept/the-realm-of-echoes/.docs/instructions.md new file mode 100644 index 00000000..8a248a7e --- /dev/null +++ b/exercises/concept/the-realm-of-echoes/.docs/instructions.md @@ -0,0 +1,52 @@ +# Instructions + +In the magical world of Echoes, there exists a powerful spell called "EchoSpeak". +This spell allows wizards to communicate their thoughts and observations through spoken words, which are written out by magical quills. +To master this spell, wizards must learn how to formulate their words and thoughts clearly and concisely, whether it's about numbers, objects, or complex magical constructs. + +Your task as a Wizard's Apprentice is to master the art of EchoSpeak. +This requires you to learn how to print various types of magical data to the enchanted scrolls. +EchoSpeak works by taking a special magical ink, represented by the `ByteArray`, and inscribing formatted messages to the scroll using spells like `println!` and `print!`. + +However, when dealing with custom magical constructs like enchanted items or spells, EchoSpeak needs a special incantation to understand how to represent these objects. +That's where your magic comes in - learning to implement special spells known as `traits` for custom magical items. + +## 1. Formatting Magical Words + +Sometimes, wizards need to format their magical words before inscribing them on scrolls. +You'll write a function that formats three magical words into a single message. + +Implement a function called `format_magical_chant` that returns a formatted string in the form of "chant1-chant2-chant3". + +Example: + +```rust +let result = format_magical_chant("Spark", "Shine", "Glow"); +assert_eq!(result, "Spark-Shine-Glow"); +``` + +## 2. Echoing Custom Enchanted Items + +The realm of Echoes is filled with enchanted items. +One such item is the EchoStone, which contains two magical attributes: power and duration. +Your task is to implement the `stringify_echo_stone` function that converts an EchoStone into a `ByteArray` in a special format `"EchoStone [power: x, duration: y]"` by implementing the `Display` trait. + +Example: + +```rust +let stone = EchoStone { power: 100, duration: 300 }; +assert_eq!(stringify(stone), "EchoStone [power: 100, duration: 300]"); +``` + +## 3. Debugging the Magic + +As a wizard apprentice, debugging is essential for complex magical constructs. +You will create a function that prints an `EchoStone` for debugging using the `Debug` trait. +The format should be: `"Debugging EchoStone: { power: x, duration: y }"`. + +Example: + +```rust +let stone = EchoStone { power: 25, duration: 10 }; +assert_eq!(format!("{:?}", stone), "Debugging EchoStone: { power: 25, duration: 10 }"); +``` diff --git a/exercises/concept/the-realm-of-echoes/.docs/introduction.md b/exercises/concept/the-realm-of-echoes/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/the-realm-of-echoes/.meta/config.json b/exercises/concept/the-realm-of-echoes/.meta/config.json new file mode 100644 index 00000000..8af744c3 --- /dev/null +++ b/exercises/concept/the-realm-of-echoes/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "0xNeshi" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/the_realm_of_echoes.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "" +} diff --git a/exercises/concept/the-realm-of-echoes/.meta/design.md b/exercises/concept/the-realm-of-echoes/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/the-realm-of-echoes/.meta/exemplar.cairo b/exercises/concept/the-realm-of-echoes/.meta/exemplar.cairo new file mode 100644 index 00000000..ece7a592 --- /dev/null +++ b/exercises/concept/the-realm-of-echoes/.meta/exemplar.cairo @@ -0,0 +1,23 @@ +use core::fmt::{Debug, Display, Formatter, Error}; + +pub fn format_magical_chant(chant1: ByteArray, chant2: ByteArray, chant3: ByteArray) -> ByteArray { + format!("{chant1}-{chant2}-{chant3}") +} + +#[derive(Drop)] +pub struct EchoStone { + pub power: u32, + pub duration: u32, +} + +impl EchoStoneDisplay of Display { + fn fmt(self: @EchoStone, ref f: Formatter) -> Result<(), Error> { + write!(f, "EchoStone [power: {}, duration: {}]", *self.power, *self.duration) + } +} + +impl EchoStoneDebug of Debug { + fn fmt(self: @EchoStone, ref f: Formatter) -> Result<(), Error> { + write!(f, "Debugging EchoStone: {{ power: {}, duration: {} }}", *self.power, *self.duration) + } +} diff --git a/exercises/concept/the-realm-of-echoes/Scarb.toml b/exercises/concept/the-realm-of-echoes/Scarb.toml new file mode 100644 index 00000000..55e1060d --- /dev/null +++ b/exercises/concept/the-realm-of-echoes/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "the_realm_of_echoes" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.8.2" diff --git a/exercises/concept/the-realm-of-echoes/src/lib.cairo b/exercises/concept/the-realm-of-echoes/src/lib.cairo new file mode 100644 index 00000000..24349bbd --- /dev/null +++ b/exercises/concept/the-realm-of-echoes/src/lib.cairo @@ -0,0 +1,23 @@ +use core::fmt::{Debug, Display, Formatter, Error}; + +pub fn format_magical_chant(chant1: ByteArray, chant2: ByteArray, chant3: ByteArray) -> ByteArray { + panic!("implement 'format_magical_chant'") +} + +#[derive(Drop)] +pub struct EchoStone { + pub power: u32, + pub duration: u32, +} + +impl EchoStoneDisplay of Display { + fn fmt(self: @EchoStone, ref f: Formatter) -> Result<(), Error> { + panic!("implement 'Display::::fmt'") + } +} + +impl EchoStoneDebug of Debug { + fn fmt(self: @EchoStone, ref f: Formatter) -> Result<(), Error> { + panic!("implement 'Debug::::fmt'") + } +} diff --git a/exercises/concept/the-realm-of-echoes/tests/the_realm_of_echoes.cairo b/exercises/concept/the-realm-of-echoes/tests/the_realm_of_echoes.cairo new file mode 100644 index 00000000..69e31eab --- /dev/null +++ b/exercises/concept/the-realm-of-echoes/tests/the_realm_of_echoes.cairo @@ -0,0 +1,21 @@ +use the_realm_of_echoes::{EchoStone, format_magical_chant}; + +#[test] +fn test_format_magical_chant() { + let result = format_magical_chant("Spark", "Shine", "Glow"); + assert_eq!(result, "Spark-Shine-Glow"); +} + +#[test] +#[ignore] +fn test_stringify_echo_stone() { + let stone = EchoStone { power: 100, duration: 300 }; + assert_eq!(format!("{stone}"), "EchoStone [power: 100, duration: 300]"); +} + +#[test] +#[ignore] +fn test_debug_echo_stone() { + let stone = EchoStone { power: 100, duration: 300 }; + assert_eq!(format!("{:?}", stone), "Debugging EchoStone: { power: 100, duration: 300 }"); +} diff --git a/exercises/concept/welcome-to-tech-palace/.docs/hints.md b/exercises/concept/welcome-to-tech-palace/.docs/hints.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/welcome-to-tech-palace/.docs/instructions.md b/exercises/concept/welcome-to-tech-palace/.docs/instructions.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/welcome-to-tech-palace/.docs/introduction.md b/exercises/concept/welcome-to-tech-palace/.docs/introduction.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/welcome-to-tech-palace/.meta/config.json b/exercises/concept/welcome-to-tech-palace/.meta/config.json new file mode 100644 index 00000000..24531679 --- /dev/null +++ b/exercises/concept/welcome-to-tech-palace/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/welcome_to_tech_palace.cairo" + ], + "exemplar": [ + ".meta/exemplar.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "forked_from": [ + "go/welcome-to-tech-palace" + ], + "blurb": "" +} diff --git a/exercises/concept/welcome-to-tech-palace/.meta/design.md b/exercises/concept/welcome-to-tech-palace/.meta/design.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/welcome-to-tech-palace/.meta/exemplar.cairo b/exercises/concept/welcome-to-tech-palace/.meta/exemplar.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/welcome-to-tech-palace/Scarb.toml b/exercises/concept/welcome-to-tech-palace/Scarb.toml new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/welcome-to-tech-palace/src/lib.cairo b/exercises/concept/welcome-to-tech-palace/src/lib.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/concept/welcome-to-tech-palace/tests/welcome_to_tech_palace.cairo b/exercises/concept/welcome-to-tech-palace/tests/welcome_to_tech_palace.cairo new file mode 100644 index 00000000..e69de29b diff --git a/exercises/practice/all-your-base/.docs/instructions.md b/exercises/practice/all-your-base/.docs/instructions.md new file mode 100644 index 00000000..1b688b69 --- /dev/null +++ b/exercises/practice/all-your-base/.docs/instructions.md @@ -0,0 +1,28 @@ +# Instructions + +Convert a sequence of digits in one base, representing a number, into a sequence of digits in another base, representing the same number. + +~~~~exercism/note +Try to implement the conversion yourself. +Do not use something else to perform the conversion for you. +~~~~ + +## About [Positional Notation][positional-notation] + +In positional notation, a number in base **b** can be understood as a linear combination of powers of **b**. + +The number 42, _in base 10_, means: + +`(4 × 10¹) + (2 × 10⁰)` + +The number 101010, _in base 2_, means: + +`(1 × 2⁵) + (0 × 2⁴) + (1 × 2³) + (0 × 2²) + (1 × 2¹) + (0 × 2⁰)` + +The number 1120, _in base 3_, means: + +`(1 × 3³) + (1 × 3²) + (2 × 3¹) + (0 × 3⁰)` + +_Yes. Those three numbers above are exactly the same. Congratulations!_ + +[positional-notation]: https://en.wikipedia.org/wiki/Positional_notation diff --git a/exercises/practice/all-your-base/.docs/introduction.md b/exercises/practice/all-your-base/.docs/introduction.md new file mode 100644 index 00000000..68aaffbe --- /dev/null +++ b/exercises/practice/all-your-base/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +You've just been hired as professor of mathematics. +Your first week went well, but something is off in your second week. +The problem is that every answer given by your students is wrong! +Luckily, your math skills have allowed you to identify the problem: the student answers _are_ correct, but they're all in base 2 (binary)! +Amazingly, it turns out that each week, the students use a different base. +To help you quickly verify the student answers, you'll be building a tool to translate between bases. diff --git a/exercises/practice/all-your-base/.meta/config.json b/exercises/practice/all-your-base/.meta/config.json new file mode 100644 index 00000000..a18e9700 --- /dev/null +++ b/exercises/practice/all-your-base/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "Falilah" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/all_your_base.cairo" + ], + "example": [ + ".meta/example.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "Convert a number, represented as a sequence of digits in one base, to any other base." +} diff --git a/exercises/practice/all-your-base/.meta/example.cairo b/exercises/practice/all-your-base/.meta/example.cairo new file mode 100644 index 00000000..584c560a --- /dev/null +++ b/exercises/practice/all-your-base/.meta/example.cairo @@ -0,0 +1,96 @@ +#[derive(Drop, Debug, PartialEq)] +pub enum Error { + InvalidInputBase, + InvalidOutputBase, + InvalidDigit: u32, +} + +pub fn rebase(digits: Array, input_base: u32, output_base: u32) -> Result, Error> { + if input_base < 2 { + return Result::Err(Error::InvalidInputBase); + } + if output_base < 2 { + return Result::Err(Error::InvalidOutputBase); + } + if digits.is_empty() { + return Result::Ok(array![0]); + } + + let mut response: u32 = 0; + let mut i: u32 = 0; + while i < digits.len() { + if *digits.at(i) >= input_base { + response = i; + break; + } + i += 1; + }; + + if response > 0 || i == 0 { + let x = *digits.at(i); + return Result::Err(Error::InvalidDigit(x)); + } + + let base_10_digits = convert_to_base_10(digits, input_base); + + let result = convert_from_base_10(base_10_digits, output_base); + + Result::Ok(result) +} + +fn convert_to_base_10(digits: Array, input_base: u32) -> u32 { + let mut sum = 0; + let mut i = 0; + let len = digits.len(); + while i < len { + let digit = digits[i]; + let exp = len - 1 - i; + let pow = pow(input_base, exp); + sum += *digit * pow; + i += 1; + }; + + sum +} + +fn convert_from_base_10(mut digits: u32, output_base: u32) -> Array { + let mut result: Array = ArrayTrait::new(); + if digits == 0 { + result.append(0); + return result; + } + + while digits > 0 { + let remainder = digits % output_base; + digits /= output_base; + result.append(remainder); + }; + + reverse_array(result) +} + +fn reverse_array(arr: Array) -> Array { + let mut reversed: Array = ArrayTrait::new(); + + let mut i = arr.len(); + while i > 0 { + i -= 1; + reversed.append(*arr[i]); + }; + + reversed +} + +fn pow(base: u32, mut power: u32) -> u32 { + if base == 0 { + return base; + } + let base: u256 = base.into(); + let mut result = 1_u256; + while power != 0 { + result *= base; + power -= 1; + }; + + result.try_into().expect('too large to fit output type') +} diff --git a/exercises/practice/all-your-base/.meta/tests.toml b/exercises/practice/all-your-base/.meta/tests.toml new file mode 100644 index 00000000..628e7d15 --- /dev/null +++ b/exercises/practice/all-your-base/.meta/tests.toml @@ -0,0 +1,81 @@ +# 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. + +[5ce422f9-7a4b-4f44-ad29-49c67cb32d2c] +description = "single bit one to decimal" + +[0cc3fea8-bb79-46ac-a2ab-5a2c93051033] +description = "binary to single decimal" + +[f12db0f9-0d3d-42c2-b3ba-e38cb375a2b8] +description = "single decimal to binary" + +[2c45cf54-6da3-4748-9733-5a3c765d925b] +description = "binary to multiple decimal" + +[65ddb8b4-8899-4fcc-8618-181b2cf0002d] +description = "decimal to binary" + +[8d418419-02a7-4824-8b7a-352d33c6987e] +description = "trinary to hexadecimal" + +[d3901c80-8190-41b9-bd86-38d988efa956] +description = "hexadecimal to trinary" + +[5d42f85e-21ad-41bd-b9be-a3e8e4258bbf] +description = "15-bit integer" + +[d68788f7-66dd-43f8-a543-f15b6d233f83] +description = "empty list" + +[5e27e8da-5862-4c5f-b2a9-26c0382b6be7] +description = "single zero" + +[2e1c2573-77e4-4b9c-8517-6c56c5bcfdf2] +description = "multiple zeros" + +[3530cd9f-8d6d-43f5-bc6e-b30b1db9629b] +description = "leading zeros" + +[a6b476a1-1901-4f2a-92c4-4d91917ae023] +description = "input base is one" + +[e21a693a-7a69-450b-b393-27415c26a016] +description = "input base is zero" + +[54a23be5-d99e-41cc-88e0-a650ffe5fcc2] +description = "input base is negative" +include = false +comment = "we use unsigned integers" + +[9eccf60c-dcc9-407b-95d8-c37b8be56bb6] +description = "negative digit" +include = false +comment = "we use unsigned integers" + +[232fa4a5-e761-4939-ba0c-ed046cd0676a] +description = "invalid positive digit" + +[14238f95-45da-41dc-95ce-18f860b30ad3] +description = "output base is one" + +[73dac367-da5c-4a37-95fe-c87fad0a4047] +description = "output base is zero" + +[13f81f42-ff53-4e24-89d9-37603a48ebd9] +description = "output base is negative" +include = false +comment = "we use unsigned integers" + +[0e6c895d-8a5d-4868-a345-309d094cfe8d] +description = "both bases are negative" +include = false +comment = "we use unsigned integers" diff --git a/exercises/practice/all-your-base/Scarb.toml b/exercises/practice/all-your-base/Scarb.toml new file mode 100644 index 00000000..f5557f14 --- /dev/null +++ b/exercises/practice/all-your-base/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "all_your_base" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.8.2" diff --git a/exercises/practice/all-your-base/src/lib.cairo b/exercises/practice/all-your-base/src/lib.cairo new file mode 100644 index 00000000..e3e2bb96 --- /dev/null +++ b/exercises/practice/all-your-base/src/lib.cairo @@ -0,0 +1,10 @@ +#[derive(Drop, Debug, PartialEq)] +pub enum Error { + InvalidInputBase, + InvalidOutputBase, + InvalidDigit: u32, +} + +pub fn rebase(digits: Array, input_base: u32, output_base: u32) -> Result, Error> { + panic!("implement `convert`") +} diff --git a/exercises/practice/all-your-base/tests/all_your_base.cairo b/exercises/practice/all-your-base/tests/all_your_base.cairo new file mode 100644 index 00000000..24691231 --- /dev/null +++ b/exercises/practice/all-your-base/tests/all_your_base.cairo @@ -0,0 +1,224 @@ +use all_your_base::{rebase, Error}; + +#[test] +fn single_bit_one_to_decimal() { + let input_base = 2; + let input_digits: Array = array![1]; + let output_base = 10; + + let expected_output = Result::Ok(array![1]); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + +#[test] +#[ignore] +fn binary_to_single_decimal() { + let input_base = 2; + let input_digits: Array = array![1, 0, 1]; + let output_base = 10; + + let expected_output = Result::Ok(array![5]); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + +#[test] +#[ignore] +fn single_decimal_to_binary() { + let input_base = 10; + let input_digits = array![5]; + let output_base = 2; + + let expected_output = Result::Ok(array![1, 0, 1]); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + + +#[test] +#[ignore] +fn binary_to_multiple_decimal() { + let input_base = 2; + let input_digits = array![1, 0, 1, 0, 1, 0]; + let output_base = 10; + + let expected_output = Result::Ok(array![4, 2]); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + +#[test] +#[ignore] +fn decimal_to_binary() { + let input_base = 10; + let input_digits = array![4, 2]; + let output_base = 2; + + let expected_output = Result::Ok(array![1, 0, 1, 0, 1, 0]); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + +#[test] +#[ignore] +fn trinary_to_hexadecimal() { + let input_base = 3; + let input_digits = array![1, 1, 2, 0]; + let output_base = 16; + + let expected_output = Result::Ok(array![2, 10]); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + +#[test] +#[ignore] +fn hexadecimal_to_trinary() { + let input_base = 16; + let input_digits = array![2, 10]; + let output_base = 3; + + let expected_output = Result::Ok(array![1, 1, 2, 0]); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + +#[test] +#[ignore] +fn test_15_bit_integer() { + let input_base = 97; + let input_digits = array![3, 46, 60]; + let output_base = 73; + + let expected_output = Result::Ok(array![6, 10, 45]); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + + +#[test] +#[ignore] +fn empty_list() { + let input_base = 2; + let input_digits = array![]; + let output_base = 10; + + let expected_output = Result::Ok(array![0]); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + +#[test] +#[ignore] +fn single_zero() { + let input_base = 10; + let input_digits = array![0]; + let output_base = 2; + + let expected_output = Result::Ok(array![0]); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + +#[test] +#[ignore] +fn multiple_zeros() { + let input_base = 10; + let input_digits = array![0, 0, 0]; + let output_base = 2; + + let expected_output = Result::Ok(array![0]); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + +#[test] +#[ignore] +fn leading_zeros() { + let input_base = 7; + let input_digits = array![0, 6, 0]; + let output_base = 10; + + let expected_output = Result::Ok(array![4, 2]); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + +#[test] +#[ignore] +fn input_base_is_one() { + let input_base = 1; + let input_digits = array![0]; + let output_base = 10; + + let expected_output = Result::Err(Error::InvalidInputBase); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + +#[test] +#[ignore] +fn input_base_is_zero() { + let input_base = 0; + let input_digits = array![]; + let output_base = 10; + + let expected_output = Result::Err(Error::InvalidInputBase); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + +#[test] +#[ignore] +fn invalid_positive_digit() { + let input_base = 2; + let input_digits = array![1, 2, 1, 0, 1, 0]; + let output_base = 10; + + let expected_output = Result::Err(Error::InvalidDigit(2)); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + + +#[test] +#[ignore] +fn output_base_is_one() { + let input_base = 2; + let input_digits = array![1, 0, 1, 0, 1, 0]; + let output_base = 1; + + let expected_output = Result::Err(Error::InvalidOutputBase); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} + +#[test] +#[ignore] +fn output_base_is_zero() { + let input_base = 10; + let input_digits = array![7]; + let output_base = 0; + + let expected_output = Result::Err(Error::InvalidOutputBase); + let result = rebase(input_digits, input_base, output_base); + + assert_eq!(result, expected_output); +} 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..94114ad6 --- /dev/null +++ b/exercises/practice/high-scores/.meta/config.json @@ -0,0 +1,21 @@ +{ + "authors": [ + "0xNeshi" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/high_scores.cairo" + ], + "example": [ + ".meta/example.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "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.cairo b/exercises/practice/high-scores/.meta/example.cairo new file mode 100644 index 00000000..47041474 --- /dev/null +++ b/exercises/practice/high-scores/.meta/example.cairo @@ -0,0 +1,77 @@ +use core::dict::Felt252Dict; + +#[derive(Drop,)] +pub struct HighScores { + scores: Array, +} + +#[generate_trait] +pub impl HighScoresImpl of HighScoresTrait { + fn new(scores: Array) -> HighScores { + HighScores { scores } + } + + fn scores(self: @HighScores) -> Span { + self.scores.span() + } + + fn latest(self: @HighScores) -> Option { + match self.scores.get(self.scores.len() - 1) { + Option::Some(boxed) => Option::Some(*boxed.unbox()), + _ => Option::None + } + } + + fn personal_best(self: @HighScores) -> Option { + if self.scores.is_empty() { + return Option::None; + }; + let mut max = 0; + for score in self.scores.span() { + if score > @max { + max = *score; + } + }; + Option::Some(max) + } + + fn personal_top_three(self: @HighScores) -> Span { + let sorted = insertion_sort(self.scores.span()); + let take = if sorted.len() > 3 { + 3 + } else { + sorted.len() + }; + sorted.span().slice(0, take) + } +} + +fn insertion_sort(span: Span) -> Array { + if span.is_empty() { + return array![]; + } + + // Felt252Dict enables swapping and moving values + let mut sorted: Felt252Dict = Default::default(); + sorted.insert(0, *span[0]); + + // insert all elements in their sorted position + for i in 1 + ..span + .len() { + let elem = *span[i]; + let mut j: felt252 = i.into(); + while j != 0 && elem > sorted.get(j - 1) { + sorted.insert(j, sorted.get(j - 1)); + j -= 1; + }; + sorted.insert(j.into(), elem); + }; + + // collect all elements into an array + let mut sorted_arr: Array = array![]; + for i in 0..span.len() { + sorted_arr.append(sorted.get(i.into())); + }; + sorted_arr +} 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/Scarb.toml b/exercises/practice/high-scores/Scarb.toml new file mode 100644 index 00000000..679160df --- /dev/null +++ b/exercises/practice/high-scores/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "high_scores" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.8.2" diff --git a/exercises/practice/high-scores/src/lib.cairo b/exercises/practice/high-scores/src/lib.cairo new file mode 100644 index 00000000..9365d5b8 --- /dev/null +++ b/exercises/practice/high-scores/src/lib.cairo @@ -0,0 +1,25 @@ +#[derive(Drop,)] +pub struct HighScores {} + +#[generate_trait] +pub impl HighScoresImpl of HighScoresTrait { + fn new(scores: Array) -> HighScores { + panic!("implement `new`") + } + + fn scores(self: @HighScores) -> Span { + panic!("implement `scores`") + } + + fn latest(self: @HighScores) -> Option { + panic!("implement `latest`") + } + + fn personal_best(self: @HighScores) -> Option { + panic!("implement `personal_best`") + } + + fn personal_top_three(self: @HighScores) -> Span { + panic!("implement `personal_top_three`") + } +} diff --git a/exercises/practice/high-scores/tests/high_scores.cairo b/exercises/practice/high-scores/tests/high_scores.cairo new file mode 100644 index 00000000..ebba147b --- /dev/null +++ b/exercises/practice/high-scores/tests/high_scores.cairo @@ -0,0 +1,100 @@ +use high_scores::HighScoresTrait as HighScores; + +#[test] +fn list_of_scores() { + let expected: Array = array![30, 50, 20, 70]; + let high_scores = HighScores::new(expected.clone()); + assert_eq!(high_scores.scores(), expected.span()); +} + +#[test] +#[ignore] +fn latest_score() { + let high_scores = HighScores::new(array![100, 0, 90, 30]); + let expected = Option::Some(30); + assert_eq!(high_scores.latest(), expected); +} + +#[test] +#[ignore] +fn personal_best() { + let high_scores = HighScores::new(array![40, 100, 70]); + let expected = Option::Some(100); + assert_eq!(high_scores.personal_best(), expected); +} + +#[test] +#[ignore] +fn personal_top_three_from_a_list_of_scores() { + let high_scores = HighScores::new(array![10, 30, 90, 30, 100, 20, 10, 0, 30, 40, 40, 70, 70]); + let expected = array![100, 90, 70].span(); + assert_eq!(high_scores.personal_top_three(), expected); +} + +#[test] +#[ignore] +fn personal_top_highest_to_lowest() { + let high_scores = HighScores::new(array![20, 10, 30]); + let expected = array![30, 20, 10].span(); + assert_eq!(high_scores.personal_top_three(), expected); +} + +#[test] +#[ignore] +fn personal_top_when_there_is_a_tie() { + let high_scores = HighScores::new(array![40, 20, 40, 30]); + let expected = array![40, 40, 30].span(); + assert_eq!(high_scores.personal_top_three(), expected); +} + +#[test] +#[ignore] +fn personal_top_when_there_are_less_than_3() { + let high_scores = HighScores::new(array![30, 70]); + let expected = array![70, 30].span(); + assert_eq!(high_scores.personal_top_three(), expected); +} + +#[test] +#[ignore] +fn personal_top_when_there_is_only_one() { + let high_scores = HighScores::new(array![40]); + let expected = array![40].span(); + assert_eq!(high_scores.personal_top_three(), expected); +} + +#[test] +#[ignore] +fn latest_score_after_personal_top_scores() { + let high_scores = HighScores::new(array![70, 50, 20, 30]); + let expected = Option::Some(30); + high_scores.personal_top_three(); + assert_eq!(high_scores.latest(), expected); +} + +#[test] +#[ignore] +fn scores_after_personal_top_scores() { + let expected: Array = array![30, 50, 20, 70]; + let high_scores = HighScores::new(expected.clone()); + high_scores.personal_top_three(); + assert_eq!(high_scores.scores(), expected.span()); +} + +#[test] +#[ignore] +fn latest_score_after_personal_best() { + let high_scores = HighScores::new(array![20, 70, 15, 25, 30]); + let expected = Option::Some(30); + high_scores.personal_best().unwrap(); + assert_eq!(high_scores.latest(), expected); +} + +#[test] +#[ignore] +fn scores_after_personal_best() { + let expected: Array = array![20, 70, 15, 25, 30]; + let high_scores = HighScores::new(expected.clone()); + high_scores.personal_best().unwrap(); + assert_eq!(high_scores.scores(), expected.span()); +} diff --git a/exercises/practice/kindergarten-garden/.docs/instructions.md b/exercises/practice/kindergarten-garden/.docs/instructions.md new file mode 100644 index 00000000..6fe11a58 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.docs/instructions.md @@ -0,0 +1,56 @@ +# Instructions + +Your task is to, given a diagram, determine which plants each child in the kindergarten class is responsible for. + +There are 12 children in the class: + +- Alice, Bob, Charlie, David, Eve, Fred, Ginny, Harriet, Ileana, Joseph, Kincaid, and Larry. + +Four different types of seeds are planted: + +| Plant | Diagram encoding | +| ------ | ---------------- | +| Grass | G | +| Clover | C | +| Radish | R | +| Violet | V | + +Each child gets four cups, two on each row: + +```text +[window][window][window] +........................ # each dot represents a cup +........................ +``` + +Their teacher assigns cups to the children alphabetically by their names, which means that Alice comes first and Larry comes last. + +Here is an example diagram representing Alice's plants: + +```text +[window][window][window] +VR...................... +RG...................... +``` + +In the first row, nearest the windows, she has a violet and a radish. +In the second row she has a radish and some grass. + +Your program will be given the plants from left-to-right starting with the row nearest the windows. +From this, it should be able to determine which plants belong to each student. + +For example, if it's told that the garden looks like so: + +```text +[window][window][window] +VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV +``` + +Then if asked for Alice's plants, it should provide: + +- Violets, radishes, violets, radishes + +While asking for Bob's plants would yield: + +- Clover, grass, clover, clover diff --git a/exercises/practice/kindergarten-garden/.docs/introduction.md b/exercises/practice/kindergarten-garden/.docs/introduction.md new file mode 100644 index 00000000..5ad97d23 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +The kindergarten class is learning about growing plants. +The teacher thought it would be a good idea to give the class seeds to plant and grow in the dirt. +To this end, the children have put little cups along the window sills and planted one type of plant in each cup. +The children got to pick their favorites from four available types of seeds: grass, clover, radishes, and violets. diff --git a/exercises/practice/kindergarten-garden/.meta/config.json b/exercises/practice/kindergarten-garden/.meta/config.json new file mode 100644 index 00000000..cfbeaff5 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "0xNeshi" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/kindergarten_garden.cairo" + ], + "example": [ + ".meta/example.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "Given a diagram, determine which plants each child in the kindergarten class is responsible for.", + "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.", + "source_url": "https://turing.edu" +} diff --git a/exercises/practice/kindergarten-garden/.meta/example.cairo b/exercises/practice/kindergarten-garden/.meta/example.cairo new file mode 100644 index 00000000..0784baef --- /dev/null +++ b/exercises/practice/kindergarten-garden/.meta/example.cairo @@ -0,0 +1,91 @@ +#[derive(Drop, Debug, PartialEq)] +pub enum Plant { + Radishes, + Clover, + Grass, + Violets +} + +#[derive(Drop)] +pub enum Student { + Alice, + Bob, + Charlie, + David, + Eve, + Fred, + Ginny, + Harriet, + Ileana, + Joseph, + Kincaid, + Larry, +} + +/// Function to determine the index of a student in the list +fn get_student_index(student: Student) -> usize { + match student { + Student::Alice => 0, + Student::Bob => 1, + Student::Charlie => 2, + Student::David => 3, + Student::Eve => 4, + Student::Fred => 5, + Student::Ginny => 6, + Student::Harriet => 7, + Student::Ileana => 8, + Student::Joseph => 9, + Student::Kincaid => 10, + Student::Larry => 11, + } +} + +/// Get string slice +fn lines(diagram: @ByteArray) -> [ByteArray; 2] { + let mut line1 = ""; + let mut line2 = ""; + + let mut i = 0; + // everything before the newline char is line 1 + while diagram[i] != '\n' { + line1.append_byte(diagram[i]); + i += 1; + }; + // `i` is at the newline char index, so everything after it is line 2 + for i in (i + 1)..diagram.len() { + line2.append_byte(diagram[i]); + }; + + [line1, line2] +} + +/// Mapping plant characters to plant names +fn plant_from_char(c: u8) -> Plant { + if c == 'R' { + Plant::Radishes + } else if c == 'C' { + Plant::Clover + } else if c == 'G' { + Plant::Grass + } else if c == 'V' { + Plant::Violets + } else { + panic!("No such plant") + } +} + +/// Function to retrieve the plants for a given student based on the diagram +pub fn plants(diagram: ByteArray, student: Student) -> Array { + let index = get_student_index(student); + + let [line1, line2] = lines(@diagram); + + // Retrieve the plants for the student based on the index + let start = index * 2; + let plant1 = plant_from_char(line1[start]); + let plant2 = plant_from_char(line1[start + 1]); + let plant3 = plant_from_char(line2[start]); + let plant4 = plant_from_char(line2[start + 1]); + + array![plant1, plant2, plant3, plant4,] +} diff --git a/exercises/practice/kindergarten-garden/.meta/tests.toml b/exercises/practice/kindergarten-garden/.meta/tests.toml new file mode 100644 index 00000000..0cdd9ad6 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.meta/tests.toml @@ -0,0 +1,61 @@ +# 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. + +[1fc316ed-17ab-4fba-88ef-3ae78296b692] +description = "partial garden -> garden with single student" + +[acd19dc1-2200-4317-bc2a-08f021276b40] +description = "partial garden -> different garden with single student" + +[c376fcc8-349c-446c-94b0-903947315757] +description = "partial garden -> garden with two students" + +[2d620f45-9617-4924-9d27-751c80d17db9] +description = "partial garden -> multiple students for the same garden with three students -> second student's garden" + +[57712331-4896-4364-89f8-576421d69c44] +description = "partial garden -> multiple students for the same garden with three students -> third student's garden" + +[149b4290-58e1-40f2-8ae4-8b87c46e765b] +description = "full garden -> for Alice, first student's garden" + +[ba25dbbc-10bd-4a37-b18e-f89ecd098a5e] +description = "full garden -> for Bob, second student's garden" + +[566b621b-f18e-4c5f-873e-be30544b838c] +description = "full garden -> for Charlie" + +[3ad3df57-dd98-46fc-9269-1877abf612aa] +description = "full garden -> for David" + +[0f0a55d1-9710-46ed-a0eb-399ba8c72db2] +description = "full garden -> for Eve" + +[a7e80c90-b140-4ea1-aee3-f4625365c9a4] +description = "full garden -> for Fred" + +[9d94b273-2933-471b-86e8-dba68694c615] +description = "full garden -> for Ginny" + +[f55bc6c2-ade8-4844-87c4-87196f1b7258] +description = "full garden -> for Harriet" + +[759070a3-1bb1-4dd4-be2c-7cce1d7679ae] +description = "full garden -> for Ileana" + +[78578123-2755-4d4a-9c7d-e985b8dda1c6] +description = "full garden -> for Joseph" + +[6bb66df7-f433-41ab-aec2-3ead6e99f65b] +description = "full garden -> for Kincaid, second to last student's garden" + +[d7edec11-6488-418a-94e6-ed509e0fa7eb] +description = "full garden -> for Larry, last student's garden" diff --git a/exercises/practice/kindergarten-garden/Scarb.toml b/exercises/practice/kindergarten-garden/Scarb.toml new file mode 100644 index 00000000..01b6a1ff --- /dev/null +++ b/exercises/practice/kindergarten-garden/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "kindergarten_garden" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.8.2" diff --git a/exercises/practice/kindergarten-garden/src/lib.cairo b/exercises/practice/kindergarten-garden/src/lib.cairo new file mode 100644 index 00000000..5aa9d38e --- /dev/null +++ b/exercises/practice/kindergarten-garden/src/lib.cairo @@ -0,0 +1,27 @@ +#[derive(Drop, Debug, PartialEq)] +pub enum Plant { + Radishes, + Clover, + Grass, + Violets +} + +#[derive(Drop)] +pub enum Student { + Alice, + Bob, + Charlie, + David, + Eve, + Fred, + Ginny, + Harriet, + Ileana, + Joseph, + Kincaid, + Larry, +} + +pub fn plants(diagram: ByteArray, student: Student) -> Array { + panic!("implement `plants`") +} diff --git a/exercises/practice/kindergarten-garden/tests/kindergarten_garden.cairo b/exercises/practice/kindergarten-garden/tests/kindergarten_garden.cairo new file mode 100644 index 00000000..52f15781 --- /dev/null +++ b/exercises/practice/kindergarten-garden/tests/kindergarten_garden.cairo @@ -0,0 +1,170 @@ +use kindergarten_garden::{plants, Plant, Student}; + +#[test] +fn garden_with_single_student() { + let diagram = "RC +GG"; + let student = Student::Alice; + let expected = array![Plant::Radishes, Plant::Clover, Plant::Grass, Plant::Grass]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn different_garden_with_single_student() { + let diagram = "VC +RC"; + let student = Student::Alice; + let expected = array![Plant::Violets, Plant::Clover, Plant::Radishes, Plant::Clover]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn garden_with_two_students() { + let diagram = "VVCG +VVRC"; + let student = Student::Bob; + let expected = array![Plant::Clover, Plant::Grass, Plant::Radishes, Plant::Clover]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn second_students_garden() { + let diagram = "VVCCGG +VVCCGG"; + let student = Student::Bob; + let expected = array![Plant::Clover, Plant::Clover, Plant::Clover, Plant::Clover]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn third_students_garden() { + let diagram = "VVCCGG +VVCCGG"; + let student = Student::Charlie; + let expected = array![Plant::Grass, Plant::Grass, Plant::Grass, Plant::Grass]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn for_alice_first_students_garden() { + let diagram = "VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV"; + let student = Student::Alice; + let expected = array![Plant::Violets, Plant::Radishes, Plant::Violets, Plant::Radishes]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn for_bob_second_students_garden() { + let diagram = "VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV"; + let student = Student::Bob; + let expected = array![Plant::Clover, Plant::Grass, Plant::Clover, Plant::Clover]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn for_charlie() { + let diagram = "VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV"; + let student = Student::Charlie; + let expected = array![Plant::Violets, Plant::Violets, Plant::Clover, Plant::Grass]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn for_david() { + let diagram = "VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV"; + let student = Student::David; + let expected = array![Plant::Radishes, Plant::Violets, Plant::Clover, Plant::Radishes]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn for_eve() { + let diagram = "VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV"; + let student = Student::Eve; + let expected = array![Plant::Clover, Plant::Grass, Plant::Radishes, Plant::Grass]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn for_fred() { + let diagram = "VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV"; + let student = Student::Fred; + let expected = array![Plant::Grass, Plant::Clover, Plant::Violets, Plant::Clover]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn for_ginny() { + let diagram = "VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV"; + let student = Student::Ginny; + let expected = array![Plant::Clover, Plant::Grass, Plant::Grass, Plant::Clover]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn for_harriet() { + let diagram = "VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV"; + let student = Student::Harriet; + let expected = array![Plant::Violets, Plant::Radishes, Plant::Radishes, Plant::Violets]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn for_ileana() { + let diagram = "VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV"; + let student = Student::Ileana; + let expected = array![Plant::Grass, Plant::Clover, Plant::Violets, Plant::Clover]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn for_joseph() { + let diagram = "VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV"; + let student = Student::Joseph; + let expected = array![Plant::Violets, Plant::Clover, Plant::Violets, Plant::Grass]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn for_kincaid_second_to_last_students_garden() { + let diagram = "VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV"; + let student = Student::Kincaid; + let expected = array![Plant::Grass, Plant::Clover, Plant::Clover, Plant::Grass]; + assert_eq!(plants(diagram, student), expected); +} + +#[test] +#[ignore] +fn for_larry_last_students_garden() { + let diagram = "VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV"; + let student = Student::Larry; + let expected = array![Plant::Grass, Plant::Violets, Plant::Clover, Plant::Violets]; + assert_eq!(plants(diagram, student), expected); +} diff --git a/exercises/practice/luhn/.docs/instructions.md b/exercises/practice/luhn/.docs/instructions.md new file mode 100644 index 00000000..49934c10 --- /dev/null +++ b/exercises/practice/luhn/.docs/instructions.md @@ -0,0 +1,65 @@ +# Instructions + +Given a number determine whether or not it is valid per the Luhn formula. + +The [Luhn algorithm][luhn] is a simple checksum formula used to validate a variety of identification numbers, such as credit card numbers and Canadian Social Insurance Numbers. + +The task is to check if a given string is valid. + +## Validating a Number + +Strings of length 1 or less are not valid. +Spaces are allowed in the input, but they should be stripped before checking. +All other non-digit characters are disallowed. + +### Example 1: valid credit card number + +```text +4539 3195 0343 6467 +``` + +The first step of the Luhn algorithm is to double every second digit, starting from the right. +We will be doubling + +```text +4539 3195 0343 6467 +↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ (double these) +``` + +If doubling the number results in a number greater than 9 then subtract 9 from the product. +The results of our doubling: + +```text +8569 6195 0383 3437 +``` + +Then sum all of the digits: + +```text +8+5+6+9+6+1+9+5+0+3+8+3+3+4+3+7 = 80 +``` + +If the sum is evenly divisible by 10, then the number is valid. +This number is valid! + +### Example 2: invalid credit card number + +```text +8273 1232 7352 0569 +``` + +Double the second digits, starting from the right + +```text +7253 2262 5312 0539 +``` + +Sum the digits + +```text +7+2+5+3+2+2+6+2+5+3+1+2+0+5+3+9 = 57 +``` + +57 is not evenly divisible by 10, so this number is not valid. + +[luhn]: https://en.wikipedia.org/wiki/Luhn_algorithm diff --git a/exercises/practice/luhn/.meta/config.json b/exercises/practice/luhn/.meta/config.json new file mode 100644 index 00000000..49fd5985 --- /dev/null +++ b/exercises/practice/luhn/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "0xNeshi" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/luhn.cairo" + ], + "example": [ + ".meta/example.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "Given a number determine whether or not it is valid per the Luhn formula.", + "source": "The Luhn Algorithm on Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Luhn_algorithm" +} diff --git a/exercises/practice/luhn/.meta/example.cairo b/exercises/practice/luhn/.meta/example.cairo new file mode 100644 index 00000000..0d0f2f9d --- /dev/null +++ b/exercises/practice/luhn/.meta/example.cairo @@ -0,0 +1,58 @@ +// Utility function to convert a char representing a digit into its numerical value (u32 equivalent) +fn char_to_digit(c: u8) -> Option { + let zero_ascii = '0'; + let nine_ascii = '9'; + + if c >= zero_ascii && c <= nine_ascii { + Option::Some(c - zero_ascii) + } else { + Option::None // Return None for invalid characters + } +} + +// Function to validate a number using the Luhn algorithm +pub fn valid(candidate: ByteArray) -> bool { + let mut count_digits = 0_u32; + let mut processed_digits_sum = 0_u32; + let mut should_double = false; + let mut valid = true; + + // Iterate over characters from right to left + let mut i = candidate.len(); + while i != 0 { + i -= 1; + + let c = candidate[i]; + + // Skip spaces + if c == ' ' { + continue; + } + + // Convert char to digit and check validity + let digit = match char_to_digit(c) { + Option::Some(val) => val, + Option::None => { + valid = false; + break; + } + }; + + // Process digit according to Luhn's algorithm + if should_double { + let mut doubled = digit * 2; + if doubled > 9 { + doubled -= 9; + } + processed_digits_sum += doubled.into(); + } else { + processed_digits_sum += digit.into(); + } + + // Flip should_double for alternating digits + should_double = !should_double; + count_digits += 1; + }; + + valid && count_digits > 1 && processed_digits_sum % 10 == 0 +} diff --git a/exercises/practice/luhn/.meta/tests.toml b/exercises/practice/luhn/.meta/tests.toml new file mode 100644 index 00000000..c0be0c4d --- /dev/null +++ b/exercises/practice/luhn/.meta/tests.toml @@ -0,0 +1,76 @@ +# 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. + +[792a7082-feb7-48c7-b88b-bbfec160865e] +description = "single digit strings can not be valid" + +[698a7924-64d4-4d89-8daa-32e1aadc271e] +description = "a single zero is invalid" + +[73c2f62b-9b10-4c9f-9a04-83cee7367965] +description = "a simple valid SIN that remains valid if reversed" + +[9369092e-b095-439f-948d-498bd076be11] +description = "a simple valid SIN that becomes invalid if reversed" + +[8f9f2350-1faf-4008-ba84-85cbb93ffeca] +description = "a valid Canadian SIN" + +[1cdcf269-6560-44fc-91f6-5819a7548737] +description = "invalid Canadian SIN" + +[656c48c1-34e8-4e60-9a5a-aad8a367810a] +description = "invalid credit card" + +[20e67fad-2121-43ed-99a8-14b5b856adb9] +description = "invalid long number with an even remainder" + +[7e7c9fc1-d994-457c-811e-d390d52fba5e] +description = "invalid long number with a remainder divisible by 5" + +[ad2a0c5f-84ed-4e5b-95da-6011d6f4f0aa] +description = "valid number with an even number of digits" + +[ef081c06-a41f-4761-8492-385e13c8202d] +description = "valid number with an odd number of spaces" + +[bef66f64-6100-4cbb-8f94-4c9713c5e5b2] +description = "valid strings with a non-digit added at the end become invalid" + +[2177e225-9ce7-40f6-b55d-fa420e62938e] +description = "valid strings with punctuation included become invalid" + +[ebf04f27-9698-45e1-9afe-7e0851d0fe8d] +description = "valid strings with symbols included become invalid" + +[08195c5e-ce7f-422c-a5eb-3e45fece68ba] +description = "single zero with space is invalid" + +[12e63a3c-f866-4a79-8c14-b359fc386091] +description = "more than a single zero is valid" + +[ab56fa80-5de8-4735-8a4a-14dae588663e] +description = "input digit 9 is correctly converted to output digit 9" + +[b9887ee8-8337-46c5-bc45-3bcab51bc36f] +description = "very long input is valid" + +[8a7c0e24-85ea-4154-9cf1-c2db90eabc08] +description = "valid luhn with an odd number of digits and non zero first digit" + +[39a06a5a-5bad-4e0f-b215-b042d46209b1] +description = "using ascii value for non-doubled non-digit isn't allowed" + +[f94cf191-a62f-4868-bc72-7253114aa157] +description = "using ascii value for doubled non-digit isn't allowed" + +[8b72ad26-c8be-49a2-b99c-bcc3bf631b33] +description = "non-numeric, non-space char in the middle with a sum that's divisible by 10 isn't allowed" diff --git a/exercises/practice/luhn/Scarb.toml b/exercises/practice/luhn/Scarb.toml new file mode 100644 index 00000000..d67c0f9a --- /dev/null +++ b/exercises/practice/luhn/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "luhn" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.8.2" diff --git a/exercises/practice/luhn/src/lib.cairo b/exercises/practice/luhn/src/lib.cairo new file mode 100644 index 00000000..6592cfa4 --- /dev/null +++ b/exercises/practice/luhn/src/lib.cairo @@ -0,0 +1,4 @@ +// Function to validate a number using the Luhn algorithm +pub fn valid(candidate: ByteArray) -> bool { + panic!("implement `valid`") +} diff --git a/exercises/practice/luhn/tests/luhn.cairo b/exercises/practice/luhn/tests/luhn.cairo new file mode 100644 index 00000000..d9fe85e0 --- /dev/null +++ b/exercises/practice/luhn/tests/luhn.cairo @@ -0,0 +1,132 @@ +use luhn::valid; + +#[test] +fn single_digit_strings_can_not_be_valid() { + assert!(!valid("1")); +} + +#[test] +#[ignore] +fn a_single_zero_is_invalid() { + assert!(!valid("0")); +} + +#[test] +#[ignore] +fn a_simple_valid_sin_that_remains_valid_if_reversed() { + assert!(valid("059")); +} + +#[test] +#[ignore] +fn a_simple_valid_sin_that_becomes_invalid_if_reversed() { + assert!(valid("59")); +} + +#[test] +#[ignore] +fn a_valid_canadian_sin() { + assert!(valid("055 444 285")); +} + +#[test] +#[ignore] +fn invalid_canadian_sin() { + assert!(!valid("055 444 286")); +} + +#[test] +#[ignore] +fn invalid_credit_card() { + assert!(!valid("8273 1232 7352 0569")); +} + +#[test] +#[ignore] +fn invalid_long_number_with_an_even_remainder() { + assert!(!valid("1 2345 6789 1234 5678 9012")); +} + +#[test] +#[ignore] +fn invalid_long_number_with_a_remainder_divisible_by_5() { + assert!(!valid("1 2345 6789 1234 5678 9013")); +} + +#[test] +#[ignore] +fn valid_number_with_an_even_number_of_digits() { + assert!(valid("095 245 88")); +} + +#[test] +#[ignore] +fn valid_number_with_an_odd_number_of_spaces() { + assert!(valid("234 567 891 234")); +} + +#[test] +#[ignore] +fn valid_strings_with_a_non_digit_added_at_the_end_become_invalid() { + assert!(!valid("059a")); +} + +#[test] +#[ignore] +fn valid_strings_with_punctuation_included_become_invalid() { + assert!(!valid("055-444-285")); +} + +#[test] +#[ignore] +fn valid_strings_with_symbols_included_become_invalid() { + assert!(!valid("055# 444$ 285")); +} + +#[test] +#[ignore] +fn single_zero_with_space_is_invalid() { + assert!(!valid(" 0")); +} + +#[test] +#[ignore] +fn more_than_a_single_zero_is_valid() { + assert!(valid("0000 0")); +} + +#[test] +#[ignore] +fn input_digit_9_is_correctly_converted_to_output_digit_9() { + assert!(valid("091")); +} + +#[test] +#[ignore] +fn very_long_input_is_valid() { + assert!(valid("9999999999 9999999999 9999999999 9999999999")); +} + +#[test] +#[ignore] +fn valid_luhn_with_an_odd_number_of_digits_and_non_zero_first_digit() { + assert!(valid("109")); +} + +#[test] +#[ignore] +fn using_ascii_value_for_non_doubled_non_digit_isn_t_allowed() { + assert!(!valid("055b 444 285")); +} + +#[test] +#[ignore] +fn using_ascii_value_for_doubled_non_digit_isn_t_allowed() { + assert!(!valid(":9")); +} + +#[test] +#[ignore] +fn non_numeric_non_space_char_in_the_middle_with_a_sum_that_s_divisible_by_10_isn_t_allowed() { + assert!(!valid("59%59")); +} diff --git a/exercises/practice/minesweeper/.docs/instructions.md b/exercises/practice/minesweeper/.docs/instructions.md new file mode 100644 index 00000000..7c1df2e4 --- /dev/null +++ b/exercises/practice/minesweeper/.docs/instructions.md @@ -0,0 +1,26 @@ +# Instructions + +Your task is to add the mine counts to empty squares in a completed Minesweeper board. +The board itself is a rectangle composed of squares that are either empty (`' '`) or a mine (`'*'`). + +For each empty square, count the number of mines adjacent to it (horizontally, vertically, diagonally). +If the empty square has no adjacent mines, leave it empty. +Otherwise replace it with the adjacent mines count. + +For example, you may receive a 5 x 4 board like this (empty spaces are represented here with the '·' character for display on screen): + +```text +·*·*· +··*·· +··*·· +····· +``` + +Which your code should transform into this: + +```text +1*3*1 +13*31 +·2*2· +·111· +``` diff --git a/exercises/practice/minesweeper/.docs/introduction.md b/exercises/practice/minesweeper/.docs/introduction.md new file mode 100644 index 00000000..5f74a742 --- /dev/null +++ b/exercises/practice/minesweeper/.docs/introduction.md @@ -0,0 +1,5 @@ +# Introduction + +[Minesweeper][wikipedia] is a popular game where the user has to find the mines using numeric hints that indicate how many mines are directly adjacent (horizontally, vertically, diagonally) to a square. + +[wikipedia]: https://en.wikipedia.org/wiki/Minesweeper_(video_game) diff --git a/exercises/practice/minesweeper/.meta/config.json b/exercises/practice/minesweeper/.meta/config.json new file mode 100644 index 00000000..a4b07b17 --- /dev/null +++ b/exercises/practice/minesweeper/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "0xNeshi" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/minesweeper.cairo" + ], + "example": [ + ".meta/example.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "Add the numbers to a minesweeper board." +} diff --git a/exercises/practice/minesweeper/.meta/example.cairo b/exercises/practice/minesweeper/.meta/example.cairo new file mode 100644 index 00000000..083cca0b --- /dev/null +++ b/exercises/practice/minesweeper/.meta/example.cairo @@ -0,0 +1,100 @@ +// Struct to represent the board +#[derive(Drop)] +struct Board { + pieces: Span, + num_rows: usize, + num_cols: usize, +} + +#[generate_trait] +impl BoardImpl of BoardTrait { + // Annotates the board by replacing empty squares with mine counts + fn annotated(self: @Board) -> Array { + let mut annotated_board: Array = array![]; + let num_rows = *self.num_rows; + + // Process each row + for y in 0 + ..num_rows { + let annotated_row = self.annotated_row(y); + annotated_board.append(annotated_row); + }; + + annotated_board + } + + // Annotates a single row by counting the neighboring mines + fn annotated_row(self: @Board, y: usize) -> ByteArray { + let mut row: ByteArray = ""; + let num_cols = *self.num_cols; + + // Process each cell in the row + for x in 0 + ..num_cols { + let c = self.pieces[y].at(x).expect('indexes should be correct'); + + // If it's an empty square (' '), count neighboring mines + if c == ' ' { + let char_count = self.count_neighbouring_mines_char(x, y); + row.append_byte(char_count); + } else { + row.append_byte(c); + } + }; + + row + } + + // Counts neighboring mines for a specific cell and returns the character representation + fn count_neighbouring_mines_char(self: @Board, x: usize, y: usize) -> u8 { + let mut count = 0; + let num_cols = self.num_cols; + let num_rows = self.num_rows; + + // Iterate through neighboring cells + for x1 in neighbouring_points( + x, num_cols + ) { + for y1 in neighbouring_points( + y, num_rows + ) { + let piece = self.pieces[y1].at(x1).expect('indexes should be correct'); + if piece == '*' { + count += 1; + } + } + }; + + // If no mines are adjacent, return ' ' (empty); otherwise, return the count as a char + if count == 0 { + ' ' + } else { + (count + '0') // Convert to character representation + } + } +} + +pub fn annotate(pieces: Span) -> Array { + if pieces.len() == 0 { + return array![]; + } + + let board = Board { pieces, num_rows: pieces.len(), num_cols: pieces.at(0).len(), }; + + board.annotated() +} + +// Helper function to return valid neighboring points for a given x or y coordinate +fn neighbouring_points(x: usize, limit: @usize) -> Array { + let mut offsets: Array = array![x]; + + if x >= 1 { + offsets.append(x - 1); + } + + if x + 1 < *limit { + offsets.append(x + 1); + } + + offsets +} diff --git a/exercises/practice/minesweeper/.meta/tests.toml b/exercises/practice/minesweeper/.meta/tests.toml new file mode 100644 index 00000000..2a142222 --- /dev/null +++ b/exercises/practice/minesweeper/.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. + +[0c5ec4bd-dea7-4138-8651-1203e1cb9f44] +description = "no rows" + +[650ac4c0-ad6b-4b41-acde-e4ea5852c3b8] +description = "no columns" + +[6fbf8f6d-a03b-42c9-9a58-b489e9235478] +description = "no mines" + +[61aff1c4-fb31-4078-acad-cd5f1e635655] +description = "minefield with only mines" + +[84167147-c504-4896-85d7-246b01dea7c5] +description = "mine surrounded by spaces" + +[cb878f35-43e3-4c9d-93d9-139012cccc4a] +description = "space surrounded by mines" + +[7037f483-ddb4-4b35-b005-0d0f4ef4606f] +description = "horizontal line" + +[e359820f-bb8b-4eda-8762-47b64dba30a6] +description = "horizontal line, mines at edges" + +[c5198b50-804f-47e9-ae02-c3b42f7ce3ab] +description = "vertical line" + +[0c79a64d-703d-4660-9e90-5adfa5408939] +description = "vertical line, mines at edges" + +[4b098563-b7f3-401c-97c6-79dd1b708f34] +description = "cross" + +[04a260f1-b40a-4e89-839e-8dd8525abe0e] +description = "large minefield" diff --git a/exercises/practice/minesweeper/Scarb.toml b/exercises/practice/minesweeper/Scarb.toml new file mode 100644 index 00000000..5493feed --- /dev/null +++ b/exercises/practice/minesweeper/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "minesweeper" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.8.2" diff --git a/exercises/practice/minesweeper/src/lib.cairo b/exercises/practice/minesweeper/src/lib.cairo new file mode 100644 index 00000000..0bdcb4a9 --- /dev/null +++ b/exercises/practice/minesweeper/src/lib.cairo @@ -0,0 +1,3 @@ +pub fn annotate(pieces: Span) -> Array { + panic!("implement `annotate`") +} diff --git a/exercises/practice/minesweeper/tests/minesweeper.cairo b/exercises/practice/minesweeper/tests/minesweeper.cairo new file mode 100644 index 00000000..478535ab --- /dev/null +++ b/exercises/practice/minesweeper/tests/minesweeper.cairo @@ -0,0 +1,190 @@ +use minesweeper::annotate; + +#[test] +fn no_rows() { + let input = array![]; + let expected = array![]; + let actual = annotate(input.span()); + assert_eq!(actual, expected); +} + +#[test] +#[ignore] +fn no_columns() { + let input = array![""]; + let expected = array![""]; + let actual = annotate(input.span()); + assert_eq!(actual, expected); +} + +#[test] +#[ignore] +fn no_mines() { + #[cairofmt::skip] + let (input, expected) = (array![ + " ", + " ", + " ", + ], array![ + " ", + " ", + " ", + ]); + let actual = annotate(input.span()); + assert_eq!(actual, expected); +} + +#[test] +#[ignore] +fn minefield_with_only_mines() { + #[cairofmt::skip] + let (input, expected) = (array![ + "***", + "***", + "***", + ], array![ + "***", + "***", + "***", + ]); + let actual = annotate(input.span()); + assert_eq!(actual, expected); +} + +#[test] +#[ignore] +fn mine_surrounded_by_spaces() { + #[cairofmt::skip] + let (input, expected) = (array![ + " ", + " * ", + " ", + ], array![ + "111", + "1*1", + "111", + ]); + let actual = annotate(input.span()); + assert_eq!(actual, expected); +} + +#[test] +#[ignore] +fn space_surrounded_by_mines() { + #[cairofmt::skip] + let (input, expected) = (array![ + "***", + "* *", + "***", + ], array![ + "***", + "*8*", + "***", + ]); + let actual = annotate(input.span()); + assert_eq!(actual, expected); +} + +#[test] +#[ignore] +fn horizontal_line() { + let input = array![" * * "]; + let expected = array!["1*2*1"]; + let actual = annotate(input.span()); + assert_eq!(actual, expected); +} + +#[test] +#[ignore] +fn horizontal_line_mines_at_edges() { + let input = array!["* *"]; + let expected = array!["*1 1*"]; + let actual = annotate(input.span()); + assert_eq!(actual, expected); +} + +#[test] +#[ignore] +fn vertical_line() { + #[cairofmt::skip] + let (input, expected) = (array![ + " ", + "*", + " ", + "*", + " ", + ], array![ + "1", + "*", + "2", + "*", + "1", + ]); + let actual = annotate(input.span()); + assert_eq!(actual, expected); +} + +#[test] +#[ignore] +fn vertical_line_mines_at_edges() { + #[cairofmt::skip] + let (input, expected) = (array![ + "*", + " ", + " ", + " ", + "*", + ], array![ + "*", + "1", + " ", + "1", + "*", + ]); + let actual = annotate(input.span()); + assert_eq!(actual, expected); +} + +#[test] +#[ignore] +fn cross() { + #[cairofmt::skip] + let (input, expected) = (array![ + " * ", + " * ", + "*****", + " * ", + " * ", + ], array![ + " 2*2 ", + "25*52", + "*****", + "25*52", + " 2*2 ", + ]); + let actual = annotate(input.span()); + assert_eq!(actual, expected); +} + +#[test] +#[ignore] +fn large_minefield() { + #[cairofmt::skip] + let (input, expected) = (array![ + " * * ", + " * ", + " * ", + " * *", + " * * ", + " ", + ], array![ + "1*22*1", + "12*322", + " 123*2", + "112*4*", + "1*22*2", + "111111", + ]); + let actual = annotate(input.span()); + assert_eq!(actual, expected); +} diff --git a/exercises/practice/nth-prime/.meta/config.json b/exercises/practice/nth-prime/.meta/config.json index 2bd9ccfa..d7f51841 100644 --- a/exercises/practice/nth-prime/.meta/config.json +++ b/exercises/practice/nth-prime/.meta/config.json @@ -1,5 +1,7 @@ { - "authors": ["Falilah"], + "authors": [ + "Falilah" + ], "files": { "solution": [ "src/lib.cairo" @@ -9,7 +11,7 @@ ], "example": [ ".meta/example.cairo" - ], + ], "invalidator": [ "Scarb.toml" ] diff --git a/exercises/practice/semi-structured-logs/.docs/hints.md b/exercises/practice/semi-structured-logs/.docs/hints.md index d50064c0..9bbcc1ed 100644 --- a/exercises/practice/semi-structured-logs/.docs/hints.md +++ b/exercises/practice/semi-structured-logs/.docs/hints.md @@ -7,8 +7,8 @@ ## 1. Emit semi-structured messages -- `match` comes in handy when working with enums. In this case, see how you - might use it when figuring how what kind of log message to generate. +- `match` comes in handy when working with enums. + In this case, see how you might use it when figuring how what kind of log message to generate. [tcb-enums]: https://book.cairo-lang.org/ch06-01-enums.html [tcb-match]: https://book.cairo-lang.org/ch06-02-the-match-control-flow-construct.html diff --git a/exercises/practice/series/.docs/instructions.md b/exercises/practice/series/.docs/instructions.md new file mode 100644 index 00000000..fd97a670 --- /dev/null +++ b/exercises/practice/series/.docs/instructions.md @@ -0,0 +1,19 @@ +# Instructions + +Given a string of digits, output all the contiguous substrings of length `n` in that string in the order that they appear. + +For example, the string "49142" has the following 3-digit series: + +- "491" +- "914" +- "142" + +And the following 4-digit series: + +- "4914" +- "9142" + +And if you ask for a 6-digit series from a 5-digit string, you deserve whatever you get. + +Note that these series are only required to occupy _adjacent positions_ in the input; +the digits need not be _numerically consecutive_. diff --git a/exercises/practice/series/.meta/config.json b/exercises/practice/series/.meta/config.json new file mode 100644 index 00000000..f02ee823 --- /dev/null +++ b/exercises/practice/series/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "0xNeshi" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/series.cairo" + ], + "example": [ + ".meta/example.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "Given a string of digits, output all the contiguous substrings of length `n` in that string.", + "source": "A subset of the Problem 8 at Project Euler", + "source_url": "https://projecteuler.net/problem=8" +} diff --git a/exercises/practice/series/.meta/example.cairo b/exercises/practice/series/.meta/example.cairo new file mode 100644 index 00000000..602d8d29 --- /dev/null +++ b/exercises/practice/series/.meta/example.cairo @@ -0,0 +1,19 @@ +pub fn slices(series: ByteArray, slice_length: usize) -> Array { + assert!(series.len() != 0, "series cannot be empty"); + assert!(slice_length != 0, "slice length cannot be zero"); + assert!(slice_length <= series.len(), "slice length cannot be greater than series length"); + + let max_start = series.len() - slice_length + 1; + + let mut all_slices = array![]; + for i in 0 + ..max_start { + let mut slice = ""; + for j in i..(i + slice_length) { + slice.append_byte(series[j]); + }; + all_slices.append(slice); + }; + + all_slices +} diff --git a/exercises/practice/series/.meta/tests.toml b/exercises/practice/series/.meta/tests.toml new file mode 100644 index 00000000..79345a10 --- /dev/null +++ b/exercises/practice/series/.meta/tests.toml @@ -0,0 +1,45 @@ +# 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. + +[7ae7a46a-d992-4c2a-9c15-a112d125ebad] +description = "slices of one from one" + +[3143b71d-f6a5-4221-aeae-619f906244d2] +description = "slices of one from two" + +[dbb68ff5-76c5-4ccd-895a-93dbec6d5805] +description = "slices of two" + +[19bbea47-c987-4e11-a7d1-e103442adf86] +description = "slices of two overlap" + +[8e17148d-ba0a-4007-a07f-d7f87015d84c] +description = "slices can include duplicates" + +[bd5b085e-f612-4f81-97a8-6314258278b0] +description = "slices of a long series" + +[6d235d85-46cf-4fae-9955-14b6efef27cd] +description = "slice length is too large" + +[d7957455-346d-4e47-8e4b-87ed1564c6d7] +description = "slice length is way too large" + +[d34004ad-8765-4c09-8ba1-ada8ce776806] +description = "slice length cannot be zero" + +[10ab822d-8410-470a-a85d-23fbeb549e54] +description = "slice length cannot be negative" +include = false +comment = "Cairo can exclude negative integers with its type system" + +[c7ed0812-0e4b-4bf3-99c4-28cbbfc246a2] +description = "empty series is invalid" diff --git a/exercises/practice/series/Scarb.toml b/exercises/practice/series/Scarb.toml new file mode 100644 index 00000000..024a38a1 --- /dev/null +++ b/exercises/practice/series/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "series" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.8.2" diff --git a/exercises/practice/series/src/lib.cairo b/exercises/practice/series/src/lib.cairo new file mode 100644 index 00000000..c8578313 --- /dev/null +++ b/exercises/practice/series/src/lib.cairo @@ -0,0 +1,3 @@ +pub fn slices(series: ByteArray, slice_length: usize) -> Array { + panic!("implement `slices`") +} diff --git a/exercises/practice/series/tests/series.cairo b/exercises/practice/series/tests/series.cairo new file mode 100644 index 00000000..23273596 --- /dev/null +++ b/exercises/practice/series/tests/series.cairo @@ -0,0 +1,96 @@ +use series::slices; + +#[test] +fn slices_of_one_from_one() { + let input = "1"; + let length = 1; + let output = slices(input, length); + let expected = array!["1"]; + assert_eq!(output, expected); +} + +#[test] +#[ignore] +fn slices_of_one_from_two() { + let input = "12"; + let length = 1; + let output = slices(input, length); + let expected = array!["1", "2"]; + assert_eq!(output, expected); +} + +#[test] +#[ignore] +fn slices_of_two() { + let input = "35"; + let length = 2; + let output = slices(input, length); + let expected = array!["35"]; + assert_eq!(output, expected); +} + +#[test] +#[ignore] +fn slices_of_two_overlap() { + let input = "9142"; + let length = 2; + let output = slices(input, length); + let expected = array!["91", "14", "42"]; + assert_eq!(output, expected); +} + +#[test] +#[ignore] +fn slices_can_include_duplicates() { + let input = "777777"; + let length = 3; + let output = slices(input, length); + let expected = array!["777", "777", "777", "777"]; + assert_eq!(output, expected); +} + +#[test] +#[ignore] +fn slices_of_a_long_series() { + let input = "918493904243"; + let length = 5; + let output = slices(input, length); + let expected = array!["91849", "18493", "84939", "49390", "93904", "39042", "90424", "04243",]; + assert_eq!(output, expected); +} + +#[test] +#[ignore] +#[should_panic(expected: ("slice length cannot be greater than series length",))] +fn slice_length_is_too_large() { + let input = "12345"; + let length = 6; + slices(input, length); +} + +#[test] +#[ignore] +#[should_panic(expected: ("slice length cannot be greater than series length",))] +fn slice_length_is_way_too_large() { + let input = "12345"; + let length = 42; + slices(input, length); +} + +#[test] +#[ignore] +#[should_panic(expected: ("slice length cannot be zero",))] +fn slice_length_cannot_be_zero() { + let input = "12345"; + let length = 0; + slices(input, length); +} + +#[test] +#[ignore] +#[should_panic(expected: ("series cannot be empty",))] +fn empty_series_is_invalid() { + let input = ""; + let length = 1; + slices(input, length); +} diff --git a/exercises/practice/space-age/.docs/hints.md b/exercises/practice/space-age/.docs/hints.md new file mode 100644 index 00000000..74f267ae --- /dev/null +++ b/exercises/practice/space-age/.docs/hints.md @@ -0,0 +1,21 @@ +# Hints + +## General + +- [The Cairo Book: Data Types][data-types] +- [The Cairo Book: Default Trait Implementations][default-implementation] +- [The Cairo Book: Associated Constants][associated-constants] + +## 1. Set each planet's `const PERIOD` to its appropriate value + +- Remember, there are not floating point numbers in Cairo! + +## 2. Write the default implementation for function `Planet::age` + +- All planets use the same age calculation algorithm, only their `const PERIOD` differs. +- The `age` method default implementation will automatically apply to all planets. +- Pay close attention to how you represent decimal values. + +[data-types]: https://book.cairo-lang.org/ch02-02-data-types.html +[default-implementation]: https://book.cairo-lang.org/ch08-02-traits-in-cairo.html?highlight=default#default-implementations +[associated-constants]: https://book.cairo-lang.org/ch11-10-associated-items.html?highlight=const#associated-constants diff --git a/exercises/practice/space-age/.docs/instructions.append.md b/exercises/practice/space-age/.docs/instructions.append.md new file mode 100644 index 00000000..2c5b11ca --- /dev/null +++ b/exercises/practice/space-age/.docs/instructions.append.md @@ -0,0 +1,26 @@ +# Instructions append + +In Cairo, where there's no native support for floating-point numbers, we represent fractional values using integers. + +This approach is essential in blockchain development to maintain precision in calculations. + +In this exercise, we use **fixed-point arithmetic** by converting orbital periods into microseconds. + +For example, Mercury’s orbital period of `0.2408467` Earth years becomes `240,846,700` microseconds by multiplying by `1,000,000`. + +To account for decimal precision, the test cases assume that the resulting age has **two decimal places**, represented as integers. + +This means that an age of `31.69` years is stored as `3169` in the code. + +To achieve this, we multiply by 100 before performing the division. + +Here's an example: + +```rust +let mercury_orbital_period = 240_846_700; // in microseconds +let age_microseconds = age_seconds * 1_000_000; +// multiplying with 100 to retain 2 decimal places +age_microseconds * 100 / mercury_orbital_period +``` + +By using this method, you ensure that fractional values are accurately represented as integers while maintaining the required two-decimal precision, which is crucial for the tests to pass. diff --git a/exercises/practice/space-age/.docs/instructions.md b/exercises/practice/space-age/.docs/instructions.md new file mode 100644 index 00000000..f23b5e2c --- /dev/null +++ b/exercises/practice/space-age/.docs/instructions.md @@ -0,0 +1,28 @@ +# Instructions + +Given an age in seconds, calculate how old someone would be on a planet in our Solar System. + +One Earth year equals 365.25 Earth days, or 31,557,600 seconds. +If you were told someone was 1,000,000,000 seconds old, their age would be 31.69 Earth-years. + +For the other planets, you have to account for their orbital period in Earth Years: + +| Planet | Orbital period in Earth Years | +| ------- | ----------------------------- | +| Mercury | 0.2408467 | +| Venus | 0.61519726 | +| Earth | 1.0 | +| Mars | 1.8808158 | +| Jupiter | 11.862615 | +| Saturn | 29.447498 | +| Uranus | 84.016846 | +| Neptune | 164.79132 | + +~~~~exercism/note +The actual length of one complete orbit of the Earth around the sun is closer to 365.256 days (1 sidereal year). +The Gregorian calendar has, on average, 365.2425 days. +While not entirely accurate, 365.25 is the value used in this exercise. +See [Year on Wikipedia][year] for more ways to measure a year. + +[year]: https://en.wikipedia.org/wiki/Year#Summary +~~~~ diff --git a/exercises/practice/space-age/.docs/introduction.md b/exercises/practice/space-age/.docs/introduction.md new file mode 100644 index 00000000..014d7885 --- /dev/null +++ b/exercises/practice/space-age/.docs/introduction.md @@ -0,0 +1,20 @@ +# Introduction + +The year is 2525 and you've just embarked on a journey to visit all planets in the Solar System (Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus and Neptune). +The first stop is Mercury, where customs require you to fill out a form (bureaucracy is apparently _not_ Earth-specific). +As you hand over the form to the customs officer, they scrutinize it and frown. +"Do you _really_ expect me to believe you're just 50 years old? +You must be closer to 200 years old!" + +Amused, you wait for the customs officer to start laughing, but they appear to be dead serious. +You realize that you've entered your age in _Earth years_, but the officer expected it in _Mercury years_! +As Mercury's orbital period around the sun is significantly shorter than Earth, you're actually a lot older in Mercury years. +After some quick calculations, you're able to provide your age in Mercury Years. +The customs officer smiles, satisfied, and waves you through. +You make a mental note to pre-calculate your planet-specific age _before_ future customs checks, to avoid such mix-ups. + +~~~~exercism/note +If you're wondering why Pluto didn't make the cut, go watch [this YouTube video][pluto-video]. + +[pluto-video]: https://www.youtube.com/watch?v=Z_2gbGXzFbs +~~~~ diff --git a/exercises/practice/space-age/.meta/config.json b/exercises/practice/space-age/.meta/config.json new file mode 100644 index 00000000..d46eb66a --- /dev/null +++ b/exercises/practice/space-age/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "0xNeshi" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/space_age.cairo" + ], + "example": [ + ".meta/example.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "Given an age in seconds, calculate how old someone is in terms of a given planet's solar years.", + "source": "Partially inspired by Chapter 1 in Chris Pine's online Learn to Program tutorial.", + "source_url": "https://pine.fm/LearnToProgram/?Chapter=01" +} diff --git a/exercises/practice/space-age/.meta/example.cairo b/exercises/practice/space-age/.meta/example.cairo new file mode 100644 index 00000000..a66b9415 --- /dev/null +++ b/exercises/practice/space-age/.meta/example.cairo @@ -0,0 +1,62 @@ +#[derive(Drop)] +pub struct Mercury {} +#[derive(Drop)] +pub struct Venus {} +#[derive(Drop)] +pub struct Earth {} +#[derive(Drop)] +pub struct Mars {} +#[derive(Drop)] +pub struct Jupiter {} +#[derive(Drop)] +pub struct Saturn {} +#[derive(Drop)] +pub struct Uranus {} +#[derive(Drop)] +pub struct Neptune {} + +const MICROSECONDS_IN_A_SECOND: u64 = 1_000_000; + +pub trait Planet { + const PERIOD: u256; + fn age( + self: @T, seconds: u256 + ) -> u256 { + let microseconds = seconds * MICROSECONDS_IN_A_SECOND.into(); + // multiplying with 100 before the division makes the result + // include the first 2 decimals of the division result + microseconds * 100 / Self::PERIOD + } +} + +impl MercuryPlanet of Planet { + const PERIOD: u256 = 7_600_543_819_920; +} + +impl VenusPlanet of Planet { + const PERIOD: u256 = 19_414_149_052_176; +} + +impl EarthPlanet of Planet { + const PERIOD: u256 = 31_557_600_000_000; +} + +impl MarsPlanet of Planet { + const PERIOD: u256 = 59_354_032_690_080; +} + +impl JupiterPlanet of Planet { + const PERIOD: u256 = 374_355_659_124_000; +} + +impl SaturnPlanet of Planet { + const PERIOD: u256 = 929_292_362_884_800; +} + +impl UranusPlanet of Planet { + const PERIOD: u256 = 2_651_370_019_329_600; +} + +impl NeptunePlanet of Planet { + const PERIOD: u256 = 5_200_418_560_032_000; +} diff --git a/exercises/practice/space-age/.meta/tests.toml b/exercises/practice/space-age/.meta/tests.toml new file mode 100644 index 00000000..3cac30bd --- /dev/null +++ b/exercises/practice/space-age/.meta/tests.toml @@ -0,0 +1,39 @@ +# 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. + +[84f609af-5a91-4d68-90a3-9e32d8a5cd34] +description = "age on Earth" + +[ca20c4e9-6054-458c-9312-79679ffab40b] +description = "age on Mercury" + +[502c6529-fd1b-41d3-8fab-65e03082b024] +description = "age on Venus" + +[9ceadf5e-a0d5-4388-9d40-2c459227ceb8] +description = "age on Mars" + +[42927dc3-fe5e-4f76-a5b5-f737fc19bcde] +description = "age on Jupiter" + +[8469b332-7837-4ada-b27c-00ee043ebcad] +description = "age on Saturn" + +[999354c1-76f8-4bb5-a672-f317b6436743] +description = "age on Uranus" + +[80096d30-a0d4-4449-903e-a381178355d8] +description = "age on Neptune" + +[57b96e2a-1178-40b7-b34d-f3c9c34e4bf4] +description = "invalid planet causes error" +include = false +comment = "Cairo's type system can exclude invalid planets" diff --git a/exercises/practice/space-age/Scarb.toml b/exercises/practice/space-age/Scarb.toml new file mode 100644 index 00000000..6187f611 --- /dev/null +++ b/exercises/practice/space-age/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "space_age" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.8.2" diff --git a/exercises/practice/space-age/src/lib.cairo b/exercises/practice/space-age/src/lib.cairo new file mode 100644 index 00000000..77ff72fe --- /dev/null +++ b/exercises/practice/space-age/src/lib.cairo @@ -0,0 +1,55 @@ +#[derive(Drop)] +pub struct Mercury {} +#[derive(Drop)] +pub struct Venus {} +#[derive(Drop)] +pub struct Earth {} +#[derive(Drop)] +pub struct Mars {} +#[derive(Drop)] +pub struct Jupiter {} +#[derive(Drop)] +pub struct Saturn {} +#[derive(Drop)] +pub struct Uranus {} +#[derive(Drop)] +pub struct Neptune {} + +pub trait Planet { + const PERIOD: u256; + fn age(self: @T, seconds: u256) -> u256 { + panic!("implement `Planet::age`") + } +} + +impl MercuryPlanet of Planet { + const PERIOD: u256 = 0; // UPDATE THIS TO ACTUAL PERIOD +} + +impl VenusPlanet of Planet { + const PERIOD: u256 = 0; // UPDATE THIS TO ACTUAL PERIOD +} + +impl EarthPlanet of Planet { + const PERIOD: u256 = 0; // UPDATE THIS TO ACTUAL PERIOD +} + +impl MarsPlanet of Planet { + const PERIOD: u256 = 0; // UPDATE THIS TO ACTUAL PERIOD +} + +impl JupiterPlanet of Planet { + const PERIOD: u256 = 0; // UPDATE THIS TO ACTUAL PERIOD +} + +impl SaturnPlanet of Planet { + const PERIOD: u256 = 0; // UPDATE THIS TO ACTUAL PERIOD +} + +impl UranusPlanet of Planet { + const PERIOD: u256 = 0; // UPDATE THIS TO ACTUAL PERIOD +} + +impl NeptunePlanet of Planet { + const PERIOD: u256 = 0; // UPDATE THIS TO ACTUAL PERIOD +} diff --git a/exercises/practice/space-age/tests/space_age.cairo b/exercises/practice/space-age/tests/space_age.cairo new file mode 100644 index 00000000..596e94be --- /dev/null +++ b/exercises/practice/space-age/tests/space_age.cairo @@ -0,0 +1,79 @@ +use space_age::{Planet, Earth, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune}; + +fn assert_in_delta(expected: u256, actual: u256) { + // absolute value of the difference + let diff: u256 = if expected > actual { + expected - actual + } else { + actual - expected + }; + let delta: u256 = 10000; // 0.01 seconds + if diff > delta { + panic!( + "Your result of {actual} should be within {delta} of the expected result {expected}" + ); + }; +} + +#[test] +fn age_on_earth() { + let seconds = 1_000_000_000; + let expected = 3169; + assert_in_delta(Earth {}.age(seconds), expected); +} + +#[test] +#[ignore] +fn age_on_mercury() { + let seconds = 2_134_835_688; + let expected = 28088; + assert_in_delta(Mercury {}.age(seconds), expected); +} + +#[test] +#[ignore] +fn age_on_venus() { + let seconds = 189_839_836; + let expected = 978; + assert_in_delta(Venus {}.age(seconds), expected); +} + +#[test] +#[ignore] +fn age_on_mars() { + let seconds = 2_129_871_239; + let expected = 3588; + assert_in_delta(Mars {}.age(seconds), expected); +} + +#[test] +#[ignore] +fn age_on_jupiter() { + let seconds = 901_876_382; + let expected = 241; + assert_in_delta(Jupiter {}.age(seconds), expected); +} + +#[test] +#[ignore] +fn age_on_saturn() { + let seconds = 2_000_000_000; + let expected = 215; + assert_in_delta(Saturn {}.age(seconds), expected); +} + +#[test] +#[ignore] +fn age_on_uranus() { + let seconds = 1_210_123_456; + let expected = 46; + assert_in_delta(Uranus {}.age(seconds), expected); +} + +#[test] +#[ignore] +fn age_on_neptune() { + let seconds = 1_821_023_456; + let expected = 35; + assert_in_delta(Neptune {}.age(seconds), expected); +} diff --git a/exercises/practice/sum-of-multiples/.docs/instructions.md b/exercises/practice/sum-of-multiples/.docs/instructions.md new file mode 100644 index 00000000..d69f890e --- /dev/null +++ b/exercises/practice/sum-of-multiples/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Your task is to write the code that calculates the energy points that get awarded to players when they complete a level. + +The points awarded depend on two things: + +- The level (a number) that the player completed. +- The base value of each magical item collected by the player during that level. + +The energy points are awarded according to the following rules: + +1. For each magical item, take the base value and find all the multiples of that value that are less than the level number. +2. Combine the sets of numbers. +3. Remove any duplicates. +4. Calculate the sum of all the numbers that are left. + +Let's look at an example: + +**The player completed level 20 and found two magical items with base values of 3 and 5.** + +To calculate the energy points earned by the player, we need to find all the unique multiples of these base values that are less than level 20. + +- Multiples of 3 less than 20: `{3, 6, 9, 12, 15, 18}` +- Multiples of 5 less than 20: `{5, 10, 15}` +- Combine the sets and remove duplicates: `{3, 5, 6, 9, 10, 12, 15, 18}` +- Sum the unique multiples: `3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 = 78` +- Therefore, the player earns **78** energy points for completing level 20 and finding the two magical items with base values of 3 and 5. diff --git a/exercises/practice/sum-of-multiples/.docs/introduction.md b/exercises/practice/sum-of-multiples/.docs/introduction.md new file mode 100644 index 00000000..69cabeed --- /dev/null +++ b/exercises/practice/sum-of-multiples/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +You work for a company that makes an online, fantasy-survival game. + +When a player finishes a level, they are awarded energy points. +The amount of energy awarded depends on which magical items the player found while exploring that level. diff --git a/exercises/practice/sum-of-multiples/.meta/config.json b/exercises/practice/sum-of-multiples/.meta/config.json new file mode 100644 index 00000000..e706b33f --- /dev/null +++ b/exercises/practice/sum-of-multiples/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "Falilah" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/sum_of_multiples.cairo" + ], + "example": [ + ".meta/example.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "Given a number, find the sum of all the multiples of particular numbers up to but not including that number.", + "source": "A variation on Problem 1 at Project Euler", + "source_url": "https://projecteuler.net/problem=1" +} diff --git a/exercises/practice/sum-of-multiples/.meta/example.cairo b/exercises/practice/sum-of-multiples/.meta/example.cairo new file mode 100644 index 00000000..073deca6 --- /dev/null +++ b/exercises/practice/sum-of-multiples/.meta/example.cairo @@ -0,0 +1,39 @@ +pub fn sum(limit: u32, factors: Array) -> u32 { + let mut multiples: Array = array![]; + + for f in factors { + if f > 0 { + let mut multiplier = 1; + let mut x = f; + + while x < limit { + if !contains(@multiples, x) { + multiples.append(x); + }; + + multiplier += 1; + x = f * multiplier; + } + } + }; + + let mut total_sum = 0; + for m in multiples { + total_sum += m; + }; + + total_sum +} + +fn contains(arr: @Array, value: u32) -> bool { + let mut i = 0; + let mut result = false; + while i < arr.len() { + if arr[i] == @value { + result = true; + break; + } + i += 1; + }; + result +} diff --git a/exercises/practice/sum-of-multiples/.meta/tests.toml b/exercises/practice/sum-of-multiples/.meta/tests.toml new file mode 100644 index 00000000..89e441c0 --- /dev/null +++ b/exercises/practice/sum-of-multiples/.meta/tests.toml @@ -0,0 +1,59 @@ +# 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. + +[54aaab5a-ce86-4edc-8b40-d3ab2400a279] +description = "no multiples within limit" + +[361e4e50-c89b-4f60-95ef-5bc5c595490a] +description = "one factor has multiples within limit" + +[e644e070-040e-4ae0-9910-93c69fc3f7ce] +description = "more than one multiple within limit" + +[607d6eb9-535c-41ce-91b5-3a61da3fa57f] +description = "more than one factor with multiples within limit" + +[f47e8209-c0c5-4786-b07b-dc273bf86b9b] +description = "each multiple is only counted once" + +[28c4b267-c980-4054-93e9-07723db615ac] +description = "a much larger limit" + +[09c4494d-ff2d-4e0f-8421-f5532821ee12] +description = "three factors" + +[2d0d5faa-f177-4ad6-bde9-ebb865083751] +description = "factors not relatively prime" + +[ece8f2e8-96aa-4166-bbb7-6ce71261e354] +description = "some pairs of factors relatively prime and some not" + +[624fdade-6ffb-400e-8472-456a38c171c0] +description = "one factor is a multiple of another" + +[949ee7eb-db51-479c-b5cb-4a22b40ac057] +description = "much larger factors" + +[41093673-acbd-482c-ab80-d00a0cbedecd] +description = "all numbers are multiples of 1" + +[1730453b-baaa-438e-a9c2-d754497b2a76] +description = "no factors means an empty sum" + +[214a01e9-f4bf-45bb-80f1-1dce9fbb0310] +description = "the only multiple of 0 is 0" + +[c423ae21-a0cb-4ec7-aeb1-32971af5b510] +description = "the factor 0 does not affect the sum of multiples of other factors" + +[17053ba9-112f-4ac0-aadb-0519dd836342] +description = "solutions using include-exclude must extend to cardinality greater than 3" +comment = "decreased the limit input value to fit into Cairo's runtime limitation" diff --git a/exercises/practice/sum-of-multiples/Scarb.toml b/exercises/practice/sum-of-multiples/Scarb.toml new file mode 100644 index 00000000..9c7265c8 --- /dev/null +++ b/exercises/practice/sum-of-multiples/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "sum_of_multiples" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.8.2" diff --git a/exercises/practice/sum-of-multiples/src/lib.cairo b/exercises/practice/sum-of-multiples/src/lib.cairo new file mode 100644 index 00000000..960cb7ac --- /dev/null +++ b/exercises/practice/sum-of-multiples/src/lib.cairo @@ -0,0 +1,3 @@ +pub fn sum(limit: u32, factors: Array) -> u32 { + panic!("implement `sum_of_multiples`") +} diff --git a/exercises/practice/sum-of-multiples/tests/sum_of_multiples.cairo b/exercises/practice/sum-of-multiples/tests/sum_of_multiples.cairo new file mode 100644 index 00000000..a60be78a --- /dev/null +++ b/exercises/practice/sum-of-multiples/tests/sum_of_multiples.cairo @@ -0,0 +1,144 @@ +use sum_of_multiples::sum; + +#[test] +fn no_multiples_within_limit() { + let factors: Array = array![3, 5]; + let limit = 1; + let output = sum(limit, factors); + assert_eq!(output, 0); +} + +#[test] +#[ignore] +fn one_factor_has_multiples_within_limit() { + let factors: Array = array![3, 5]; + let limit = 4; + let output = sum(limit, factors); + assert_eq!(output, 3); +} + +#[test] +#[ignore] +fn more_than_one_multiple_within_limit() { + let factors: Array = array![3]; + let limit = 7; + let output = sum(limit, factors); + assert_eq!(output, 9); +} + +#[test] +#[ignore] +fn more_than_one_factor_with_multiples_within_limit() { + let factors: Array = array![3, 5]; + let limit = 10; + let output = sum(limit, factors); + assert_eq!(output, 23); +} + +#[test] +#[ignore] +fn each_multiple_is_only_counted_once() { + let factors: Array = array![3, 5]; + let limit = 100; + let output = sum(limit, factors); + assert_eq!(output, 2318); +} + +#[test] +#[ignore] +fn a_much_larger_limit() { + let factors: Array = array![3, 5]; + let limit = 1000; + let output = sum(limit, factors); + assert_eq!(output, 233168); +} + +#[test] +#[ignore] +fn three_factors() { + let factors: Array = array![7, 13, 17]; + let limit = 20; + let output = sum(limit, factors); + assert_eq!(output, 51); +} + +#[test] +#[ignore] +fn factors_not_relatively_prime() { + let factors: Array = array![4, 6]; + let limit = 15; + let output = sum(limit, factors); + assert_eq!(output, 30); +} + +#[test] +#[ignore] +fn some_pairs_of_factors_relatively_prime_and_some_not() { + let factors: Array = array![5, 6, 8]; + let limit = 150; + let output = sum(limit, factors); + assert_eq!(output, 4419); +} + +#[test] +#[ignore] +fn one_factor_is_a_multiple_of_another() { + let factors: Array = array![5, 25]; + let limit = 51; + let output = sum(limit, factors); + assert_eq!(output, 275); +} + +#[test] +#[ignore] +fn much_larger_factors() { + let factors: Array = array![43, 47]; + let limit = 10000; + let output = sum(limit, factors); + assert_eq!(output, 2203160); +} + +#[test] +#[ignore] +fn all_numbers_are_multiples_of_1() { + let factors: Array = array![1]; + let limit = 100; + let output = sum(limit, factors); + assert_eq!(output, 4950); +} + +#[test] +#[ignore] +fn no_factors_means_an_empty_sum() { + let factors: Array = array![]; + let limit = 10000; + let output = sum(limit, factors); + assert_eq!(output, 0); +} + +#[test] +#[ignore] +fn the_only_multiple_of_0_is_0() { + let factors: Array = array![0]; + let limit = 1; + let output = sum(limit, factors); + assert_eq!(output, 0); +} + +#[test] +#[ignore] +fn the_factor_0_does_not_affect_the_sum_of_multiples_of_other_factors() { + let factors: Array = array![3, 0]; + let limit = 4; + let output = sum(limit, factors); + assert_eq!(output, 3); +} + +#[test] +#[ignore] +fn solutions_using_include_exclude_must_extend_to_cardinality_greater_than_3() { + let factors: Array = array![2, 3, 5, 7, 11]; + let limit = 1500; + let output = sum(limit, factors); + assert_eq!(output, 888403); +} diff --git a/exercises/practice/two-fer/.docs/instructions.append.md b/exercises/practice/two-fer/.docs/instructions.append.md index 1d3ea913..3af9b0ac 100644 --- a/exercises/practice/two-fer/.docs/instructions.append.md +++ b/exercises/practice/two-fer/.docs/instructions.append.md @@ -5,5 +5,6 @@ In Cairo, the `Option` enumeration is used to represent the concept of a value being absent or present. There are two variants: + - `Option::None` takes no asssociated value and represents no data being present. - `Option::Some` takes an associated value and represents that value being represented like `Option::Some(value)`. diff --git a/single-sentence-per-line-rule.js b/single-sentence-per-line-rule.js new file mode 100644 index 00000000..459508f9 --- /dev/null +++ b/single-sentence-per-line-rule.js @@ -0,0 +1,105 @@ +// @ts-check + +"use strict"; + +// BUG: for some reason this rule does not detect sentences starting with *, _ or ` +// so these have to be manually placed on new lines + +const indentFor = (string, indentation) => { + const regex = new RegExp( + "^(?(" + indentation + ")*)(?- |> |>|\\* |\\d+\\. )?", + ); + const match = regex.exec(string); + if (!match) { + return ""; + } + let indentSize = 0; + if (match.groups?.indents) { + indentSize = match.groups.indents.length / indentation.length; + } + if (match.groups?.adds) { + if (match.groups.adds.includes(">")) { + return indentation.repeat(indentSize) + match.groups.adds; + } + indentSize++; + } + return indentation.repeat(indentSize); +}; + +const isAfterIgnoredWord = (ignoredWords, line, i) => { + for (const ignoredWord of ignoredWords) { + const lastWordInLine = line.substring(i - ignoredWord.length, i); + if (ignoredWord === lastWordInLine.toLowerCase()) { + return true; + } + } + + return false; +}; + +module.exports = { + names: ["max-one-sentence-per-line"], + description: "Max 1 sentence should be on a line", + information: new URL( + "https://github.com/aepfli/markdownlint-rule-max-one-sentence-per-line", + ), + tags: ["sentences"], + function: (params, onError) => { + const ignoredWords = params.config.ignored_words || [ + "ie", + "i.e", + "eg", + "e.g", + "etc", + "ex", + ]; + const lineEndings = params.config.line_endings || [".", "?", "!"]; + const sentenceStartRegex = + params.config.sentence_start || "^\\s+(\\w|[*_'\"`])"; + const indentation = params.config.indentation || " "; + + const sentenceStart = new RegExp(sentenceStartRegex); + + const relevantTokens = []; + for (let i = 0; i < params.tokens.length; i++) { + const token = params.tokens[i]; + if ( + token.type === "paragraph_open" && + params.tokens[i + 1].type === "inline" + ) { + relevantTokens.push(params.tokens[i + 1]); + } + } + + for (const relevantToken of relevantTokens) { + for (const token of relevantToken.children) { + const lineNumber = token.lineNumber; + if (token.type === "text") { + const content = token.content; + for (let i = 0; i < content.length - 2; i += 1) { + if (lineEndings.includes(content[i])) { + const sentence = sentenceStart.exec(content.substr(i + 1)); + if ( + sentence !== null && + !isAfterIgnoredWord(ignoredWords, content, i) + ) { + const spaces = sentence[1]; + const pointInLine = token.line.indexOf(content) + i; + onError({ + lineNumber, // Report error on paragraph start + detail: "Each sentence should be on its own line.", + fixInfo: { + editColumn: pointInLine + 2, + deleteCount: spaces.length, + insertText: + "\n" + indentFor(relevantToken.line, indentation), + } + }); + } + } + } + } + } + } + }, +};