Skip to content

Commit

Permalink
Welcome to tech palace (#342) [no important files changed]
Browse files Browse the repository at this point in the history
* remove cars-assemble

* init

* introduction + new links for strings and felts

* update prereq to arrays

* update prereq to control-flow + move enums to rpn-calculator

* start exemplar

* implement exemplar

* add hints

* prerequisite `control-flow` instead of `array`

* fix lint errors instructions.md

* remove trailing space in introduction.md

* Apply suggestions from code review

Co-authored-by: András B Nagy <20251272+BNAndras@users.noreply.github.com>

---------

Co-authored-by: András B Nagy <20251272+BNAndras@users.noreply.github.com>
  • Loading branch information
0xNeshi and BNAndras authored Feb 25, 2025
1 parent 98caeeb commit 58525f9
Show file tree
Hide file tree
Showing 12 changed files with 437 additions and 23 deletions.
4 changes: 4 additions & 0 deletions concepts/felts/links.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"url": "https://book.cairo-lang.org/ch02-02-data-types.html#felt-type",
"description": "Felt type in the Cairo book"
},
{
"url": "https://docs.starknet.io/architecture-and-concepts/smart-contracts/serialization-of-cairo-types/#data_types_of_252_bits_or_less",
"description": "Explanation for how certain data types get serialized into felt252"
},
{
"url": "https://starknet-by-example.voyager.online/getting-started/cairo_cheatsheet/felt.html",
"description": "Starknet by Example section on felts"
Expand Down
8 changes: 2 additions & 6 deletions concepts/strings/links.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@
"description": "String types in the Cairo book"
},
{
"url": "https://starknet-by-example.voyager.online/getting-started/basics/bytearrays-strings.html",
"description": "Starknet by Example section on Strings and ByteArrays"
"url": "https://docs.swmansion.com/scarb/corelib/core-byte_array-ByteArrayTrait.html",
"description": "Operations defined on ByteArray"
},
{
"url": "https://docs.starknet.io/architecture-and-concepts/smart-contracts/serialization-of-cairo-types/#serialization_of_byte_arrays",
"description": "Starknet docs explaining how ByteArray is implemented and how it's serialized"
},
{
"url": "https://community.starknet.io/t/cairo-v2-4-0-is-out/109275",
"description": "Cairo v2.4.0 is out"
}
]
30 changes: 15 additions & 15 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,6 @@
],
"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",
Expand All @@ -102,11 +90,23 @@
"name": "RPN Calculator",
"uuid": "536d9f09-5910-4a26-93fd-2242667b0b87",
"concepts": [
"control-flow"
"control-flow",
"enums"
],
"prerequisites": [
"arrays",
"enums"
"arrays"
],
"status": "wip"
},
{
"slug": "welcome-to-tech-palace",
"name": "Welcome To Tech Palace!",
"uuid": "f8108950-9819-4540-8a3a-880d0778806c",
"concepts": [
"strings"
],
"prerequisites": [
"control-flow"
],
"status": "wip"
},
Expand Down
24 changes: 24 additions & 0 deletions exercises/concept/welcome-to-tech-palace/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Hints

## General

- The [ByteArray][bytearray] type contains a few useful inbuilt methods, so you'll generally have to implement your own utility functions for manipulating ByteArrays.
- `ByteArray` actually represents an array of bytes and not "chars" as in many other languages.
- Working with both short and long strings at the same time can be challenging, so some helper functions are already implemented which you can use for this exercise.

## 1. Create the welcome message

- Strings can be concatenated using the `+` operator.
- Check if you can use any of the helper functions.

## 2. Add a fancy border

- You can append `'*'` characters in a loop.
- A newline is a special escape character.

## 3. Clean up old marketing messages

- Find the start and end indices of the clean message.
- You will probably need to copy the clean message characters into a new ByteArray one by one.

[bytearray]: https://docs.swmansion.com/scarb/corelib/core-byte_array-ByteArrayTrait.html
61 changes: 61 additions & 0 deletions exercises/concept/welcome-to-tech-palace/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Instructions

There is an appliance store called "Tech Palace" nearby.
The owner of the store recently installed a big display to use for marketing messages and to show a special greeting when customers scan their loyalty cards at the entrance.
The display consists of lots of small LED lights and can show multiple lines of text.

The store owner needs your help with the code that is used to generate the text for the new display.

## 1. Create the welcome message

<!-- markdownlint-disable-next-line MD038 -->
For most customers who scan their loyalty cards, the store owner wants to see `Welcome to the Tech Palace, ` followed by the name of the customer in capital letters on the display.

Implement the function `welcome_message` that accepts the name of the customer as a short string (`felt252`) argument and returns the desired message as a `ByteArray`.

```rust
welcome_message("Judy")
// => Welcome to the Tech Palace, JUDY
```

## 2. Add a fancy border

For loyal customers that buy a lot at the store, the owner wants the welcome display to be more fancy by adding a line of stars before and after the welcome message.
They are not sure yet how many stars should be in the lines so they want that to be configurable.

Write a function `add_border` that accepts a welcome message (a `ByteArray`) and the number of stars per line (type `u32`) as arguments.
It should return a `ByteArray` that consists of 3 lines, a line with the desired number of stars, then the welcome message as it was passed in, then another line of stars.

```rust
add_border("Welcome!", 10)
```

Should return the following:

```bash
**********
Welcome!
**********
```

## 3. Clean up old marketing messages

Before installing this new display, the store had a similar display that could only show non-configurable, static lines.
The owner would like to reuse some of the old marketing messages on the new display.
However, the data already includes a star border and some unfortunate whitespaces.
Your task is to clean up the messages so they can be re-used.

Implement a function `clean_up_message` that accepts the old marketing message as a `ByteArray`.
The function should first remove all stars from the text and afterwards remove the leading and trailing whitespaces from the remaining text.
The function should then return the cleaned up message.

```rust
let message: ByteArray = "
**************************
* BUY NOW, SAVE 10% *
**************************
";

clean_up_message(message)
// => BUY NOW, SAVE 10%
```
76 changes: 76 additions & 0 deletions exercises/concept/welcome-to-tech-palace/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Introduction

Cairo doesn't have a native type for strings but provides two ways to handle them: short strings using simple quotes and `ByteArray` using double quotes.

A short string is an ASCII string where each character is encoded on one byte.

The type Cairo uses for short strings by default is `felt252`, making their maximum length only 31 characters.

```rust
// below are different ways to write the same value
let name: felt252 = 'Jane'; // short string format
let name: felt252 = 0x4a616e65; // byte format
let name: felt252 = 1247899237; // decimal format
```

Technically, any integer type smaller than `felt252` can be represented using the short string format.

```rust
// all of the below are equal u8 values, they are just written using different formats!
let lowercase_a_in_ascii: u8 = 'a'; // short string format
let lowercase_a_in_ascii: u8 = 0x61; // byte format
let lowercase_a_in_ascii: u8 = 97; // decimal format
```

For strings longer than 31 characters Cairo provides `ByteArray`, which has an unlimited length.

```rust
let long_string: ByteArray = "this is a string which has more than 31 characters";
```

ByteArrays can be concatenated via the `+` operator:

```rust
"Jane" + " " + "Austen"
// => "Jane Austen"
```

Some special characters need to be escaped with a leading backslash, such as `\t` for a tab and `\n` for a new line in strings.

```rust
"How is the weather today?\nIt's sunny"
// =>
// How is the weather today?
// It's sunny
```

You can even access individual bytes within a ByteArray using its index.

Be careful though, the type of the returned byte is `u8` and not `ByteArray`!

```rust
let hello: ByteArray = "Hello World!";
let exclamation_mark: u8 = hello[11];
// => 33
```

The core library provides many useful methods and operators to work on ByteArrays.

For more information about ByteArray methods, check out the [ByteArrayTrait documentation][docs].

Here are some examples:

```rust
let name: felt252 = 'Jane';
let mut greeting: ByteArray = "Welcome ";

// append_word appends a single word of a given number of bytes to the end of the ByteArray
greeting.append_word(name, 4);
// => "Welcome Jane"

// rev returns a ByteArray with the all characters reversed
let rev_greeting = greeting.rev();
// => "enaJ emocleW"
```

[docs]: https://docs.swmansion.com/scarb/corelib/core-byte_array-ByteArrayTrait.html
4 changes: 2 additions & 2 deletions exercises/concept/welcome-to-tech-palace/.meta/config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"authors": [
"<your_gh_username>"
"0xNeshi"
],
"files": {
"solution": [
Expand All @@ -19,5 +19,5 @@
"forked_from": [
"go/welcome-to-tech-palace"
],
"blurb": "<blurb>"
"blurb": "Learn how to represent strings by generating text for a store's new display."
}
26 changes: 26 additions & 0 deletions exercises/concept/welcome-to-tech-palace/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Design

## Goal

The goal of this exercise is to teach the student the basics of strings and the `strings` package in Go.

## Learning objectives

- Know how to define short strings
- Know how to define Byte Array strings
- Know how to concatenate different string types
- Know how to use escape characters

## Out of scope

- String formatting

## Concepts

The Concepts this exercise unlocks are:

- `strings`

## Prerequisites

- `control-flow`
76 changes: 76 additions & 0 deletions exercises/concept/welcome-to-tech-palace/.meta/exemplar.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Returns a welcome message for the customer.
pub fn welcome_message(customer: felt252) -> ByteArray {
"Welcome to the Tech Palace, " + to_uppercase(customer.into())
}

// Adds a border to a welcome message.
pub fn add_border(welcome_msg: ByteArray, num_stars_per_line: u32) -> ByteArray {
let mut border: ByteArray = "";
for _ in 0..num_stars_per_line {
border.append_byte('*');
};
border.clone() + "\n" + welcome_msg + "\n" + border
}

// Cleans up an old marketing message.
pub fn clean_up_message(old_msg: ByteArray) -> ByteArray {
let mut start = 0;
while is_whitespace(old_msg[start]) || old_msg[start] == '*' {
start += 1;
};
let mut end = old_msg.len();
while is_whitespace(old_msg[end - 1]) || old_msg[end - 1] == '*' {
end -= 1;
};
let mut clean_msg = "";
for i in start..end {
clean_msg.append_byte(old_msg[i]);
};
clean_msg
}

///////////////
/// Helpers ///
///////////////

// Distance between a lowercase and uppercase representations
// of the same character in the ASCII table
const ASCII_CASE_OFFSET: u8 = 32;
const BYTE_SIZE: u256 = 256; // 2^8, number of possible values in a byte
const BYTE_MASK: u256 = 0xff; // Mask to extract the last byte (8 bits)

fn to_uppercase(input: u256) -> ByteArray {
let mut remaining_bytes = input;
let mut uppercase_chars: ByteArray = "";
while remaining_bytes != 0 {
uppercase_chars.append_byte(char_to_uppercase(get_last_byte(remaining_bytes)));
remaining_bytes = remove_last_byte(remaining_bytes);
};
uppercase_chars.rev()
}

fn get_last_byte(chars: u256) -> u8 {
(chars & BYTE_MASK.into()).try_into().unwrap()
}

fn remove_last_byte(chars: u256) -> u256 {
chars / BYTE_SIZE.into()
}

fn char_to_uppercase(c: u8) -> u8 {
if is_lowercase(c) {
c - ASCII_CASE_OFFSET
} else {
c
}
}

fn is_lowercase(c: u8) -> bool {
// This comparison is valid because the type of the value in the short
// string format gets automatically inferred by the compiler
c >= 'a' && c <= 'z'
}

fn is_whitespace(chr: u8) -> bool {
chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r'
}
7 changes: 7 additions & 0 deletions exercises/concept/welcome-to-tech-palace/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "welcome_to_tech_palace"
version = "0.1.0"
edition = "2024_07"

[dev-dependencies]
cairo_test = "2.9.2"
Loading

0 comments on commit 58525f9

Please sign in to comment.