feat(curriculum): add Caesar cipher workshop (#60198)

Co-authored-by: Ilenia <26656284+ilenia-magoni@users.noreply.github.com>
Co-authored-by: Zaira <33151350+zairahira@users.noreply.github.com>
Co-authored-by: Kolade Chris <65571316+Ksound22@users.noreply.github.com>
This commit is contained in:
Dario-DC
2025-05-30 13:36:11 +02:00
committed by GitHub
parent 8fe6064013
commit 4bf9754ee1
28 changed files with 1370 additions and 1 deletions
+3 -1
View File
@@ -3742,7 +3742,9 @@
},
"workshop-caesar-cipher": {
"title": "Build a Caesar Cipher",
"intro": ["Learn about Build a Caesar Cipher in these lectures."]
"intro": [
"In this workshop, you'll build a Caesar cipher using basic Python concepts such as strings, conditionals, functions, and more."
]
},
"lab-rpg-character": {
"title": "Build an RPG character",
@@ -0,0 +1,9 @@
---
title: Introduction to the Build a Caesar Cipher
block: workshop-caesar-cipher
superBlock: full-stack-developer
---
## Introduction to the Build a Caesar Cipher
In this workshop, you'll build a Caesar cipher using basic Python concepts such as strings, conditionals, functions, and more.
@@ -0,0 +1,113 @@
{
"name": "Build a Caesar Cipher",
"isUpcomingChange": true,
"usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "workshop-caesar-cipher",
"superBlock": "full-stack-developer",
"challengeOrder": [
{
"id": "680a3d4c065557260de2ff33",
"title": "Step 1"
},
{
"id": "680a41ef5bfdc059cfd9d4b5",
"title": "Step 2"
},
{
"id": "680a431692a5f863f726c438",
"title": "Step 3"
},
{
"id": "680a549f60f5a3fedfc0edd9",
"title": "Step 4"
},
{
"id": "680a5e7f3d47715196b12ca0",
"title": "Step 5"
},
{
"id": "680a7e200400089967b8b1f4",
"title": "Step 6"
},
{
"id": "680a82806c4c6bc3567f2708",
"title": "Step 7"
},
{
"id": "680a836235461fce1ff53b3f",
"title": "Step 8"
},
{
"id": "6810869355fa2468f9ebaf9c",
"title": "Step 9"
},
{
"id": "6810891bd6de8a87a833df42",
"title": "Step 10"
},
{
"id": "6818b80519e6c6c47a8bb6e8",
"title": "Step 11"
},
{
"id": "6818e9c7c8dc5694e8f64fc3",
"title": "Step 12"
},
{
"id": "6818eb3f98c0bca5af7c8d03",
"title": "Step 13"
},
{
"id": "6818ed1adc249bb91d373729",
"title": "Step 14"
},
{
"id": "6818fcb250f34e62cab32c39",
"title": "Step 15"
},
{
"id": "6819bc0637b80256a33adccc",
"title": "Step 16"
},
{
"id": "6819be9055828d65c813eec5",
"title": "Step 17"
},
{
"id": "6819c5c9faf2548e556163d5",
"title": "Step 18"
},
{
"id": "6819c7beb4a8289c2b4f24d5",
"title": "Step 19"
},
{
"id": "6819ca464db219ab86f37dc4",
"title": "Step 20"
},
{
"id": "6819cf38880d8cc9b9f6af7a",
"title": "Step 21"
},
{
"id": "681a1d60e037127e7c8e0230",
"title": "Step 22"
},
{
"id": "681a2a6cf95e82ca30866364",
"title": "Step 23"
},
{
"id": "681a2e35c406ade2f0a5f9b2",
"title": "Step 24"
},
{
"id": "681a44f4c3235f7d8f428545",
"title": "Step 25"
}
],
"blockLayout": "challenge-grid",
"blockType": "workshop",
"helpCategory": "Python"
}
@@ -0,0 +1,40 @@
---
id: 680a3d4c065557260de2ff33
title: Step 1
challengeType: 20
dashedName: step-1
---
# --description--
As you may recall from the lectures, in Python, you declare a variable by writing the variable name on the left side of the assignment operator (`=`) and the value to assign on the right side:
```py
variable_name = value
```
Create a variable called `shift` and assign the value `5` to your new variable.
# --hints--
You should declare a variable called `shift`. Pay attention to place the variable name at the beginning of the line.
```js
({ test: () => assert(runPython(`_Node(_code).has_variable("shift")`)) })
```
You should assign the value `5` to your `shift` variable.
```js
({ test: () => assert(runPython(`_Node(_code).find_variable("shift").is_equivalent("shift = 5")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,44 @@
---
id: 680a41ef5bfdc059cfd9d4b5
title: Step 2
challengeType: 20
dashedName: step-2
---
# --description--
In previous lectures, you learned about different data types you can store in a variable. You just assigned an integer value. Now you need to assign a string, which is a sequences of characters enclosed by either single or double quotes:
```py
string_1 = 'I am a string'
string_2 = "I am also a string"
```
Declare another variable called `alphabet` and assign the string `abcdefghijklmnopqrstuvwxyz` to this variable.
# --hints--
You should declare a variable called `alphabet`. Pay attention to place the variable name at the beginning of the line.
```js
({ test: () => assert(runPython(`_Node(_code).has_variable("alphabet")`)) })
```
You should assign the string `abcdefghijklmnopqrstuvwxyz` to your `alphabet` variable. Remember to use either single or double quotes to enclose the string.
```js
({ test: () => assert(runPython(`
_Node(_code).find_variable("alphabet").is_equivalent("alphabet = 'abcdefghijklmnopqrstuvwxyz'")
`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
--fcc-editable-region--
shift = 5
```
@@ -0,0 +1,52 @@
---
id: 680a431692a5f863f726c438
title: Step 3
challengeType: 20
dashedName: step-3
---
# --description--
In this workshop, you are going to build a *Caesar cipher*. This is one of the simplest techniques to encrypt text, which consists of substituting each letter of the plain text with the letter found at a fixed number of positions down the alphabet. For example, with a shift of 5, `a` would be replaced by `f`, `b` by `g` and so on.
To implement this cipher, you'll need to create a new version of your alphabet that starts at the position indicated by the shift. As you learned in a previous lecture, you can extract part of a string using string slicing:
```py
fcc = 'freeCodeCamp'
print(fcc[8:]) # Camp
```
Create a variable named `shifted_alphabet` and use the slicing syntax to assign it the portion of `alphabet` that starts at the index of `shift`. Then, call the built-in `print()` function to print `shifted_alphabet` on the terminal and look at the result.
# --hints--
You should declare a new variable named `shifted_alphabet`.
```js
({ test: () => assert(runPython(`_Node(_code).has_variable("shifted_alphabet")`)) })
```
You should assign `alphabet[shift:]` to `shifted_alphabet`.
```js
({ test: () => assert(runPython(`_Node(_code).find_variable("shifted_alphabet").is_equivalent("shifted_alphabet = alphabet[shift:]")`)) })
```
You should call the `print()` function passing in `shifted_alphabet` as an argument.
```js
({ test: () => assert(runPython(`_Node(_code).has_call("print(shifted_alphabet)")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shift = 5
--fcc-editable-region--
```
@@ -0,0 +1,48 @@
---
id: 680a549f60f5a3fedfc0edd9
title: Step 4
challengeType: 20
dashedName: step-4
---
# --description--
As you can see from the output, the shifted alphabet starts at the letter `f` because `shift` has the value `5`. But now the first five letters of the alphabet `a`, `b`, `c`, `d` and `e` are missing from the shifted alphabet, so you'll need to add them at the end of the shifted alphabet.
The `+` operator is used to combine two or more strings together in a process called concatenation like this:
```py
greeting = 'Hello' + ' ' + 'World'
print(greeting) # Hello World
```
Use the slicing syntax to extract the missing first portion of `alphabet` and concatenate it to `alphabet[shift:]`.
As a reminder, `sentence[start:stop]` returns the characters of `sentence` from position `start` (included) to `stop` (excluded).
# --hints--
You should concatenate the missing first portion of `alphabet` to `alphabet[shift:]` using the slicing syntax. Use `shift` to specify where to stop the slicing.
```js
({ test: () => runPython(`
_shifted_alpha = _Node(_code).find_variable("shifted_alphabet")
_first = _shifted_alpha.is_equivalent("shifted_alphabet = alphabet[shift:] + alphabet[:shift]")
_second =_shifted_alpha.is_equivalent("shifted_alphabet = alphabet[shift:] + alphabet[0:shift]")
assert _first or _second
`) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shift = 5
shifted_alphabet = alphabet[shift:]
print(shifted_alphabet)
--fcc-editable-region--
```
@@ -0,0 +1,31 @@
---
id: 680a5e7f3d47715196b12ca0
title: Step 5
challengeType: 20
dashedName: step-5
---
# --description--
Now that you have your entire shifted alphabet, remove the `print(shifted_alphabet)` call.
# --hints--
You should remove `print(shifted_alphabet)` from your code.
```js
({ test: () => assert(runPython(`not _Node(_code).has_call("print(shifted_alphabet)")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shift = 5
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
print(shifted_alphabet)
--fcc-editable-region--
```
@@ -0,0 +1,47 @@
---
id: 680a7e200400089967b8b1f4
title: Step 6
challengeType: 20
dashedName: step-6
---
# --description--
The `str.maketrans()` method takes two strings of equal length and returns a translation table that maps each character of the first string with the corresponding character of the second string. Each character in the translation table is stored as a Unicode ordinal, a number that uniquely identifies the character.
```py
lower_chars = 'abc'
upper_chars = 'ABC'
table = str.maketrans(lower_chars, upper_chars)
print(table) # {97: 65, 98: 66, 99: 67}
```
You'll learn more about the type of structure returned by `str.maketrans()` later on in the curriculum. For now, create a translation table that maps the characters in `alphabet` to the characters in `shifted_alphabet` and assign it to a variable named `translation_table`.
# --hints--
You should declare a variable named `translation_table`.
```js
({ test: () => assert(runPython(`_Node(_code).has_variable("translation_table")`)) })
```
You should call `str.maketrans()` passing in `alphabet` and `shifted_alphabet` as the arguments, and assign the result to `translation_table`.
```js
({ test: () => assert(runPython(`_Node(_code).find_variable("translation_table").is_equivalent("translation_table = str.maketrans(alphabet, shifted_alphabet)")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shift = 5
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
--fcc-editable-region--
```
@@ -0,0 +1,38 @@
---
id: 680a82806c4c6bc3567f2708
title: Step 7
challengeType: 20
dashedName: step-7
---
# --description--
Declare a new variable named `text` and assign it the string `hello world`. This will be the message to encrypt.
# --hints--
You should declare a variable named `text`.
```js
({ test: () => assert(runPython(`_Node(_code).has_variable("text")`)) })
```
You should assign the string `hello world` to `text`. Remember to enclose the string in either single or double quotes and pay attention to the letter case.
```js
({ test: () => assert(runPython(`_Node(_code).find_variable("text").is_equivalent("text = 'hello world'")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shift = 5
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet, shifted_alphabet)
--fcc-editable-region--
```
@@ -0,0 +1,49 @@
---
id: 680a836235461fce1ff53b3f
title: Step 8
challengeType: 20
dashedName: step-8
---
# --description--
The `translate()` method takes as argument the translation table generated by `str.maketrans()`. It is called on a string and returns a copy of the original string where the characters have been replaced based on the translation table:
```py
t = str.maketrans('lk', 'br')
sentence = 'The tent gave in to the leaks.'
print(sentence.translate(t))
# Output: The tent gave in to the bears.
```
Call the `translate()` method on `text` passing in the `translation_table` as the argument and assign the result to a variable named `encrypted_text`.
# --hints--
You should have a variable named `encrypted_text`.
```js
({ test: () => assert(runPython(`_Node(_code).has_variable("encrypted_text")`)) })
```
You should assign `text.translate(translation_table)` to `encrypted_text`.
```js
({ test: () => assert(runPython(`_Node(_code).find_variable("encrypted_text").is_equivalent("encrypted_text = text.translate(translation_table)")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shift = 5
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet, shifted_alphabet)
text = 'hello world'
--fcc-editable-region--
```
@@ -0,0 +1,34 @@
---
id: 6810869355fa2468f9ebaf9c
title: Step 9
challengeType: 20
dashedName: step-9
---
# --description--
Now print `encrypted_text` to see the output in the terminal.
# --hints--
You should print `encrypted_text`.
```js
({ test: () => assert(runPython(`_Node(_code).has_call("print(encrypted_text)")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shift = 5
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet, shifted_alphabet)
text = 'hello world'
encrypted_text = text.translate(translation_table)
--fcc-editable-region--
```
@@ -0,0 +1,49 @@
---
id: 6810891bd6de8a87a833df42
title: Step 10
challengeType: 20
dashedName: step-10
---
# --description--
As you can see from the output, the message has been encrypted. The next step will be making your code reusable in case you wanted to encrypt different messages.
For that, you'll need to create a function. As a reminder, here's how to create a function named `spam` that prints `Spam!` on the terminal:
```py
def spam():
print('Spam!')
```
Create a function named `caesar`. Put all your existing code within the function body. Pay attention to keep the same indentation level for all of the lines within the function body.
# --hints--
You should have a function named `caesar`.
```js
({ test: () => assert(runPython(`_Node(_code).has_function("caesar")`)) })
```
You should move all the code you wrote so far within the `caesar` function body. Make sure you keep the same indentation level for all of the lines within the function body...
```js
({ test: () => assert(runPython(`_Node(_code).find_function("caesar").find_body().is_equivalent("alphabet = 'abcdefghijklmnopqrstuvwxyz'\\nshift = 5\\nshifted_alphabet = alphabet[shift:] + alphabet[:shift]\\ntranslation_table = str.maketrans(alphabet, shifted_alphabet)\\ntext = 'hello world'\\nencrypted_text = text.translate(translation_table)\\nprint(encrypted_text)")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shift = 5
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet, shifted_alphabet)
text = 'hello world'
encrypted_text = text.translate(translation_table)
print(encrypted_text)
--fcc-editable-region--
```
@@ -0,0 +1,54 @@
---
id: 6818b80519e6c6c47a8bb6e8
title: Step 11
challengeType: 20
dashedName: step-11
---
# --description--
As you learned in a previous lecture, functions can have parameters, which are variables that can be referenced within the function. Here's a `sum` function with two parameters, `num1` and `num2`.
```py
def sum(num1, num2):
print(num1 + num2)
```
The message to encrypt and the shift are still hardcoded in your function. Give your function two parameters named `text` and `shift`, and delete the declarations of both the `text` and `shift` variables from the `caesar` function body.
# --hints--
Your `caesar` function should have two parameters, `text` and `shift`.
```js
({ test: () => assert(runPython(`_Node(_code).find_function("caesar").has_args("text, shift")`)) })
```
You should not have a `text` variable declared within the `caesar` function body.
```js
({ test: () => assert(runPython(`not _Node(_code).find_function("caesar").find_body().has_variable("text")`)) })
```
You should not have a `shift` variable declared within the `caesar` function body.
```js
({ test: () => assert(runPython(`not _Node(_code).find_function("caesar").find_body().has_variable("shift")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
def caesar():
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shift = 5
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet, shifted_alphabet)
text = 'hello world'
encrypted_text = text.translate(translation_table)
print(encrypted_text)
--fcc-editable-region--
```
@@ -0,0 +1,41 @@
---
id: 6818e9c7c8dc5694e8f64fc3
title: Step 12
challengeType: 20
dashedName: step-12
---
# --description--
Now, test the `caesar` function by calling it with the string `freeCodeCamp` and the number `3` as the arguments. Assign the function call to a variable named `encrypted_text`.
# --hints--
You should have a variable named `encrypted_text` in the global scope.
```js
({ test: () => assert(runPython(`_Node(_code).has_variable("encrypted_text")`)) })
```
You should assign `caesar('freeCodeCamp', 3)` to the `encrypted_text` variable.
```js
({ test: () => assert(runPython(`_Node(_code).find_variable("encrypted_text").is_equivalent("encrypted_text = caesar('freeCodeCamp', 3)")`)) })
```
# --seed--
## --seed-contents--
```py
def caesar(text, shift):
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet, shifted_alphabet)
encrypted_text = text.translate(translation_table)
print(encrypted_text)
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,36 @@
---
id: 6818eb3f98c0bca5af7c8d03
title: Step 13
challengeType: 20
dashedName: step-13
---
# --description--
Now your code is reusable. However, the `caesar` function prints a message on the terminal and gives back `None` by default. To prove it, print `encrypted_text` at the end of your code.
# --hints--
You should print `encrypted_text` outside of the function body.
```js
({ test: () => assert(runPython(`_Node(_code).has_call("print(encrypted_text)")`)) })
```
# --seed--
## --seed-contents--
```py
def caesar(text, shift):
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet, shifted_alphabet)
encrypted_text = text.translate(translation_table)
print(encrypted_text)
--fcc-editable-region--
encrypted_text = caesar('freeCodeCamp', 3)
--fcc-editable-region--
```
@@ -0,0 +1,56 @@
---
id: 6818ed1adc249bb91d373729
title: Step 14
challengeType: 20
dashedName: step-14
---
# --description--
In previous lectures, you learned that the `return` statement is used to return a value from a function, so that you can use it elsewhere in your code:
```py
def spam():
return 'Spam!'
```
Remove the `print(encrypted_text)` call from your function. Then, delete the declaration of the `encrypted_text` variable and return directly `text.translate(translation_table)` from your function.
# --hints--
You should not have `print(encrypted_text)` within the `caesar` function.
```js
({ test: () => assert(runPython(`not _Node(_code).find_function("caesar").has_call("print(encrypted_text)")`)) })
```
You should not have an `encrypted_text` variable within the `caesar` function.
```js
({ test: () => assert(runPython(`not _Node(_code).find_function("caesar").has_variable("encrypted_text")`)) })
```
Your `caesar` function should return `text.translate(translation_table)`.
```js
({ test: () => assert(runPython(`_Node(_code).find_function("caesar").has_return("text.translate(translation_table)")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
def caesar(text, shift):
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet, shifted_alphabet)
encrypted_text = text.translate(translation_table)
print(encrypted_text)
--fcc-editable-region--
encrypted_text = caesar('freeCodeCamp', 3)
print(encrypted_text)
```
@@ -0,0 +1,45 @@
---
id: 6818fcb250f34e62cab32c39
title: Step 15
challengeType: 20
dashedName: step-15
---
# --description--
You might have noticed that, although the message is encrypted, the uppercase characters have been left untouched. This happens because the translation table does not include uppercase letters.
To fix it, you'll need to modify the strings passed to your `str.maketrans()` call using the `upper()` method:
```py
greet = "Hello"
print(greet.upper()) # HELLO
```
Update your `str.maketrans()` call by concatenating to each argument the uppercase version of the argument itself.
# --hints--
You should modify the arguments of your `str.maketrans()` call by concatenating to each argument the uppercase version of the argument itself.
```js
({ test: () => assert(runPython(`caesar('This Is A TEST', 5) == 'Ymnx Nx F YJXY'`)) })
```
# --seed--
## --seed-contents--
```py
def caesar(text, shift):
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
--fcc-editable-region--
translation_table = str.maketrans(alphabet, shifted_alphabet)
--fcc-editable-region--
return text.translate(translation_table)
encrypted_text = caesar('freeCodeCamp', 3)
print(encrypted_text)
```
@@ -0,0 +1,56 @@
---
id: 6819bc0637b80256a33adccc
title: Step 16
challengeType: 20
dashedName: step-16
---
# --description--
Now that you've implemented the basic functionalities of the cipher, it's time to add some validation. For that, you'll need an `if` statement. Here's a reminder of the syntax for an `if` statement:
```py
if condition:
# code to run when condition is true
```
At the beginning of your function body, create an `if` statement. For now, use `True` as the condition, and within the `if` statement body return the string `Shift must be an integer value.`.
# --hints--
You should have an `if` statement inside your `caesar` function.
```js
({ test: () => assert(runPython(`_Node(_code).find_function("caesar").find_ifs()[0]`)) })
```
Your `if` statement should use `True` as its condition.
```js
({ test: () => assert(runPython(`_Node(_code).find_function("caesar").find_ifs()[0].find_conditions()[0].is_equivalent("True")`)) })
```
You should return the string `Shift must be an integer value.` from your `if` statement.
```js
({ test: () => assert(runPython(`_Node(_code).find_function("caesar").find_ifs()[0].find_bodies()[0].has_return("'Shift must be an integer value.'")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
def caesar(text, shift):
--fcc-editable-region--
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet + alphabet.upper(), shifted_alphabet + shifted_alphabet.upper())
return text.translate(translation_table)
encrypted_text = caesar('freeCodeCamp', 3)
print(encrypted_text)
```
@@ -0,0 +1,45 @@
---
id: 6819be9055828d65c813eec5
title: Step 17
challengeType: 20
dashedName: step-17
---
# --description--
The `isinstance()` function returns `True` if its first argument is an instance of the second argument, and `False` otherwise:
```py
print(isinstance('Hello World', str)) # True
print(isinstance(42, int)) # True
```
Replace the current condition of your `if` statement with an `isinstance()` call. Pass in `shift` as the first argument, and `int` as the second argument.
# --hints--
Your `if` statement should use `isinstance(shift, int)` as its condition.
```js
({ test: () => assert(runPython(`_Node(_code).find_function("caesar").find_ifs()[0].find_conditions()[0].is_equivalent("isinstance(shift, int)")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
def caesar(text, shift):
if True:
return 'Shift must be an integer value.'
--fcc-editable-region--
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet + alphabet.upper(), shifted_alphabet + shifted_alphabet.upper())
return text.translate(translation_table)
encrypted_text = caesar('freeCodeCamp', 3)
print(encrypted_text)
```
@@ -0,0 +1,45 @@
---
id: 6819c5c9faf2548e556163d5
title: Step 18
challengeType: 20
dashedName: step-18
---
# --description--
As you recall from the lectures, the `not` operator is a unary logical operator that negates an expression:
```py
print(not True) # False
print(not False) # True
```
Use the `not` operator to fix the condition of the `if` statement so that your function returns `Shift must be an integer value.` when `shift` is *not* an integer.
# --hints--
You should use the `not` operator to negate `isinstance(shift, int)` in your `if` statement.
```js
({ test: () => assert(runPython(`_Node(_code).find_function("caesar").find_ifs()[0].find_conditions()[0].is_equivalent("not isinstance(shift, int)")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
def caesar(text, shift):
if isinstance(shift, int):
return 'Shift must be an integer value.'
--fcc-editable-region--
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet + alphabet.upper(), shifted_alphabet + shifted_alphabet.upper())
return text.translate(translation_table)
encrypted_text = caesar('freeCodeCamp', 3)
print(encrypted_text)
```
@@ -0,0 +1,49 @@
---
id: 6819c7beb4a8289c2b4f24d5
title: Step 19
challengeType: 20
dashedName: step-19
---
# --description--
A negative or null shift should not be accepted by your function. Therefore, after your first `if` statement, create another `if` statement that checks if `shift` is less than `1` and returns the string `Shift must be a positive integer.`.
# --hints--
You should have a second `if` statement that checks if `shift` is less than `1`.
```js
({ test: () => runPython(`
_cond = _Node(_code).find_function("caesar").find_ifs()[1].find_conditions()[0]
_options = ["shift < 1", "1 > shift", "shift <= 0", "0 => shift"]
assert any(_cond.is_equivalent(option) for option in _options)
`) })
```
Your second `if` statement should return `Shift must be a positive integer.`.
```js
({ test: () => assert(runPython(`_Node(_code).find_function("caesar").find_ifs()[1].find_bodies()[0].has_return("'Shift must be a positive integer.'")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
def caesar(text, shift):
if not isinstance(shift, int):
return 'Shift must be an integer value.'
--fcc-editable-region--
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet + alphabet.upper(), shifted_alphabet + shifted_alphabet.upper())
return text.translate(translation_table)
encrypted_text = caesar('freeCodeCamp', 3)
print(encrypted_text)
```
@@ -0,0 +1,54 @@
---
id: 6819ca464db219ab86f37dc4
title: Step 20
challengeType: 20
dashedName: step-20
---
# --description--
As you've already verified, the shift passed to encrypt the text should be positive. It cannot exceed `25` (the last index of `alphabet`) though.
Add a second condition to the `if` statement that verifies that `shift` is greater than `25`. Remember that the logical OR operation in Python is implemented through the `or` operator.
Also, update the returned message to `Shift must be an integer between 1 and 25.`.
# --hints--
You should use the `or` operator to add a second condition after `shift < 1` checking if `shift` is greater than `25`. You should not modify the existing condition.
```js
({ test: () => runPython(`
_cond = _Node(_code).find_function("caesar").find_ifs()[1].find_conditions()[0]
_options = ["shift > 25", "25 < shift", "shift >= 26", "26 =< shift"]
assert any(_cond.is_equivalent(f"shift < 1 or {option}") for option in _options)
`) })
```
Your second `if` statement should return `Shift must be an integer between 1 and 25.`.
```js
({ test: () => assert(runPython(`_Node(_code).find_function("caesar").find_ifs()[1].find_bodies()[0].has_return("'Shift must be an integer between 1 and 25.'")`)) })
```
# --seed--
## --seed-contents--
```py
def caesar(text, shift):
if not isinstance(shift, int):
return 'Shift must be an integer value.'
--fcc-editable-region--
if shift < 1:
return 'Shift must be a positive integer.'
--fcc-editable-region--
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet + alphabet.upper(), shifted_alphabet + shifted_alphabet.upper())
return text.translate(translation_table)
encrypted_text = caesar('freeCodeCamp', 3)
print(encrypted_text)
```
@@ -0,0 +1,51 @@
---
id: 6819cf38880d8cc9b9f6af7a
title: Step 21
challengeType: 20
dashedName: step-21
---
# --description--
Python allows you to specify a default value for the parameters in a function, creating a function that can be called with fewer or no arguments. Here's how to create a function with a `name` parameter that has a default value:
```py
def greet(name='Polly'):
return 'Hello ' + name
print(greet()) # Hello Polly
```
Add a third parameter named `encrypt` to your function and give it a default value of `True`.
# --hints--
You should add a third parameter named `encrypt` with a default value of `True` to your function. Do not modify the existing parameters.
```js
({ test: () => assert(runPython(`_Node(_code).find_function("caesar").has_args("text, shift, encrypt=True")`)) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
def caesar(text, shift):
--fcc-editable-region--
if not isinstance(shift, int):
return 'Shift must be an integer value.'
if shift < 1 or shift > 25:
return 'Shift must be an integer between 1 and 25.'
alphabet = 'abcdefghijklmnopqrstuvwxyz'
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet + alphabet.upper(), shifted_alphabet + shifted_alphabet.upper())
return text.translate(translation_table)
encrypted_text = caesar('freeCodeCamp', 3)
print(encrypted_text)
```
@@ -0,0 +1,49 @@
---
id: 681a1d60e037127e7c8e0230
title: Step 22
challengeType: 20
dashedName: step-22
---
# --description--
You are going to use the parameter added in the previous step to determine if the function should encrypt the text passed to it (default behavior, `encrypt=True`), or if it should decrypt an encrypted message.
Create an `if` statement that checks if `encrypt` is not truthy. Within the new `if` statement, set `shift` to `- shift`. This is necessary to enable the shift to take place in the opposite direction with respect to the encryption process.
# --hints--
You should set `shift` to `- shift` when `encrypt` is not truthy.
```js
({ test: () => assert(runPython(`caesar('Ymnx Nx F Yjxy', 5, False) == 'This Is A Test'`)) })
```
# --seed--
## --seed-contents--
```py
def caesar(text, shift, encrypt=True):
if not isinstance(shift, int):
return 'Shift must be an integer value.'
if shift < 1 or shift > 25:
return 'Shift must be an integer between 1 and 25.'
alphabet = 'abcdefghijklmnopqrstuvwxyz'
--fcc-editable-region--
--fcc-editable-region--
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet + alphabet.upper(), shifted_alphabet + shifted_alphabet.upper())
encrypted_text = text.translate(translation_table)
return encrypted_text
encrypted_text = caesar('freeCodeCamp', 3)
print(encrypted_text)
```
@@ -0,0 +1,74 @@
---
id: 681a2a6cf95e82ca30866364
title: Step 23
challengeType: 20
dashedName: step-23
---
# --description--
Declare two functions named `encrypt` and `decrypt`, both with `text` and `shift` parameters.
You'll use `encrypt` for the encryption process, and `decrypt` for the decryption, labeling the two actions with a descriptive name.
Return a `caesar` call passing in `text` and `shift` from both your new functions, but make sure to pass in also `False` as the third argument for the `decrypt` function.
# --hints--
You should have a function named `encrypt` with two parameters, `text` and `shift`.
```js
({ test: () => assert(runPython(`_Node(_code).find_function("encrypt").has_args("text, shift")`)) })
```
Your `encrypt` function should return `caesar(text, shift)`.
```js
({ test: () => assert(runPython(`_Node(_code).find_function("encrypt").has_return("caesar(text, shift)")`)) })
```
You should have a function named `decrypt` with two parameters, `text` and `shift`.
```js
({ test: () => assert(runPython(`_Node(_code).find_function("decrypt").has_args("text, shift")`)) })
```
Your `decrypt` function should return `caesar(text, shift, False)`.
```js
({ test: () => runPython(`
_n = _Node(_code).find_function("decrypt")
assert _n.has_return("caesar(text, shift, encrypt=False)") or _n.has_return("caesar(text, shift, False)")
`) })
```
# --seed--
## --seed-contents--
```py
def caesar(text, shift, encrypt=True):
if not isinstance(shift, int):
return 'Shift must be an integer value.'
if shift < 1 or shift > 25:
return 'Shift must be an integer between 1 and 25.'
alphabet = 'abcdefghijklmnopqrstuvwxyz'
if not encrypt:
shift = - shift
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet + alphabet.upper(), shifted_alphabet + shifted_alphabet.upper())
encrypted_text = text.translate(translation_table)
return encrypted_text
--fcc-editable-region--
--fcc-editable-region--
encrypted_text = caesar('freeCodeCamp', 3)
print(encrypted_text)
```
@@ -0,0 +1,52 @@
---
id: 681a2e35c406ade2f0a5f9b2
title: Step 24
challengeType: 20
dashedName: step-24
---
# --description--
It's time to test the `encrypt` function. Using the same arguments, replace your `caesar` call with a call to `encrypt`. You'll see the same output on the terminal.
# --hints--
Your `encrypted_text` variable should have the value of `encrypt('freeCodeCamp', 3)`.
```js
({ test: () => assert(runPython(`_Node(_code).find_variable("encrypted_text").is_equivalent("encrypted_text = encrypt('freeCodeCamp', 3)")`)) })
```
# --seed--
## --seed-contents--
```py
def caesar(text, shift, encrypt=True):
if not isinstance(shift, int):
return 'Shift must be an integer value.'
if shift < 1 or shift > 25:
return 'Shift must be an integer between 1 and 25.'
alphabet = 'abcdefghijklmnopqrstuvwxyz'
if not encrypt:
shift = - shift
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet + alphabet.upper(), shifted_alphabet + shifted_alphabet.upper())
encrypted_text = text.translate(translation_table)
return encrypted_text
def encrypt(text, shift):
return caesar(text, shift)
def decrypt(text, shift):
return caesar(text, shift, encrypt=False)
--fcc-editable-region--
encrypted_text = caesar('freeCodeCamp', 3)
print(encrypted_text)
--fcc-editable-region--
```
@@ -0,0 +1,106 @@
---
id: 681a44f4c3235f7d8f428545
title: Step 25
challengeType: 20
dashedName: step-25
---
# --description--
Now you're going to test the `decrypt` function. Replace the value assigned to `encrypted_text` with the following string, which represents a message to decrypt: `Pbhentr vf sbhaq va hayvxryl cynprf.`.
Then, declare a variable named `decrypted_text` and assign it a call to `decrypt` with `encrypted_text` as it first argument and a shift of `13` as the second argument.
Finally, print the `decrypted_text` on the terminal. With that, the Caesar cipher is complete.
# --hints--
You should assign `Pbhentr vf sbhaq va hayvxryl cynprf.` to `encrypted_text`.
```js
({ test: () => assert(runPython(`_Node(_code).find_variable("encrypted_text").is_equivalent("encrypted_text = 'Pbhentr vf sbhaq va hayvxryl cynprf.'")`)) })
```
You should have a variable named `decrypted_text`.
```js
({ test: () => assert(runPython(`_Node(_code).has_variable("decrypted_text")`)) })
```
You should assign `decrypt(encrypted_text, 13)` to `decrypted_text`.
```js
({ test: () => assert(runPython(`_Node(_code).find_variable("decrypted_text").is_equivalent("decrypted_text = decrypt(encrypted_text, 13)")`)) })
```
You should print `decrypted_text`.
```js
({ test: () => assert(runPython(`_Node(_code).has_call("print(decrypted_text)")`)) })
```
# --seed--
## --seed-contents--
```py
def caesar(text, shift, encrypt=True):
if not isinstance(shift, int):
return 'Shift must be an integer value.'
if shift < 1 or shift > 25:
return 'Shift must be an integer between 1 and 25.'
alphabet = 'abcdefghijklmnopqrstuvwxyz'
if not encrypt:
shift = - shift
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet + alphabet.upper(), shifted_alphabet + shifted_alphabet.upper())
encrypted_text = text.translate(translation_table)
return encrypted_text
def encrypt(text, shift):
return caesar(text, shift)
def decrypt(text, shift):
return caesar(text, shift, encrypt=False)
--fcc-editable-region--
encrypted_text = encrypt('freeCodeCamp', 3)
print(encrypted_text)
--fcc-editable-region--
```
# --solutions--
```py
def caesar(text, shift, encrypt=True):
if not isinstance(shift, int):
return 'Shift must be an integer value.'
if shift < 1 or shift > 25:
return 'Shift must be an integer between 1 and 25.'
alphabet = 'abcdefghijklmnopqrstuvwxyz'
if not encrypt:
shift = - shift
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
translation_table = str.maketrans(alphabet + alphabet.upper(), shifted_alphabet + shifted_alphabet.upper())
encrypted_text = text.translate(translation_table)
return encrypted_text
def encrypt(text, shift):
return caesar(text, shift)
def decrypt(text, shift):
return caesar(text, shift, encrypt=False)
encrypted_text = 'Pbhentr vf sbhaq va hayvxryl cynprf.'
decrypted_text = decrypt(encrypted_text, 13)
print(decrypted_text)
```