Invisible ASCII chars
A common bug in Caesar and Vigenere is printing out characters that are not visible on screen. The most common cause here is actually through an off by one error, where the student loops to < n+1 or <= n and then mistakenly encrypts the trailing \0 to something exotic. However, check50s feedback isn't very helpful here:

The above is the result of introducing this off-by-one in vault50's solution, full results at: https://submit.cs50.io/check50/db65ac87b1538ac1afc860e7df823b5158885d90
Can we improve?
I had the very same issue when doing this pset myself! And I'd be happy to help here.
How about showing the ASCII chars that aren't visible usually in a different color in the actual output box?
Like it would say: "Actual Output: ciphertext: b\0", with the \0 in red, because it shouldn't be there, or yellow, because it's a char that's on our "seems-wrong-here" blacklist for all chars, that shouldn't ever be used in an output? Because chars like \n should be used sometimes 🤔
I'm not sure that it's the same case, but I also have similar issues with Substitution problem and check50. https://submit.cs50.io/check50/78eb10b2c314dbe8fb51c41d475dd23b15a25e83
There are no errors or additional characters
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
if (argc == 2)
{
char key[2][26];
char alphabet[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int n = strlen(argv[1]);
int alpha = 0;
int repeat = 0;
// Assign key and alphabet to one array
for (int i = 0; i < n; i++)
{
key[0][i] = alphabet[i];
key[1][i] = toupper(argv[1][i]);
// Counters for alphabetic characters and repeated characters
if (isalpha(argv[1][i]) > 0)
{
alpha++;
}
for (int j = 0; j < n; j++)
{
if (argv[1][i] == argv[1][j])
{
repeat++;
}
}
}
// Get plain text
if (n == 26 && alpha == 26 && repeat == 26)
{
char plaintext[100];
char ciphertext[100];
printf("plaintext: ");
scanf("%[^\n]s", plaintext);
// Substitute plaintext with ciphertext using key
int m = strlen(plaintext);
for (int i = 0, k = 0; i < m; i++)
{
for (int j = 0; j < 26; j++)
{
if (plaintext[i] == key[0][j])
{
ciphertext[k++] = key[1][j];
}
// Check for lowercase, spaces, digits and special characters
else if (islower(plaintext[i]) && plaintext[i] - 32 == key[0][j])
{
ciphertext[k++] = key[1][j] + 32;
}
else if (isspace(plaintext[i]))
{
ciphertext[k++] = ' ';
i++;
}
else if (isdigit(plaintext[i]))
{
ciphertext[k++] = plaintext[i];
i++;
}
else if (ispunct(plaintext[i]))
{
ciphertext[k++] = plaintext[i];
i++;
}
}
}
// Print ciphertext
printf("ciphertext: %s\n", ciphertext);
}
else if (n != 26)
{
printf("Key must contain 26 characters.\n");
return 1;
}
else if (alpha != 26)
{
printf("Key must only contain alphabetic characters.\n");
return 1;
}
else if (repeat != 26)
{
printf("Key must not contain repeated characters.\n");
return 1;
}
}
else if (argc == 1)
{
printf("Usage: ./substitution KEY\n");
return 1;
}
return 0;
}
Thanks so much for sharing this issue, I've been trying to finish my pset2 for one day because of this.