(1) Scrabble
In the game of Scrabble, players create words to score points, and the number of points is the sum of the point values of each letter in the word.

For example, if we wanted to score the word “CODE”, we would note that the ‘C’ is worth 3 points, the ‘O’ is worth 1 point, the ‘D’ is worth 2 points, and the ‘E’ is worth 1 point. Summing these, we get that “CODE” is worth 7 points.
In a file called scrabble.c in a folder called scrabble, implement a program in C that determines the winner of a short Scrabble-like game. Your program should prompt for input twice: once for “Player 1” to input their word and once for “Player 2” to input their word. Then, depending on which player scores the most points, your program should either print “Player 1 wins!”, “Player 2 wins!”, or “Tie!” (in the event the two players score equal points).
문제는 간단하다. Player 1과 Player 2가 scrabble 게임을 해서 각 캐릭터마다 위 표에 명시된 것 만큼의 점수를 받고 누가 더 많은 점수를 받았는지 비교하여 승자를 결정하면 된다.
<pseudocode>
- points를 배열로 만들어 a의 점수를 idx 0에, z의 점수를 idx 25에 위치하게 하기
- scoreSum(string word) // 점수의 합계를 구하는 로직
: 인수로 받은 word의 char 수만큼 반복문을 돌림
각 캐릭터가 points[] 배열에서 갖는 인덱스를 구해서 points[index] 값만큼 sum을 증가시킴
※ 주의할 점
*각 캐릭터는 ctype 라이브러리의 tolower()을 이용하여 모두 소문자로 변환함
*인덱스가 0 ~ 25 사이에 있을 시에만 sum을 증가시켜서 다른 특수문자 등은 점수에 영향이 없도록 함
- determineWinner(string word1, string word2) // scoreSum 함수를 이용하여 승자 결정
: 승자가 player 1일시 -> 1 반환
승자가 player 2일시 -> 2 반환
무승부일시 -> 3반환
- main(void)
: 사용자에게서 input을 받음
determineWinner() 함수를 이용해 승자를 불러와서 승자를 알려주는 문장 출력
<내 코드>
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int determineWinner(string word1, string word2);
int scoreSum(string word);
int points[] = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10}; // 0 - 25
int main(void)
{
string word1 = get_string("Player 1: ");
string word2 = get_string("Player 2: ");
int winner = determineWinner(word1, word2);
if (winner == 1)
{
printf("Player 1 wins!\n");
}
else if (winner == -1)
{
printf("Player 2 wins!\n");
}
else
{
printf("Tie!\n");
}
}
int determineWinner(string word1, string word2)
{
int sum1 = scoreSum(word1);
int sum2 = scoreSum(word2);
return (sum1 > sum2) - (sum1 < sum2);
}
int scoreSum(string word)
{
int sum = 0;
for (int i = 0, n = strlen(word); i < n; i++)
{
int charIdx = (int) tolower(word[i]) - 'a';
if (charIdx >= 0 && charIdx <= 25)
{
sum += points[charIdx];
}
}
return sum;
}
(2) Readability
According to Scholastic, E.B. White’s Charlotte’s Web is between a second- and fourth-grade reading level, and Lois Lowry’s The Giver is between an eighth- and twelfth-grade reading level. What does it mean, though, for a book to be at a particular reading level?
Well, in many cases, a human expert might read a book and make a decision on the grade (i.e., year in school) for which they think the book is most appropriate. But an algorithm could likely figure that out too!
In a file called readability.c in a folder called readability, you’ll implement a program that calculates the approximate grade level needed to comprehend some text. Your program should print as output “Grade X” where “X” is the grade level computed, rounded to the nearest integer. If the grade level is 16 or higher (equivalent to or greater than a senior undergraduate reading level), your program should output “Grade 16+” instead of giving the exact index number. If the grade level is less than 1, your program should output “Before Grade 1”.
Background
So what sorts of traits are characteristic of higher reading levels? Well, longer words probably correlate with higher reading levels. Likewise, longer sentences probably correlate with higher reading levels, too.
A number of “readability tests” have been developed over the years that define formulas for computing the reading level of a text. One such readability test is the Coleman-Liau index. The Coleman-Liau index of a text is designed to output that (U.S.) grade level that is needed to understand some text. The formula is
index = 0.0588 * L - 0.296 * S - 15.8
where L is the average number of letters per 100 words in the text, and S is the average number of sentences per 100 words in the text.
어떤 텍스트가 몇 학년의 학생들에게 적합한지를 판단하는 readability를 구하는 문제.
1) L: the average number of letters per 100 words (100단어당 평균 글자 수)
2) S: the average number of sentences per 100 words (100단어당 평균 문장 수)
를 구해서 formula에 대입하여 index를 얻어낸다.
즉, L은 한 단어당 평균 글자 수 * 100, S는 한 단어당 평균 문장 수 * 100
<pseudocode>
- get_string()으로 인풋 받기
- 텍스트 내 글자, 단어, 문장 수 계산
- formula에 대입하여 인덱스 계산
- 인덱스에 맞는 결과 출력
<내 코드>
#include <cs50.h>
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
int calIndex(int letters, int words, int sentences);
int main(void)
{
// get_string으로 인풋 받기
string text = get_string("Text: ");
// 텍스트 내 글자, 단어, 문장 수 계산
int lettersCount = 0;
int wordsCount = 0;
int sentencesCount = 0;
for (int i = 0, n = strlen(text); i < n; i++)
{
if (isalpha(text[i]))
{
lettersCount++;
}
else if (isspace(text[i]))
{
wordsCount++;
}
else if (text[i] == '.' || text[i] == '?' || text[i] == '!')
{
sentencesCount++;
}
}
wordsCount++;
// formula에 대입하여 인덱스 계산
int index = calIndex(lettersCount, wordsCount, sentencesCount);
// 인덱스에 맞는 결과 출력
if (index < 1)
{
printf("Before Grade 1\n");
}
else if (index < 16)
{
printf("Grade %i\n", index);
}
else
{
printf("Grade 16+\n");
}
}
int calIndex(int letters, int words, int sentences)
{
double l = letters / (float) words * 100;
double s = sentences / (float) words * 100;
double index = (0.0588 * l) - (0.296 * s) - 15.8;
return round(index);
}
주의할 점은 calIndex() 함수를 구현할 때 형변환을 필수로 해주어야 한다는 점이다. letters와 words 인수 모두 정수형이기 때문에 단어당 글자수, 단어당 문장수를 구하는 연산을 할 때 수동으로 형변환을 해주지 않으면 정수형으로 계산되어 부적절한 결과가 나온다. 따라서 형변환을 해서 double 형으로 계산을 마친 뒤 round 함수를 사용하여 가장 가까운 정수 값으로 반환한다.
letters의 개수를 계산할 때는 isalpha 함수를 사용, words의 개수를 계산할 때는 isspace 함수를 사용하면 된다. 그러나 sentences의 개수를 계산할 때는 문장을 끝마치는 문장부호('.', '?', '!')를 직접 대입하여 개수를 대조해야 한다.
count 하는 함수를 따로 빼고 싶었는데 그렇게 하면 리턴 값이 세 개가 돼서 함수 하나로는 어떻게 해야할지 모르겠다. 그렇다고 letters, words, sentences 따로따로 함수 만들어서 세자니 굳이 for loop를 세 번이나 돌리는게 너무 비효율적인 것 같아서 그냥 main 메서드 안에 정의했다. 더 나은 방법이 있다면 알려주시길..
ctype.h
- isalnum - check whether a character is alphanumeric
- isalpha - check whether a character is alphabetical
- isblank - check whether a character is blank (i.e., a space or tab)
- isdigit - check whether a character is a digit
- islower - check whether a character is lowercase
- ispunct - check whether a character is punctuation
- isspace - check whether a character is whitespace (e.g., a newline, space, or tab)
- isupper - check whether a character is uppercase
- tolower - convert a char to lowercase
- toupper - convert a char to uppercase
(3) Substitution (feeling more comfy version)
In a substitution cipher, we “encrypt” (i.e., conceal in a reversible way) a message by replacing every letter with another letter. To do so, we use a key: in this case, a mapping of each of the letters of the alphabet to the letter it should correspond to when we encrypt it. To “decrypt” the message, the receiver of the message would need to know the key, so that they can reverse the process: translating the encrypt text (generally called ciphertext) back into the original message (generally called plaintext).
A key, for example, might be the string NQXPOMAFTRHLZGECYJIUWSKDVB. This 26-character key means that A (the first letter of the alphabet) should be converted into N (the first character of the key), B (the second letter of the alphabet) should be converted into Q (the second character of the key), and so forth.
A message like HELLO, then, would be encrypted as FOLLE, replacing each of the letters according to the mapping determined by the key.
In a file called substitution.c in a folder called substitution, create a program that enables you to encrypt messages using a substitution cipher. At the time the user executes the program, they should decide, by providing a command-line argument, on what the key should be in the secret message they’ll provide at runtime.
Specification
Design and implement a program, substitution, that encrypts messages using a substitution cipher.
- Implement your program in a file called substitution.c in a directory called substitution.
- Your program must accept a single command-line argument, the key to use for the substitution. The key itself should be case-insensitive, so whether any character in the key is uppercase or lowercase should not affect the behavior of your program.
- If your program is executed without any command-line arguments or with more than one command-line argument, your program should print an error message of your choice (with printf) and return from main a value of 1 (which tends to signify an error) immediately.
- If the key is invalid (as by not containing 26 characters, containing any character that is not an alphabetic character, or not containing each letter exactly once), your program should print an error message of your choice (with printf) and return from main a value of 1 immediately.
- Your program must output plaintext: (without a newline) and then prompt the user for a string of plaintext (using get_string).
- Your program must output ciphertext: (without a newline) followed by the plaintext’s corresponding ciphertext, with each alphabetical character in the plaintext substituted for the corresponding character in the ciphertext; non-alphabetical characters should be outputted unchanged.
- Your program must preserve case: capitalized letters must remain capitalized letters; lowercase letters must remain lowercase letters.
- After outputting ciphertext, you should print a newline. Your program should then exit by returning 0 from main.
문자열 encryption 하는 로직 만드는 문제. 그렇게 어렵지는 않았는데 c언어에 익숙하지 않아서 어떻게 구조화(정리) 해야하는지가 좀 막막... 코드가 진짜 더럽다ㅜ
<pseudocode>
-아규먼트가 없거나 3개 이상일 때 처리
-아규먼트 키값 받아오기
-키 유효성 검사 (26자리인지, 중복되는 캐릭터 없는지, 모두 알파벳인지)
-plaintext 받아오기
-ciphertext로 변환(소문자는 소문자로, 대문자는 대문자로)
-ciphertext 내보내기
주의할 점은 키값은 소문자로 들어오든 대문자로 들어오든 똑같은 알파벳으로 취급해야하고, 변환해서 내보낼 때는 plaintext에서 소문자였던 부분은 소문자로, 대문자였던 부분은 대문자로 내보내야 한다. 근데 뭔가 하면서 내 코드가 되게 비효율적인 것 같다는 생각을 많이 함..
<내코드>
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int checkKeyIsValid(string key);
int main(int argc, char* argv[])
{
// 아규먼트가 없거나 3개 이상일 때 처리
if (argc < 2 || argc > 2)
{
printf("Usage: ./substitution key\n");
return 1;
}
// 아규먼트 키값 받아오기
string key = argv[1];
for (int i = 0; i < strlen(key); i++) {
key[i] = toupper((unsigned char)key[i]);
}
// 키 유효성 검사 (26자리인지, 중복되는 캐릭터 없는지, 모두 알파벳인지)
int keyIsValid = checkKeyIsValid(key);
if (keyIsValid == 0)
{
return 1;
}
// plaintext 받아오기
string plaintext = get_string("plaintext: ");
// ciphertext로 변환
char ciphertext[100];
int ciphertextIndex = 0;
for (int i = 0, n = strlen(plaintext); i < n; i++)
{
if (isupper(plaintext[i]))
{
ciphertext[ciphertextIndex] = key[toupper(plaintext[i]) - 'A'];
ciphertextIndex++;
}
else if (islower(plaintext[i]))
{
ciphertext[ciphertextIndex] = tolower(key[toupper(plaintext[i]) - 'A']);
ciphertextIndex++;
}
else
{
ciphertext[ciphertextIndex] = plaintext[i];
ciphertextIndex++;
}
}
ciphertext[ciphertextIndex] = '\0';
// ciphertext 내보내기
printf("ciphertext: %s\n", ciphertext);
}
int checkKeyIsValid(string key)
{
int keyIsValid = 1;
int n = strlen(key);
if (n != 26)
{
return keyIsValid = 0;
}
int alphaArr[26] = {
0,
};
for (int i = 0; i < n; i++)
{
if (!isalpha(key[i]))
{
return keyIsValid = 0;
}
alphaArr[key[i] - 'A']++;
}
for (int i = 0; i < 26; i++)
{
if (alphaArr[i] < 1 || alphaArr[i] > 1)
{
return keyIsValid = 0;
}
}
return keyIsValid;
}
<수정한 코드>
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int checkKeyIsValid(string key);
int main(int argc, char *argv[])
{
// 아규먼트가 없거나 3개 이상일 때 처리
if (argc != 2)
{
printf("Usage: ./substitution key\n");
return 1;
}
// 아규먼트 키값 받아오기
string key = argv[1];
for (int i = 0; i < strlen(key); i++)
{
key[i] = toupper((unsigned char) key[i]);
}
// 키 유효성 검사 (26자리인지, 중복되는 캐릭터 없는지, 모두 알파벳인지)
if (!checkKeyIsValid(key))
{
return 1;
}
// plaintext 받아오기
string plaintext = get_string("plaintext: ");
// ciphertext로 변환
char ciphertext[100];
int ciphertextIndex = 0;
for (int i = 0, n = strlen(plaintext); i < n; i++)
{
if (isalpha(plaintext[i]))
{
char base = isupper(plaintext[i]) ? 'A' : 'a';
ciphertext[ciphertextIndex] = key[plaintext[i] - base];
ciphertextIndex++;
}
else
{
ciphertext[ciphertextIndex] = plaintext[i];
ciphertextIndex++;
}
}
ciphertext[ciphertextIndex] = '\0';
// ciphertext 내보내기
printf("ciphertext: %s\n", ciphertext);
}
int checkKeyIsValid(string key)
{
int n = strlen(key);
if (n != 26)
{
return 0;
}
int alphaArr[26] = {0};
for (int i = 0; i < n; i++)
{
if (!isalpha(key[i]))
{
return 0;
}
alphaArr[key[i] - 'A']++;
}
for (int i = 0; i < 26; i++)
{
if (alphaArr[i] != 1)
{
return 0;
}
}
return 1;
}
개선한 점
(1) keyIsValid 라는 변수는 쓸데가 없어서 없애고 그냥 바로 리턴값을 0, 1 이렇게 주는걸로 수정
(2) checkKeyIsValid 함수는 따로 선언하지 않고 if문 조건 내에서 불러옴
(3) 조건문을 바보같이 'alphaArr[i] < 1 and alphaArr[i] > 1' 이렇게 적어서 'alphaArr[i] != 1' 이렇게 수정함
(4) encryption 로직에서 대문자 검사하고 소문자 따로 검사하는 걸 삼항연산자를 써서 조건 하나로 통합함
내가 내 로직 일단 멍청하게나마 써서 과제 제출까지 한 다음 챗지피티의 도움으로 개선점 수정하는 방식으로 하고 있는데 괜찮은 것 같다.
'Web Development > CS' 카테고리의 다른 글
[CS50] Week3: Algorithms (0) | 2024.01.18 |
---|---|
[CS50] Week2: 컴파일링, Array, String (1) | 2024.01.13 |
[CS50] Week 1: 데이터 타입, 연산자, 조건문, 반복문, 커맨드라인(CLI), 1주차 과제 (1) | 2024.01.11 |