SPY ACADEMY: CODE BREAKER TRAINING
SPY ACADEMY: CODE BREAKER TRAINING
AP Computer Science A - Arrays and Strings Lab
INTRODUCTION: WHAT IS A CAESAR CIPHER?
A Caesar cipher is one of the oldest and simplest encryption techniques. It works by shifting each letter in a message by a fixed number of positions in the alphabet.
Example with shift of 3:
A → D B → E C → F ... X → A Y → B Z → C
So the word “HELLO” becomes “KHOOR”:
H → K E → H L → O L → O O → R
To decode, you shift backwards by the same amount:
K → H H → E O → L O → L R → O
This cipher is named after Julius Caesar, who used it to protect military messages!
JAVA CONCEPT: CHARACTERS AND NUMBERS
In Java, every character has a corresponding integer value. This means you can do math with characters!
Key facts:
'A'corresponds to the number 65'B'corresponds to 66,'C'to 67, and so on…'Z'corresponds to 90
You can convert between characters and numbers:
char letter = 'A';
int num = letter; // num is now 65
int value = 67;
char ch = (char) value; // ch is now 'C'
Useful trick - if you want A=0, B=1, C=2, etc:
char letter = 'C';
int position = letter - 'A'; // position is 2 (because C is the 3rd letter)
int position = 2;
char newLetter = (char)('A' + position); // newLetter is 'C'
This is perfect for Caesar cipher! You can convert letters to numbers (0-25), shift them, and convert back.
THE LAB: TWO-PLAYER SPY GAME
THE GAME:
You’ll implement methods that power a two-player spy game:
- Player 1 (The Spy) enters a secret message and chooses a shift amount
- The computer encodes the message using YOUR
caesarShiftmethod - Player 2 (The Code Breaker) sees the encoded message
- Player 2 has a limited number of guesses to crack the shift amount
- The computer decodes their guess using YOUR
caesarDecryptmethod - Players switch roles and play again!
Example gameplay:
PLAYER 1: Types "AGENT" with shift 5
COMPUTER: Encodes to "FLMJS"
PLAYER 2: Sees "FLMJS" and must guess the shift
PLAYER 2: Guesses shift of 3 → decodes to "CEBKQ" ❌
PLAYER 2: Guesses shift of 5 → decodes to "AGENT" ✅ SUCCESS!
THE LAB HAS TWO PHASES:
Phase 1: Single Word Operations Practice with simple words (no spaces or punctuation)
reverseString()- warmupcaesarShift()- encode a wordcaesarDecrypt()- decode a wordcountVowels()- helper method
Phase 2: Full Phrase Operations Level up to messages with spaces and punctuation!
caesarShiftPhrase()- encode “MEET AT NOON!” → “PHHW DW QRRQ!”caesarDecryptPhrase()- decode the phrase backisValidDecode()- check if decoded message contains keywords (optional)
YOUR TASK: IMPLEMENT THESE METHODS
⚠️ IMPORTANT: TEST AS YOU GO! Don’t wait until you’ve written all the methods to test. After you complete each method, test it with the examples below. This will help you catch bugs early and make debugging much easier!
PHASE 1 METHODS:
1. String reverseString(String word)
Purpose: Return the word spelled backwards
Example:
reverseString("AGENT") // returns "TNEGА"
Test cases to try:
reverseString("HELLO") // should return "OLLEH"
reverseString("CODE") // should return "EDOC"
reverseString("A") // should return "A"
reverseString("RACECAR") // should return "RACECAR" (palindrome!)
Hint: Loop through the string from end to beginning
✅ Test this method before moving on! You can add a few print statements in the main method to verify it works.
2. String caesarShift(String word, int shift)
Purpose: Encode a word by shifting each letter. You shift a letter by converting to a number (A=0), then adding 26. Then you take that number MOD 26 (x % 26). The mod operation is how you can “wrap around”.
Example:
caesarShift("HELLO", 3) // returns "KHOOR"
Test cases to try:
caesarShift("ABC", 1) // should return "BCD"
caesarShift("XYZ", 3) // should return "ABC" (wraparound!)
caesarShift("AGENT", 5) // should return "FLMJS"
caesarShift("HELLO", 0) // should return "HELLO" (no shift)
caesarShift("ZEBRA", 1) // should return "AFCSB" (Z wraps to A)
caesarShift("AAA", 25) // should return "ZZZ"
Hints:
- Convert each letter to a number (A=0…Z=25)
- Add the shift
- Use
% 26to wrap around (this is crucial!) - Convert back to a letter
Algorithm:
- Get character:
word.charAt(i) - Convert to 0-25:
letter - 'A' - Add shift and wrap:
(number + shift) % 26 - Convert back to letter:
(char)('A' + number)
✅ Test this method thoroughly! The wraparound is tricky. Make sure “Z” with shift 1 gives you “A”!
3. String caesarDecrypt(String encoded, int shift)
Purpose: Decode an encoded word by shifting backwards. IN JAVA, when you take negative number mod 26, you will get a negative answer. To fix this, you can add 26 to make the negative number positive.
Example:
caesarDecrypt("KHOOR", 3) // returns "HELLO"
Test cases to try:
caesarDecrypt("BCD", 1) // should return "ABC"
caesarDecrypt("ABC", 3) // should return "XYZ" (wraparound backwards!)
caesarDecrypt("FLMJS", 5) // should return "AGENT"
caesarDecrypt("HELLO", 0) // should return "HELLO"
caesarDecrypt("AAA", 1) // should return "ZZZ" (backward wrap)
Hint: This is the opposite of caesarShift
Important: When subtracting, you might get negative numbers. Use (number - shift + 26) % 26 to handle this!
✅ Test both encoding and decoding together:
String original = "HELLO";
String encoded = caesarShift(original, 7);
String decoded = caesarDecrypt(encoded, 7);
// decoded should equal original!
4. int countVowels(String text)
Purpose: Count how many vowels (A, E, I, O, U) are in the text
Example:
countVowels("HELLO") // returns 2 (E and O)
Test cases to try:
countVowels("AEIOU") // should return 5
countVowels("BCDFG") // should return 0
countVowels("PROGRAMMING") // should return 3
countVowels("AGENT") // should return 2
countVowels("WHY") // should return 0 (Y is not a vowel)
Hint: Loop through and check if each character equals ‘A’, ‘E’, ‘I’, ‘O’, or ‘U’
✅ Quick test! This one is simpler, but still verify it counts correctly.
🎮 TEST PHASE 1 BY PLAYING THE GAME!
Once you’ve implemented all Phase 1 methods, run your program and play Phase 1 with a partner:
- If the encoded message looks like random letters ✅ Good!
- If Player 2 correctly guesses the shift but gets told it’s wrong ❌ Bug in your code!
- If decoding gives weird characters or errors ❌ Check your modulo logic!
PHASE 2 METHODS:
5. String caesarShiftPhrase(String message, int shift)
Purpose: Encode a full phrase, but KEEP spaces and punctuation unchanged
Example:
caesarShiftPhrase("MEET AT NOON!", 3) // returns "PHHW DW QRRQ!"
Test cases to try:
caesarShiftPhrase("HELLO WORLD", 5) // should return "MJQQT BTWQI"
caesarShiftPhrase("THE MISSION IS GO!", 13) // should return "GUR ZVFFVBA VF TB!"
caesarShiftPhrase("A B C", 1) // should return "B C D"
caesarShiftPhrase("TOP SECRET!!", 7) // should return "AVW ZJJYLA!!"
caesarShiftPhrase("CODE 123", 2) // should return "EQFG 123" (numbers unchanged)
caesarShiftPhrase("YES-OR-NO?", 10) // should return "IOC-BE-XY?" (hyphens stay)
Hints:
- Use
Character.isLetter(ch)to check if a character is a letter - Only shift letters; keep spaces, punctuation, and numbers the same
- You can still use your Caesar shift logic from Phase 1, just wrap it in a condition!
✅ Test with different punctuation! Make sure exclamation marks, periods, and numbers stay unchanged.
6. String caesarDecryptPhrase(String encoded, int shift)
Purpose: Decode a phrase (opposite of caesarShiftPhrase)
Example:
caesarDecryptPhrase("PHHW DW QRRQ!", 3) // returns "MEET AT NOON!"
Test cases to try:
caesarDecryptPhrase("MJQQT BTWQI", 5) // should return "HELLO WORLD"
caesarDecryptPhrase("GUR ZVFFVBA VF TB!", 13) // should return "THE MISSION IS GO!"
caesarDecryptPhrase("B C D", 1) // should return "A B C"
caesarDecryptPhrase("AVW ZJJYLA!!", 7) // should return "TOP SECRET!!"
✅ Test encoding and decoding together:
String original = "THE MISSION IS GO!";
String encoded = caesarShiftPhrase(original, 10);
String decoded = caesarDecryptPhrase(encoded, 10);
// decoded should equal original!
7. boolean isValidDecode(String decoded, String[] keywords) [OPTIONAL]
Purpose: Check if the decoded message contains any word from the keywords array
Example:
String[] keywords = {"MISSION", "AGENT", "SECRET"};
isValidDecode("THE MISSION IS GO", keywords) // returns true (contains "MISSION")
isValidDecode("HELLO THERE", keywords) // returns false
Test cases to try:
String[] spyWords = {"AGENT", "MISSION", "SECRET", "CODE"};
isValidDecode("AGENT SMITH", spyWords) // should return true
isValidDecode("TOP SECRET FILES", spyWords) // should return true
isValidDecode("THE CODE IS READY", spyWords) // should return true
isValidDecode("NOTHING HERE", spyWords) // should return false
isValidDecode("HELLO WORLD", spyWords) // should return false
Hint: Loop through the keywords array and use .contains() or .indexOf()
🎮 TEST PHASE 2 BY PLAYING THE GAME!
Once Phase 2 methods work, play the full game with phrases:
- Try messages with lots of punctuation
- Try messages with numbers
- Make sure spaces are preserved in the right places!
TESTING STRATEGY: BUILD INCREMENTALLY
The best approach:
- ✅ Write
reverseString()→ test it → move on - ✅ Write
caesarShift()→ test it thoroughly → move on - ✅ Write
caesarDecrypt()→ test it AND test encode/decode together - ✅ Write
countVowels()→ test it - ✅ Play Phase 1 game → if it works, great! If not, debug.
- ✅ Write
caesarShiftPhrase()→ test it with the examples above - ✅ Write
caesarDecryptPhrase()→ test it thoroughly - ✅ Play Phase 2 game → your reward for completing the lab!
Don’t skip testing! It’s tempting to write all the code and test at the end, but you’ll save time by testing each method as you complete it.
COMMON MISTAKES TO AVOID
❌ Forgetting to use % 26 → Letters will go past Z and become weird characters! ❌ Not adding 26 when subtracting → You’ll get negative numbers and errors ❌ In Phase 2, shifting spaces/punctuation → They should stay exactly the same! ❌ Off-by-one errors → Remember: A=0, not A=1 ❌ Not testing wraparound → Make sure Z+1=A and A-1=Z!
DEBUGGING TIPS
If your encoding looks weird:
- Print out the number values you’re getting
- Check: Does ‘A’ - ‘A’ give you 0?
- Check: Does ‘Z’ - ‘A’ give you 25?
- Are you using
% 26correctly?
If decoding doesn’t work:
- Try encoding “ABC” with shift 1, then decode “BCD” with shift 1
- Should get “ABC” back!
- If not, check your subtraction and modulo logic
If spaces disappear:
- Are you checking
Character.isLetter()in Phase 2? - Are you including non-letters in your result string?
EXTENSIONS FOR FAST FINISHERS
If you complete the lab early, try these challenges:
- Hint System: Modify the game to tell Player 2 if their guess is too high or too low
- Auto-Cracker: Write
findBestShift(String encoded, String[] keywords)that tries all 26 shifts and returns the one that produces a valid message - Alternating Case Cipher: Create a new cipher that alternates between uppercase and lowercase
- Frequency Analysis: Write a method that counts how often each letter appears (useful for breaking Caesar ciphers!)
- Multi-Shift Cipher: Instead of one shift value, use a different shift for each letter position
Good luck, Agent! 🕵️
Remember: Test often, debug early, and have fun cracking codes!