Problem Set 1
The first problem set has 2 difficulty levels. I started with the easier, but I’m going to talk about the harder as is requires a tiny bit more brain. It requires you to get input from a user that will build the following pyramid when the user enters 4 for example:
# #
## ##
### ###
#### ####
The easier level just required the first spaces and hashes, not the middle spaces or subsequent hashes or spaces. I found this quite hard to begin with and did need some help. I really struggled with getting my head around the nested for loops. So I asked AI to help out and I half got it, but still not 100% grasping what is going on. I get the user input validation, so the user can’t enter a negative value, or 0 value, or leave it blank or enter text. So I left it for a few weeks and came back to it after doing a lot of computer science and python lessons on the Brilliant app. Today I was going in clued up and fresh and I totally understand it. Here is the code:
#include <cs50.h>
#include <stdio.h>
int question(void); // get a positive int for height
int main(void)
{
int height = question();
int space1 = 0;
int block1 = 0;
int spacem = 0;
int block2 = 0;
for (int row = 0; row < height; row++)
{
// print initial spaces
for (space1 = 0; space1 < height - row - 1; space1++)
{
printf(" ");
}
// print intial blocks
for (block1 = 0; block1 < row + 1; block1++)
{
printf("#");
}
// print middle 2 spaces
for (spacem = 0; spacem < 2; spacem++)
{
printf(" ");
}
// print last blocks (same as above)
for (block2 = 0; block2 < row + 1; block2++)
{
printf("#");
}
printf("\n");
}
}
int question(void)
{
int height;
do
{
height = get_int("How high you want pyramid? ");
}
while (height < 1);
return height;
}
So now, I will write out what it is doing and where I struggled. First, user input validation. Using a do while loop which will keep prompting the user while the input is less than 1 (negative or 0 values), also the get_int function will not happen if the user enters text. This works by creating an integer variable called height, then calling the do while loop, setting the variable to the number the user types in. When input is good, the loop breaks and outputs the height variable through the question output of this function. Now we have our height value. In the main loop I take that question output and assign it to the height integer variable. I also define all of the variables I will use and set them to 0. I could do this within the individual for loops, but as part of my exploration, underneath the pyramid I got it to print out the number of hashes, spaces and total number of characters in the end row. If I defined the individual variables in their own for loop, then I wouldn’t have been able to print them at the end as they would have been out of scope. Next, and this is where the struggle started, getting my head around rows, blocks, spaces and new lines. Create a for loop that takes care of the rows. Logically, we need as many rows as the user’s input value, so this loop needs to loop as many times as the input value. To do this, the middle part of the for loop is
row < height
Meaning it will loop until there are as many rows as the height value. To make this loop actual rows, it just needs to have stuff printed inside followed by printing a new line. The new line bit is easy, that is at the end of the nested for loops. The nested for loops are there to put the content of each row. This table shows what we need (for the easier level) for a height of 4:
| row | spaces | hashes |
|---|---|---|
| 0 | 3 | 1 |
| 1 | 2 | 2 |
| 2 | 1 | 3 |
| 3 | 0 | 4 |
The maths for working out the spaces is:$$spaces=height – row$$ but as we can see, the row value starts at zero, so we need to minus 1:$$spaces=height-row-1$$The number hashes is the same as the row number, but again since the row value starts at zero, we use:$$hashes=row+1$$This is all the maths we need. So for the spaces, the for loop will keep looping until the spaces value is equal to $height-row-1$, and print a space each loop, then it will break as the condition is not true any more. On to the hashes or blocks I’ve called them. The for loop will keep looping until the block value equals $row+1$, then break. The difference between the easy and hard level is that each row on the hard level then has 2 spaces followed by the same number of blocks. For the spaces you could literally just printf(" "); but since this is a learning exercise i decided to do another for loop that just loops twice printing on space on each loop. Then I copy pasted the block for loop, giving it a new variable name.
That’s it. Job done. Everything understood and hunky dory. Now onto the next problem set.
Problem Set 2
Right now I’m, typing the notes out for this problem set as I’m working this out. Again, there are 2 difficulty levels. I will start with the easiest. This problem set requires you to work out how many coins to give to a customer. There will be a user input that sets the change owed; 70 cents for example, and given there are 25, 10, 5 and 1 cent coins, will calculate the number of coins; 4 coins.
So there will be a user input validation step like problem set 1. Then I’m thinking there will be a loop that will loop as long as the change is above or equal to 25 cents, then minus 25 off the change. The counter number will need to be output at the end. Then the new change will be compared against the next highest coin (10 cents), and looped until the remaining change is less than that coin, again outputting the counter number. The next loop will deal with 5 cents, then final loop dealing with 1 cents. At the end, there will need to be some maths that adds up all loop counters. I think I will create an excel right now to give me the answers to each value of change upto 200, to use as a reference to check of the code is correct. I won’t include this in my notes.
After some trial and error, here is my code:
#include <cs50.h>
#include <stdio.h>
int question(void); // get a positive int for change
int main(void)
{
int change = question();
printf("\n%i cents change\n\n", change);
int i25 = 0;
int i10 = 0;
int i5 = 0;
int i1 = 0;
// need to create a loop that minuses 25 off change and keeps looping till change is less than 25
for (i25 = 0; change >= 25; change -=25)
{
i25++;
printf("Change: %i\n", change);
}
// need to copy above loop but for 10cents
for (i10 = 0; change >= 10; change -=10)
{
i10++;
printf("Change: %i\n", change);
}
// need to do it again for 5c
for (i5 = 0; change >= 5; change -=5)
{
i5++;
printf("Change: %i\n", change);
}
// 1c
for (i1 = 0; change >= 1; change -=1)
{
i1++;
printf("Change: %i\n", change);
}
printf("%i 25c coins\n", i25);
printf("%i 10c coins\n", i10);
printf("%i 5c coins\n", i5);
printf("%i 1c coins\n", i1);
printf("Change: %i\n", change);
int coins = i25 + i10 + i5 + i1;
printf("total %i coins.\n\n", coins);
}
int question(void)
{
int change;
do
{
change = get_int("How much change? ");
}
while (change < 0);
return change;
}
It works. I have checked it with my spreadsheet and AI, but no part of it was written with AI. FUCKyes im so happy psyched. I knew I needed to first of all minus 25 of the change value, and each time that happens, the counter needs to increase by 1, starting at 0. I used a for loop instead of a do while loop as the for loop might not loop if the condition isn’t met but the do while loop will go at least once and then checks the condition. If the change is less than 25, then no 25c coins will be used so the 25c loop might not be needed. It took a bit of fiddling to get the maths and logic correct. Each time the loop loops when the condition is correct, the change value has the coin value taken off and that new change value is passed to the next loop. I have used change >= 25 as if the change is exactly 25, then that is one coin. All of this logic is copy and pasted for each coin. I have also included loads of print statements to see the change values and also how many of each coin in so I can see exactly what is happening, for debugging purposes. When I submit this I’ll take those out and leave just the juice.
While I was doing this, I know that this is what I have learned in week 1, but I’m also thinking there has got to be a better way with division and remainders. I asked AI about it. I found out that when you divide 2 integers, the decimal is dropped. Also the modulus function for finding remainders is %. Some cool modulus stuff:
1. $i \bmod 2$ is used to show odd or even numbers. Odds = 1, evens = 0.
2. $i \bmod 10$ is used to isolate the last digit of a number (the very right hand digit)
So I’m going to play around with that.
Done. Check it out:
#include <cs50.h>
#include <stdio.h>
int question(void); // get a positive int for change
int main(void)
{
int change = question();
int i25 = change / 25; // finds out how many 25s are in change
change = change % 25; //finds out the remainder when change is divided by 25, and updates the change variable with that value
int i10 = change / 10;
change = change % 10;
int i5 = change / 5;
change = change % 5;
int i1 = change / 1;
change = change % 1;
int coins = i25 + i10 + i5 + i1;
printf("total %i coins.\n\n", coins);
}
int question(void)
{
int change;
do
{
change = get_int("How much change? ");
}
while (change < 0);
return change;
}
Check out how much cleaner that is. Less code, don’t have to declare the variable at the beginning, and the computer has to work a lot less as it’s not having to loop around a bunch of stuff. I’m sure this is not what cs50 wants at this stage, so this was just for my own satisfaction.
Now onto the harder level. Which seems pretty interesting. We have to write a program that checks credit card numbers and outputs what payment provider it belongs to; AMEX, MASTERCARD, VISA or INVALID. I’m not going to write out the whole problem. I will however write out what my program needs to do:
1. It needs to check the first number or 2 of the card number to see what company it belongs to.
2. Then it needs to check the length of the card number to see if it fits into the specified length each company uses.
3. Then it needs to perform Luhn’s algorithm on the number to see if modulo 10 = 0.
4. Also need input validation so as short numbers are not valid or negatives or words/letters.
So I’m thinking that first off I will do a similar do while loop as in previous problem sets and then a loop that rejects any number less than 1,000,000,000,000 (the smallest 13 digit number) and and any number larger than 9999999999999999 (largest 16 digit number). The get_long function will reject anything that isn’t an integer. Then I’m thinking about if statements:
If number starts with (34 or 37) AND length == 15 AND luhn == 0 then print AMEX
Else if number starts with (51 or 52 or 53 or 54 or 55) AND length == 16 AND luhn == 0 then print MASTERCARD
Else if number starts with 4 AND length == (13 or 16) AND luhn == 0 then VISA
Else print INVALID
I’m going to have to create functions to output:
The start number
The length
Luhn’s number
First of all, the input validation. This is an easy one. I have copied the previous do while loop and changed the variable names. This is a separate function. In int main void the next step is to take that number and if it is smaller than 1000000000000 or larger than 9999999999999999 to print INVALID and break (return 0).
Maybe then it’s easiest to work out the length of the number. It is easy for me to create the prototype, and to pass the variable to the function, and to get a number out of the function, but the actual function alluded me. If I was doing this in the real world, i’d try to find a function in a library that did this, but I’m sure that’s not what we are supposed to do here. So I had to ask AI for help (not for the code, but for a hint) and it said a loop that divides by 10. That was enough for me to do the rest. Each time you divide by 10, it takes the last number away. You count how many times that happens until you reach zero, and that counter number is the length of the number.
Now the first digits. I didn’t use AI at all for this, except for when the compiler threw up errors. So I brought in the card number, and the length from the previous step. I created a loop that counted up until the length minus counter = 2, each loop truncated a digit from the end like the previous loop, but it stopped when there was 2 digits left, stored in number, and output number.
Lastly, Luhn’s algorithm. Ok difficult. The steps are starting from the end (right):
1. Take every other number, so 1st, 3rd, 5th, 7th etc (or every odd position number so $i\, \bmod\, 2=1$ ) and add it to a sum variable.
2. Take each even position number ($i\, \bmod\, 2=0$) one at a time and multiply it by 2. If the result is greater than 9, then split that number into the individual digits and add it to the sum variable, else add to sum variable.
3. Iterate until the counter equals the length.
4. Take the final sum variable and do $sum\, \bmod\, 10=luhn$
Just to add in at this point, I have used AI to clarify exactly what to do in the Luhn algorithm, and it suggested using modulo to get the odds and evens.
To be able to do this:
1. Create a for loop, i = 1.
2. Isolate the first number: $number=number\bmod10$
3. Store that in current.
4. Process: If $i \bmod 2 == 1$ add current to sum, else if $i \bmod 2 == 0$ then $current*2$ and store in times. If $times < 10$ then $sum=sum+times$ else $sum = sum + (times \bmod 10) + times/10)$.
5. Remove that number: $number=number/10$.
6. Increase $i$ by 1.
7. Loop over until $i = lengthh$.
My code is:
#include <cs50.h>
#include <stdio.h>
long question(void); // get a positive int for card number
int firstDigits(long cardNumber); // takes the card number and outputs the first 2 digits
int length(long cardNumber); // takes the card number and outputs the number of digits
int luhn(long cardNumber); // takes the card number and outputs the Luhn's number
int main(void)
{
long cardNumber = question();
// if card number is outside the range of 10000000000000 to 9999999999999999 print INVALID
if (cardNumber < 1000000000000 || cardNumber > 9999999999999999)
{
printf("INVALID\n");
return 0;
}
int firstInt = firstDigits(cardNumber); // takes the output of the firstDigits's function and stores it in the variable firstInt
int lengthh = length(cardNumber); // takes the output of the length function and stores it in the lengthh variable
int luhnn = luhn(cardNumber); // takes the output of the luhn function and stores it in the luhnn variable
if ((firstInt == 34 || firstInt == 37) && lengthh == 15 && luhnn == 0)
{
printf("AMEX\n");
}
else if ((firstInt >= 51 && firstInt <= 55) && lengthh == 16 && luhnn == 0)
{
printf("MASTERCARD\n");
}
else if ((firstInt / 10 == 4) && (lengthh == 13 || lengthh == 16) && luhnn == 0)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
}
}
long question(void)
{
long cardNumber;
do
{
cardNumber = get_long("Number: ");
}
while (cardNumber < 0);
return cardNumber;
}
int firstDigits(long cardNumber)
{
long number = cardNumber;
int lengthh = length(cardNumber);
int i = 0;
for (i = 0; lengthh - i > 2; i++)
{
number = number /10;
}
return number;
}
int length(long cardNumber)
{
long number = cardNumber;
int i = 0;
for (i = 0; number > 0; i++)
{
number = number / 10;
}
return i;
}
int luhn(long cardNumber)
{
long number = cardNumber;
int lengthh = length(cardNumber);
int sum = 0;
int times = 0;
int current = 0;
int luhn = 0;
for (int i = 1; i <= lengthh; i++)
{
current = number % 10;
if (i % 2 == 1)
{
sum = sum + current;
}
else if (i % 2 == 0)
{
times = current * 2;
if (times < 10)
{
sum = sum + times;
}
else if (times >= 10)
{
sum = sum + (times % 10) + (times / 10);
}
}
number = number / 10;
}
luhn = sum % 10;
return luhn;
}
I’m super pleased. It works. It passes the cs50 test. The style50 test threw up some minor silly stuff about formatting comments, but that is insignificant. In total this is 107 lines of code and I’ve never written this much before. It’s late. It’s time to cook dinner. It feels great.
Additional problems – 1
• The Task: Write a program that prints numbers from 1 to 100.
• The Twist: * If the number is divisible by 3, print “Fizz” instead of the number.
• If it’s divisible by 5, print “Buzz”.
• If it’s divisible by both 3 and 5, print “FizzBuzz”.
This one should be easy, since I can do the Luhn thing.
1. For loop, counting 1 to 100 separated by \n
2. If $i \bmod 3 \quad and \quad i \bmod 5 == 0$ print “FizzBuzz”
3. Else if $i \bmod3 == 0$ print “Fizz” instead of number
4. Else if $i \bmod 5 == 0$ print “Buzz”
5. Else print i.
This was too easy. Code is:
#include <stdio.h>
int main(void)
{
for(int i = 1; i <= 100; i++)
{
if ((i % 3 == 0) && (i % 5 == 0))
{
printf("FizzBuzz\n");
}
else if (i % 3 == 0)
{
printf("Fizz\n");
}
else if (i % 5 == 0)
{
printf("Buzz\n");
}
else
{
printf("%i\n", i);
}
}
}
Additional problems – 2
• The Task: Create a “Traveler’s Tool.” Ask the user for a temperature in Celsius and convert it to Fahrenheit, or ask for Pounds and convert to Kilograms.
• The Twist: Use a do-while loop or a switch statement to ask the user which conversion they want to do (1 for Temp, 2 for Weight, 3 to Exit).
Ok, first I’ll need to prompt the user for what what they want. Input validation. Then that will be passed to if statements. If temp then ask if they want C to F or F to C. Input validation. Then if statements with the appropriate prompt to get F or C, and the correct maths inside each.
#include <stdio.h>
#include <cs50.h>
int firstQ(void); // Which service, temp or weight
int tempQ(void); // F to C or C to F
int weightQ(void); // Lbs to Kg or Kg to Lbs
int main(void)
{
int servicee = firstQ(); // bring the service choice in
if (servicee == 1)
{
int temp = tempQ();
if (temp == 1)
{
float F = get_float("F= ?\n");
float C = ((F - 32) / 1.8);
printf("C = %.2f\n", C);
}
else
{
float C = get_float("C= ?\n");
float F = ((C * 1.8)+32);
printf("F = %.2f\n", F);
}
}
else
{
int weight = weightQ();
if (weight == 1)
{
float Lbs = get_float("Lbs= ?\n");
float Kg = Lbs * 0.453;
printf("Kg= %.2f\n", Kg);
}
else
{
float Kg = get_float("Kg= ?\n");
float Lbs = Kg * 2.205;
printf("Lbs= %.2f\n", Lbs);
}
}
}
int firstQ(void)
{
int service;
do
{
service = get_int("What you want? Press the corresponding number.\n1. Temperature\n2. Weight\n");
}
while ((service < 1) || (service > 2));
return service;
}
int tempQ(void)
{
int a;
do
{
a = get_int("Which option do you want? Press the corresponding number.\n1. F to C\n2. C to F\n");
}
while ((a < 1) || (a > 2));
return a;
}
int weightQ(void)
{
int a;
do
{
a = get_int("Which option do you want? Press the corresponding number.\n1. Lbs to Kg\n2. Kg to Lbs\n");
}
while ((a < 1) || (a > 2));
return a;
}
Easy. I totally forgot about floats, and then AI told me to use them, but my logic and code was spot on apart from that. It also told me about %.2f to print the float to 2 decimal places. Neat.
That’s it. I’m feeling good about this. Onto the next lecture.