Blog

On DJing, music, productivity, professional growth, and personal journey

Later Ctrl + ↑

Scrabble in C

In the second week of CS50, I was introduced to the notion of arrays and then given the following problem to solve as a practice:

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.

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).

First, I outlined the general steps needed to solve this problem:

  1. Get text inputs from the user;
  2. Calculate the score of each input;
  3. Show the winner.

For the first step, I’ll create two variables named word1 and word2 respectively, and assign each to the value of the respective user input using the get_string function from the CS50 library:

#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    // Prompt the user for two words
    string word1 = get_string("Player 1: ");
    string word2 = get_string("Player 2: ");

    // Compute the score of each word

    // Print the winner
}

That was easy. The trickiest part here, of course, is the second step, which is to assign each letter of the alphabet its unique score points value.

Here are the values:

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
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

Now that I know about arrays, I can create an array called points with a size of 26 and the type of integer, and assign values as follows:

#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    // Prompt the user for two words
    string word1 = get_string("Player 1: ");
    string word2 = get_string("Player 2: ");

    // Compute the score of each 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};

    // Print the winner
}

I don’t quite like the length of that like, but it seems there is not much to do about it since all values are set by the rules of the game.

Now that I know a little bit about functions too, I’ll create a separate function for computing the score. This should make the code more efficient and better designed, especially since I need to use it twice for calculating the score for each word.

I’ll call this function calculate_score, with one input parameter of the type of string, and output of integer. I can also straight away create a variable called score with the type of integer, as this is ultimately what I want to get from this function:

int calculate_score(string word)
{
    int score = 0;

    return score;
}

Now, the big question is how to assign each letter a corresponding score value. Well, turns out that every string is basically just an array of characters. Let’s take the word CODE as an example. We can imagine it as an array as follows, keeping in mind the zero-based indexing of arrays, i.e. starting counting from 0:

c word[i]
word[0] = C
word[1] = O
word[2] = D
word[3] = E

Knowing this, I can create a loop to ‘scan’ through the word character by character. For this loop to work, I would to know the length of the word, and luckily such a function already exists in the string.h library and is called strlen:

for (int i = 0; i < strlen(word); i++)
{
}

What I don’t quite like about this loop though, is that every time it works, it makes the function repeat that many times. To optimise this, let me create another variable called len with its value equal to the length of a word, and then use this variable as the condition of the loop:

for (int i = 0, len = strlen(word); i < len; i++)
{
}

Now, how to make the loop ‘know’ which score value corresponds to each letter? There is actually a trick that still blows my mind even after completing this task. Since every character has its numeric value in the ASCII code, we can use this information to ‘offset’ characters from one array to another and this way map them together.

Let’s take the capital letter ‘C’ from the word CODE as I used the example. Its numeric value in ASCII is 67, and the trick is to subtract the value of the letter A, which equals 65. Why A? Because it’s the first letter of the alphabet, and if we subtract it from another letter, we’ll know its place in the array. So 67 – 65 = 2, and that is exactly the index number we need from the points array.

To make it more visual, here is a similar exercise for all letters of the word CODE:

Letter ASCII Index Points
C 67 67 – 65 = [2] 3
O 79 79 – 65 = [14] 1
D 68 68 – 65 = [3] 2
E 69 69 – 65 = [4] 1

... which means the word CODE gives the Scrabble score of 7 (as 3 + 1 + 2 +1), and that is indeed correct. It works like a charm!

To make array indexing a little more obvious, I’ll add it to the score table too:

Letter: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Score: 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
Index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

This system doesn’t quite work for the lowercase letters though, because the ASCII values of the lowercase letters are different from the capital letters. However, it simply means that we need to subtract the value of the lowercase letter ‘a’ instead of the capital ‘A’.

So it seems I need an if statement within the loop to calculate the score depending on the case of the letters using isupper and islower functions from the ctype.h library, which I’ll include in the header too:

int calculate_score(string word)
{
    // Keep track of the score
    int score = 0;

    // Compute the score for each character
    for (int i = 0, len = strlen(word); i < len; i++)
    {
        if (isupper(word[i]))
        {
            score += points[word[i] - 'A'];
        }
        else if (islower(word[i]))
        {
            score += points[word[i] - 'a'];
        }
    }
    return score;
}

Since I’m using the points array in this function, I need to move it out from the main’s local scope to the global scope. The rest is easy: all I need to do is to call the function calculate_score (twice, for each word) and then compare players’ scores to determine the winner.

And here is my final code:

#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>

// points assigned to each letter of the alphabet
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};

int calculate_score(string word);

int main(void)
{
    // Prompt the user for two words
    string word1 = get_string("Player 1: ");
    string word2 = get_string("Player 2: ");

    // Compute the score of each word
    int score1 = calculate_score(word1);
    int score2 = calculate_score(word2);

    // Print the winner
    if (score1 > score2)
    {
        printf("Player 1 wins!\n");
    }
    else if (score1 < score2)
    {
        printf("Player 2 wins!\n");
    }
    else
    {
        printf("Tie!\n");
    }
}

int calculate_score(string word)
{
    // Keep track of the score
    int score = 0;

    // Compute the score for each character
    for (int i = 0, len = strlen(word); i < len; i++)
    {
        if (isupper(word[i]))
        {
            score += points[word[i] - 'A'];
        }
        else if (islower(word[i]))
        {
            score += points[word[i] - 'a'];
        }
    }
    return score;
}
 No comments    211   3 mo   C   CS50   Programming

Credit card validation in C

I keep learning programming, and today I share my baby walkthrough of the second homework from the CS50 course titled Credit.

As a quick disclaimer, I used only tools I’ve learned from the two lessons I had so far.

Here is the problem to solve:

A credit (or debit) card, of course, is a plastic card with which you can pay for goods and services. Printed on that card is a number that’s also stored in a database somewhere, so that when your card is used to buy something, the creditor knows whom to bill. There are a lot of people with credit cards in this world, so those numbers are pretty long: American Express uses 15-digit numbers, MasterCard uses 16-digit numbers, and Visa uses 13- and 16-digit numbers. And those are decimal numbers (0 through 9), not binary, which means, for instance, that American Express could print as many as 10^15 = 1,000,000,000,000,000 unique cards! (That’s, um, a quadrillion.)

Actually, that’s a bit of an exaggeration, because credit card numbers actually have some structure to them. All American Express numbers start with 34 or 37; most MasterCard numbers start with 51, 52, 53, 54, or 55 (they also have some other potential starting numbers which we won’t concern ourselves with for this problem); and all Visa numbers start with 4. But credit card numbers also have a “checksum” built into them, a mathematical relationship between at least one number and others. That checksum enables computers (or humans who like math) to detect typos (e. g., transpositions), if not fraudulent numbers, without having to query a database, which can be slow. Of course, a dishonest mathematician could certainly craft a fake number that nonetheless respects the mathematical constraint, so a database lookup is still necessary for more rigorous checks.

Write a program that prompts the user for a credit card number and then reports (via printf) whether it is a valid American Express, MasterCard, or Visa card number, per the definitions of each’s format herein. So that we can automate some tests of your code, we ask that your program’s last line of output be AMEX\n or MASTERCARD\n or VISA\n or INVALID\n, nothing more, nothing less. For simplicity, you may assume that the user’s input will be entirely numeric (i.e., devoid of hyphens, as might be printed on an actual card) and that it won’t have leading zeroes. But do not assume that the user’s input will fit in an int! Best to use get_long from CS50’s library to get users’ input.

First, I’ll prompt a user to input a number using the get_long function from the CS50 library:

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    long card_number = get_long("Number: ");
}

Next, I’ll do what seems to be the most challenging part of the problem, which is to calculate the checksum of a credit card. In the problem description, they provide additional information on Luhn’s Algorithm that determines the validity of a card as follows:

  1. Multiply every other digit by 2, starting with the number’s second-to-last digit, and then add those products’ digits together.
  2. Add the sum to the sum of the digits that weren’t multiplied by 2.
  3. If the total’s last digit is 0, the number is valid!

Let’s take the card number 4003600000000014 as an example. For the sake of visibility, let me underline every other digit, starting with the number’s second-to-last digit:

4 0 0 3 6 0 0 0 0 0 0 0 0 0 1 4

How to extract those digits from a number in C? Turns out, in maths, there is a great operation that can give us the remainder after dividing one number by another, and it’s called modulo, or mod. You can totally start laughing here, but honestly, I never heard of it before. For example, 1234 % 10 = 4 (with modulo operation being expressed by the percent sign), since 4 is the last digit in the number 1234.

Okay, that was the last digit. But how to get to the second-to-last digit, and then get every other digit from there? This might be not as obvious (at least it wasn’t at first to me), but since we’re dealing with whole numbers, a simple division by 10 basically moves us to one character in a number at a time. For example, 1234 / 10 = 123,4, but since the integer numbers get truncated, the result is actually 123.

Pretty cool, huh? Now let’s try to get every second number using the combination of division and modulo operations in the code:

#include <cs50.h>
#include <stdio.h>

long card_number;
int main(void)
{
    // Prompting a user to input a credit card number
    card_number = get_long("Number: ");

    // Calculating checksum
    int last_digit;
    int second_to_last_digit;

    while (card_number > 0)
    {
        // Removing the last digit
        last_digit = card_number % 10;
        card_number /= 10;

        // Removing the second last digit
        second_to_last_digit = card_number % 10;
        card_number /= 10;
        printf("%i", second_to_last_digit);
    }
    printf("\n");
}

This code prints 10000604, which are exactly the digits we need from the card number 4003600000000014. Keep in mind it’s not a number ten million six hundred four, but individual digits printed one after another. The order of digits is reversed, though it’s irrelevant in this case.

Next, according to Luhn’s Algorithm, I need to multiply each digit by 2 and get the sum of all of them combined. And I haven’t found a better solution than again using the same modulo and division operations, but this time on the extracted digits:

#include <cs50.h>
#include <stdio.h>

long card_number;
int main(void)
{
    // Prompting a user to input a credit card number
    card_number = get_long("Number: ");

    // Calculating checksum
    int every_second_digit = 0;
    int last_digit;
    int second_to_last_digit;
    int digit1;
    int digit2;

    while (card_number > 0)
    {
        // Removing the last digit and adding to every_other_digit
        last_digit = card_number % 10;
        card_number /= 10;

        // Removing the second last digit
        second_to_last_digit = card_number % 10;
        card_number /= 10;

        // Mupliplying second last digit
        second_to_last_digit *= 2;

        // Adding digits together to get the sum of every_second_digit
        digit1 = second_to_last_digit % 10;
        digit2 = second_to_last_digit / 10;
        every_second_digit += digit1 + digit2;
    }
    printf("%i", every_second_digit);
    printf("\n");
}

I’m pretty sure it could be done more elegantly, but I’m using only the knowledge I have so far from the first week of introduction to programming. Importantly, this code works, providing the result of the number 13, as you would expect from the sum of digits 2 + 1 + 2 + 8. Yay!

Lastly, to finish off the checksum, I’m going to find every other digit, add them together, and then add both sums in the final checksum variable:

#include <cs50.h>
#include <stdio.h>

long card_number;
int main(void)
{
    // Prompting a user to input a credit card number
    card_number = get_long("Number: ");

    // Calculating checksum
    int every_second_digit = 0;
    int every_other_digit = 0;
    int checksum = 0;
    int last_digit;
    int second_to_last_digit;
    int digit1;
    int digit2;

    while (card_number > 0)
    {
        // Removing the last digit and adding to every_other_digit
        last_digit = card_number % 10;
        card_number /= 10;
        every_other_digit += last_digit;

        // Removing the second last digit
        second_to_last_digit = card_number % 10;
        card_number /= 10;

        // Mupliplying second last digit
        second_to_last_digit *= 2;

        // Adding digits together to get the sum of every_second_digit
        digit1 = second_to_last_digit % 10;
        digit2 = second_to_last_digit / 10;
        every_second_digit += digit1 + digit2;
    }

    // Getting checksum
    checksum = every_second_digit + every_other_digit;

    printf("%i\n", checksum);
}

This code now prints the number 20, which is exactly the kind of value I want from a valid credit card. The hardest part is done!

Next, I’ll calculate the number of digits in the card number using a simple loop:

// Counting the number of digits in the card number
    long n = card_number;
    int number_of_digits = 0;
    do
    {
        n /= 10;
        number_of_digits++;
    }
    while (n != 0);

To keep the value of that card_number intact, I added a new variable called n and assigned its value to card_number, so I can do the maths with the n instead. In plain English, that loop says the following: while the card number is not equal to zero, divide the card number by 10, and for every division increase the number of number_of_digits variable which I use as a counter by one.

For example, if I were to type in the number 1234, it would go as follows:

  1. 1234 / 10 = 123; the counter increases from 0 to 1;
  2. 123,4 / 10 = 12; the counter increases from 1 to 2;
  3. 12,34 / 10 = 1; the counter increases from 2 to 3;
  4. 1 / 0 = 10; the counter increases from 3 to 4;

Now the card number gets equal to zero, and the loop ends with the counter equal to 4, which is the correct number of digits in the number 1234.

Next, I’ll make another counter to get the first two digits of the card number using pretty much the same logic:

// Getting the first two digits of the card number
    int first_two_digits = 0;
    long i = card_number;
    while (i > 100)
    {
        i /= 10;
        first_two_digits = i;
    }

Lastly, I’ll check the validity of the checksum and then check what credit company the card belongs to based of the card number length and starting digits.

So here is my final code:

// A program in C that checks the validity of a given credit card number

#include <cs50.h>
#include <stdio.h>

long card_number;
int main(void)
{
    // Prompting a user to input a credit card number
    card_number = get_long("Number: ");

    // Counting the number of digits in the card number
    long n = card_number;
    int number_of_digits = 0;
    do
    {
        n /= 10;
        number_of_digits++;
    }
    while (n != 0);

    // Getting first two digits of the card number
    int first_two_digits = 0;
    long i = card_number;
    while (i > 100)
    {
        i /= 10;
        first_two_digits = i;
    }

    // Calculating checksum
    int every_second_digit = 0;
    int every_other_digit = 0;
    int checksum = 0;
    int last_digit;
    int second_to_last_digit;
    int digit1;
    int digit2;

    while (card_number > 0)
    {
        // Removing last digit and adding to every_other_digit
        last_digit = card_number % 10;
        card_number /= 10;
        every_other_digit += last_digit;

        // Removing the second last digit
        second_to_last_digit = card_number % 10;
        card_number /= 10;

        // Mupliplying the second last digit
        second_to_last_digit *= 2;

        // Adding digits together to get the sum of every_second_digit
        digit1 = second_to_last_digit % 10;
        digit2 = second_to_last_digit / 10;
        every_second_digit += digit1 + digit2;
    }

    // Getting checksum
    checksum = every_second_digit + every_other_digit;

    // Checking for the card validity
    int validity = checksum % 10;
    if (validity == 0)
    {
        if ((number_of_digits == 15) && (first_two_digits == 34 || first_two_digits == 37))
        {
            printf("AMEX\n");
        }
        else if ((number_of_digits == 16) && (first_two_digits >= 51 && first_two_digits <= 55))
        {
            printf("MASTERCARD\n");
        }
        else if ((number_of_digits == 16 || number_of_digits == 13) && (first_two_digits / 10 == 4))
        {
            printf("VISA\n");
        }
        else
        {
            printf("INVALID\n");
        }
    }
    else
    {
        printf("INVALID\n");
    }
}

Update

A week later after posting this solution, I decided to get back to this problem and see if I could improve the code, particularly focusing on the checksum part as it’s essentially the main part.

Here is my updated code for this function:

// Calculate checksum using Luhn's Algorithm
int calculate_checksum(long long number)
{
    int sum = 0;
    int digit;
    bool is_second_digit = false;

    while (number > 0)
    {
        digit = number % 10; // Get the last digit
        number /= 10; // Reduce the number by one digit

        if (is_second_digit)
        {
            digit *= 2;
            sum += digit % 10 + digit / 10; // Split double numbers into single digits, e.g. 12 into 1 and 2
        }
        else
        {
            sum += digit;
        }

        // Switch the boolean, alternating between odd and even digits
        is_second_digit = !is_second_digit;
    }

    return sum;
}
 No comments    310   3 mo   C   CS50   Programming

Super Mario pyramids in C

Just to give a heads-up to the regular readers of my blog, this post is quite different from the usual topics that I write about.

Long story short, I decided to start learning programming, and as I’ve learned many times throughout the years of running this blog, there is no better way to learn something than by explaining it to others! After all, systemizing knowledge is one of the reasons I run my blog, so don’t be surprised to see some ‘weird’ posts here from now on.

In this post, I’ll give my walkthrough of solving the problem as a way to reinforce what I’ve learned. If by any chance you are a seasoned programmer, you are more than welcome to point out any mistakes or ways to improve for a newbie like me. However, if programming isn’t quite your thing, feel free to simply scroll through!

So, a couple of days ago I started by taking a course on computer science, and one of the first little homework I had was the following:

Toward the beginning of World 1-1 in Nintendo’s Super Mario Brothers, Mario must hop over adjacent pyramids of blocks. Implement a program in C that recreates that pyramid, using hashes (#) for bricks, as in the below:

...#  #
..##  ##
.###  ###
####  ####

And let’s allow the user to decide just how tall the pyramids should be by first prompting them for a positive number between, say, 1 and 8, inclusive.

Super Mario game on Nintendo

So, I’ll start by creating a variable with a type integer called height and the value equal to the return of the get_int function, which prompts a user for a number input. In case you wonder, get_int is a custom function defined in the CS50 library, so I included it in the header:

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    int height = get_int("Height: ");
}

Next, I’m adding validation of the user input since we need to accept only a specific range of numbers. To do so, I chose Do While Loop. Why this particular type of loop over the others? From my understanding so far, here are the best use cases for each type of loop:

  • For Loop works best when we know the exact number of repetitions, e. g. in some sort of counter;
  • While Loop works best when the number of repetitions is unknown;
  • Do While is pretty much the same as While Loop, but it’s guaranteed to trigger at least once.

So, here is the code, which in plain English says “Ask for input and keep asking while the provided number is less than one or greater than eight”:

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    do
    {
        int height = get_int("Height: ");
    }
    while (height < 1 || height > 8);
}

Now the problem is when I’m trying to compile the program, I get the following error: use of undeclared identifier ‘height’. This is happening because the variable height is defined within the Do part of the loop, so the While part doesn’t know about it. Basically, everything defined within the curly brackets stays within the curly brackets. This is a known problem of scope.

To solve the scope issue, I simply need to declare the height variable before the loop and outside its curly brackets:

#include <cs50.h>
#include <stdio.h>

int height;
int main(void)
{
    do
    {
        height = get_int("Height: ");
    }
    while (height < 1 || height > 8);
}

Note that I don’t need to assign any value to the variable, this is why a simple line int height; does the job. Also, note that in the curly brackets, height no longer needs int before, as I already defined the type of this variable as an integer at the top, and here I’m only assigning this variable a value.

Now this program works, and it only accepts numbers from 1 to 8 inclusive, as per the task description. If I type any negative number, zero, a character, or a number greater than 8, then the program is going to keep asking for another number, thanks to the Do While Loop.

Although this is correct, I don’t quite like the explicit usage of numbers 1 and 8 in the While expression. Why these particular numbers, and not 20 or 50, for example? Well, I surely know this from the problem description, but let’s say if I share this program with a colleague, it might be not so obvious to them. Numbers that appear in code seemingly out of the blue are called Magic Numbers.

To avoid Magic Numbers, I’m going to replace 1 and 8 with two variables, minHeight and maxHeight respectively. Keeping the scope in mind, I declare these variables at the top of the file, and since their values are set by the task rules, I’m giving them the type of const just to indicate that these numbers are the contestants and won’t change:

#include <cs50.h>
#include <stdio.h>

int height;
const int minHeight = 1;
const int maxHeight = 8;
int main(void)
{
    do
    {
        height = get_int("Height: ");
    }
    while (height < minHeight || height > maxHeight);
}

Functionally, this is exactly the same program as above, but I think it now has a slightly better design.

Next, I need to find a way of printing the hashes. If you think about it for a moment, what is a pyramid according to the problem set, for example, 3×3? It’s basically a brick with a length and height of 3, like so:

###
###
###

This obviously isn’t a pyramid shape yet, but we’ll get to it. Let’s think about how to print that brick first. The easiest way would be to simply repeat the printf function that many number of times:

printf("###\n");
printf("###\n");
printf("###\n");

You can see that the rows are identical, and whenever there is something copy-pastable, it might be a good indicator that there is a better solution. The first step would be to replace the rows by For Loop expression:

for (int i = 0; i < 3; i++)
{
    printf("###\n");
}

In this loop, I created a variable named i with a value of 0, and if i is less than three, then it prints the hashes, then it incrementally increases the value of і by 1, and then it checks it again until the expression of i is less than 3 is no longer true. In other words, this code does exactly the same as above – printing three rows of hashes – but in a more clever way.

But even though I’m printing rows dynamically, the number of columns is still set to a very specific number of hashes (three in this case), so I can go further to improve this. To do this, I’ll create another For Loop within that loop using a different variable j:

for (int i = 0; i < 3; i++)
{
    for (int j = 0; j < 3, j++)
    {
        printf("#");
    }
    printf("\n");
}

Note that now I can type only one hash in the printf function, and the rest is calculated mathematically. This nested structure of a loop within another loop seems exactly what I need for printing the pyramid, conceptually.

However, because the pyramid I need requires a few more parameters to get the actual pyramid shape, I think making this structure would be too large and hard to read. To slightly simplify the process, I’m going to move out the loop that prints rows into its own separate function.

To do so, I’ll add a new function called print_row. What kind of input it should take? It must be a number of hashes in a row to modify the length, so I’ll add one input parameter of a type integer that I call length. And it shouldn’t return any value, it just prints things on the screen, so I’ll keep its return value as void, which basically means nothing.

Going back to my main code now, here is what it looks like now:

#include <cs50.h>
#include <stdio.h>

int height;
const int minHeight = 1;
const int maxHeight = 8;
int main(void)
{
    do
    {
        height = get_int("Height: ");
    }
    while (height < minHeight || height > maxHeight);
}

void print_row(int length)
{

}

Nothing happens as the function literally does nothing so far, but I can work on that now. When I use this function print_row, I can pass in some number, and I can loop it that many times.

#include <cs50.h>
#include <stdio.h>

int height;
const int minHeight = 1;
const int maxHeight = 8;
int main(void)
{
    do
    {
        height = get_int("Height: ");
    }
    while (height < minHeight || height > maxHeight);
}

void print_row(int length)
{
    for (int i = 0; i < length; i++)
    {
        printf("#");
    }
    printf("\n");
}

This is basically the loop I created earlier in the brick example, but I still can’t see its result because this function is not used anywhere yet. Now I can try to use print_row after the Do While loop, and also to pass it the number that a user inputs in the program:

#include <cs50.h>
#include <stdio.h>

void print_row(int length);
int height;
const int minHeight = 1;
const int maxHeight = 8;
int main(void)
{
    do
    {
        height = get_int("Height: ");
    }
    while (height < minHeight || height > maxHeight);
    print_row(height);
}

void print_row(int length)
{
    for (int i = 0; i < length; i++)
    {
        printf("#");
    }
    printf("\n");
}

Note that I had to add the prototype of the print_row function at the top of my file again to solve the scope issue, similar to how I’ve done previously with my variables. In plain English, this program now asks a user to input a number from 1 to 8 and then prints that exact number of hashes in a single line. Why only a single line? Because when I use the print_row function, it gets only one number that a user types in, the variable height, and that’s it.

So now I need to make the print_row function repeat itself some number of times, just like I did earlier in the nested loops example, and kind of wrap it around another loop. How many times the loop should be repeated? As many as the number that I get from a user:

#include <cs50.h>
#include <stdio.h>

void print_row(int length);
int height;
const int minHeight = 1;
const int maxHeight = 8;
int main(void)
{
    do
    {
        height = get_int("Height: ");
    }
    while (height < minHeight || height > maxHeight);

    for (int i = 0; i < height; i++)
    {
        print_row(height);
    }
}

void print_row(int length)
{
    for (int i = 0; i < length; i++)
    {
        printf("#");
    }
    printf("\n");
}

It works, and now I get a brick of size x×x. For example, if I type in number 4, I get this:

####
####
####
####

... and I type in number 8, I get this:

########
########
########
########
########
########
########
########

So I’m getting close, but for now, it’s still a square brick. How to make the rows more dynamic, i.e. print only one hash on the first row, two hashes on the second row, and so forth? I guess instead of using height as my input parameter in print_row(height);, I can replace it with some other variable. I think I can use the variable i, which I use for counting the number of repetitions, however, since it starts with 0, I would get zero hashes on the first line, one hash on the second line, and so on. So to offset this, I’ll tell my program to start counting by i + 1, like so:

#include <cs50.h>
#include <stdio.h>

void print_row(int length);
int height;
const int minHeight = 1;
const int maxHeight = 8;
int main(void)
{
    do
    {
        height = get_int("Height: ");
    }
    while (height < minHeight || height > maxHeight);

    for (int i = 0; i < height; i++)
    {
        print_row(i + 1);
    }
}

void print_row(int length)
{
    for (int i = 0; i < length; i++)
    {
        printf("#");
    }
    printf("\n");
}

And this now produces the following result, for example, if I type in 8:

#
##
###
####
#####
######
#######
########

Half of the pyramid is done, yay! Almost there. The next question is how to add another half of the pyramid and what’s the difference between them.

Let me draw the left half of the pyramid just to look at it for a moment, let’s say with a height of 4:

...#
..##
.###
####

So it seems that I need to calculate not only the number of hashes but also the number of spaces (dots in this case, for the sake of visibility) to offset the hashes on each line.

To do so, I’ll add another input to my print_row function, an integer named spaces, not forgetting to mirror it in the function prototype at the top. Then inside the function, I need to adjust what is printed on every row. According to the pyramid shape, it must be some number of dots (spaces) first, then hashes for the left side of the pyramid, then a gap, and then hashes for the right side of the pyramid. For the right side, no extra spaces are needed after hashes since they won’t be visible anyway.

Also, there is a curious relationship between a row number and the amount of spaces needed to offset the hashes. For example, for the 8th row, no spaces are needed; for the 7th row, only 1 space is needed, and so forth. So my For Loop representing spaces, I’m going to assign the variable i a value of height, and then count downwards, incrementally decreasing the number of spaces by 1.

Lastly, need to not forget to add the second input when I’m calling the print_row function to print both hashes and spaces in each column.

And here is the final code:

// A program in C that recreates the pyramid from Mario using hashes (#) for bricks

#include <cs50.h>
#include <stdio.h>

void print_row(int spaces, int length);
int height;
const int minHeight = 1;
const int maxHeight = 8;
int main(void)
{
    // Prompting a user to input a number to define the height of the pyramid, accepting only numbers from 1 to 8, inclusive
    do
    {
        height = get_int("Height: ");
    }
    while (height < minHeight || height > maxHeight);

    // Printing columns of the pyramid
    for (int i = 0; i < height; i++)
    {
        print_row(i + 1, i + 1);
    }
}

// Printing rows of the pyramid
void print_row(int spaces, int length)
{
    // Printing the dots (spaces) on the left
    for (int i = height; i > spaces; i--)
    {
        printf(" ");
    }

    // Printing hashes for the left side of the pyramid
    for (int i = 0; i < length; i++)
    {
        printf("#");
    }

    // Printing the gap between the sides of the pyramid
    printf("  ");

    // Printing hashes for the right side of the pyramid
    for (int i = 0; i < length; i++)
    {
        printf("#");
    }

    printf("\n");
}

Update

As kindly pointed out by Sergey Khivuk after I published the post, there are at least a few ways to improve the code. I’ll leave his feedback below:

  • Instead of passing two identical parameters to void print_row(int spaces, int length), just pass one and replace spaces with length. The pyramid is always square in terms of width and height anyway.
  • Instead of:
for (int i = 0; i < height; i++)
{
  print_row(i + 1);
}

...just use:

for (int i = 1; i <= height; i++)
{ 
  print_row(i);
}

There will be no need to calculate the parameter every time the function is called.

 1 comment    498   3 mo   C   CS50   Programming

Dark Entity (Hypnotic Mix) is out now

An ever darker version of my collaboration with Enlusion

Some interesting backstory. In 2021, Enlusion and I collaborated to create ‘Dark Entity’. While the original mix was a heavy-hitting dancefloor banger, I also wanted to have a more stripped-down version and that is how we made the ‘Heads-down Mix’.

Fast-forward to 2024, upon relistening to those versions I realised that I want something even more heads-down, more... hypnotic. So I sent a message to Kirill, and our dialogue went something like that:

— me: Let’s get rid of those acids, BXR-style snares and other extra sounds?
— Kirill: Hmm okay. But we’re going to add new melodies and a new bassline then, right?... Right?
— me: 😎

So there you go guys, Dark Entity (Hypnotic Mix) is out now, and Kirill is probably still mad that we didn’t add five more layers of extra melodies!

Preview:

Ask Me Anything 001

Building Up a Set, My DJ Playlists, and Opinion on the Trance Scene

Welcome to ‘Ask Me Anything’, a new audio series where I answer your questions on music curation, the art of DJing, the music industry, record labels, behind-the-scenes, productivity, tech, and other areas of expertise and pretty much anything else, as the name suggests.

As you might know, over the years I’ve been pretty vocal about sharing knowledge and have written over a hundred of advice on my blog. So although this podcast is new, here I’m doing pretty much the same thing, just in a new format.

In this episode:

I would love to see how a DJ set builds up. Is it driven by the mood or more like new tracks / tracks you haven’t played before?
It would be really interesting to understand the reasoning behind how you curate your DJ playlists
How would you assess the current state of the Trance scene, including your thoughts on the quality of music being released nowadays?

You can listen to a preview of the show on Apple Podcasts, Spotify Podcasts, YouTube, SoundCloud, and Mixcloud, while full episodes are available exclusively to my subscribers on Patreon.

Preview:

 No comments    97   4 mo   Ask Me Anything   Curation   DJing

Rave Podcast 148

The February podcast is here, bringing you a lot of heads-down grooves. This is the kind of sound and the tempo I really like now, and I hope you do too! The show also includes a couple of unreleased tracks from JOOF Recordings and Be As One, so plenty of fresh music here from the names like Wata Igarashi, Atlas, Coyu, Talfelt, DJ Dextro, Kashpitzky, Villa, Nordic Echoes, Alan Backdrop, and more.

Tracklisting:

00:00 Sigha — Black Massing (Wata Igarashi ‘Dusk Falls’ Remix) [Token]
04:25 Atlas — Caravan (Original Mix) [JOOF Recordings]
09:50 Daniel Lesden, Enlusion — Dark Entity (Hypnotic Mix) [Forescape Digital]
15:14 Coyu — We Live In A World Full Of Rage (Original Mix) [Suara Records]
18:52 Talfelt — Motus Chao (Original Mix) [Vitus’ Curse]
26:40 Paragliders — Lithium 2 (Original Mix) [Solieb Digital]
27:35 DJ Dextro — Commodore (Original Mix) [Second State Audio]
31:03 Kashpitzky — Response (Original Mix) [Be As One]
34:00 Villa — Elysian Pool (Original Mix) [Vitus’ Curse]
36:15 Talfelt — Memories (Original Mix) [Vitus’ Curse]
39:16 The Groove Room — Surge (Original Mix) [Numerus Records]
42:25 Hardickoff — Hipotalamus (Original Mix) [Soviett]
45:34 Nordic Echoes — Starbound (Original Mix) [JOOF Recordings]
49:37 Alan Backdrop — Vortex (Original Mix) [Not On Label]
54:04 Steffi — Individuals From All Walks (Rosati Remix) [Candy Mountain]
56:05 Kashpitzky — Romantic & Dead (Original Mix) [Be As One]
 No comments    93   4 mo   Daniel Lesden   Music   Rave Podcast

First ‘Ask Me Anything’ recording session

Had a productive day recording the pilot episode of my new ‘Ask Me Anything’ series – and it was pretty fun (spoiler alert: I almost lost my voice by the end of the recording!).

Stay tuned, as it will be published next week!

My entire DJ collection: I’m sharing all of my 84 playlists!

Back in 2019, I published an article How I prepare my DJ playlists (which now has over 21K views), providing behind-the-scenes into the structure of my DJ collection. Since then, my collection has evolved, as it’s an ever-changing process that reflects my DJ needs.

And today, I’m sharing my entire DJ collection with my Patreon subscribers. This is something that sane DJs probably would never do!

So, here’s the thing. I have my DJ collection with over 7000 tracks on iTunes (‘Music’ app on macOS), which I use as the main hub for all my music. To keep all of the music sorted and to find tracks during my DJ sets easily, I keep all those tracks in over 80 playlists, neatly organised by energy levels and mood. This collection and the playlists are mirrored in Rekordbox, which is the main DJ software that I use to export music to USB sticks which I then plug into the DJ decks on my gigs.

I also have that music collection and playlists mirrored on Spotify, and this is what I am sharing with my Patreon subscribers. While I can’t share the physical audio files from my music library for copyright reasons, even Spotify playlists are a pretty big deal.

First of all, that’s a lot of great music. A lot. Secondly and most importantly, this is my real DJ collection that I am actively using and keeping up to date – so it’s a great material for learning and inspiration to see how I organise my playlists, a real behind-the-scenes peek into the mind of a DJ. I’ve also recorded a video walkthrough to provide more explanation of my playlists.

If it sounds interesting to you and want to get access to it, consider joining me on Patreon (and have many more goodies besides this DJ collection): patreon.com/daniellesden

 No comments    143   4 mo   Behind the scenes   DJing   Patreon

Rave Podcast 147

The January edition of Rave Podcast is available on SoundCloud, YouTube, and Mixcloud, while subscribers on Patreon enjoyed exclusive early access one week before. Individual tracks are also available on the Spotify playlist, and you can find links to all platforms below.

To start 2024 on a high note, and as we also get almost to the Rave Podcast’s 13th anniversary, I decided to make this episode 2 hours long. Expect a blend of raw, deep, and hypnotic techno and trance with a psychedelic twist. Enjoy the journey!

Tracklisting:

0:00:00 Whirloop — Departure (Intro) [JOOF Recordings]
0:01:13 Fabio Florido — Galactica (Luigi Tozzi Remix) [Runa]
0:05:23 Vladw — Teppou (Original Mix) [Node Recordings]
0:09:31 Bastian Bux — Neopisivo (Original Mix) [Suara Records]
0:13:38 John 00 Fleming — Baphomets Horn (Original Mix) [JOOF Recordings]
0:18:28 Bitkit — Meteorite (Original Mix) [Dacru Records]
0:22:38 Rocky — Pass (Mindwave Remix) [Echoes Records]
0:25:42 Fabio Florido — Galactica (Original Mix) [Runa]
0:30:24 Duotekk — Zlow (Original Mix) [Tip Records]
0:36:27 Stef Mendesidis — Pain Killer (Original Mix) [Klockworks]
0:39:28 Luke Hess — E Grand Blvd (Fireground Remix) [Dolly]
0:42:42 Steffi — Primary Chaos (DJ Agitated Remix) [Candy Mountain]
0:46:29 Altinbas — Biosfera (Original Mix) [Observer Station]
0:50:08 Regent — Arithma (Original Mix) [Planet Rhythm Records]
0:53:48 Axel Karakasis — Day Zero (Original Mix) [Remain Records]
0:57:00 00.db — Orba (Album Version) [Fektive Records]
1:02:15 Triforce — Transmute (Original Mix) [Zenon Records]
1:07:31 Uncharted Territory — Odyssey (Original Mix) [Stereo Society]
1:10:29 William Arist — Form (Original Mix) [Mutual Rytm]
1:13:32 Kashpitzky — Timeline Off (Original Mix) [Monnom Black]
1:17:48 Anne — Zephyrus (Original Mix) [Mutual Rytm]
1:20:02 Raar — Blue Mitsubishi (Original Mix) [Antiverse]
1:24:32 Skodde — Quantum Travel (Original Mix) [Flux Collective]
1:27:44 Raar — Flashlight (Original Mix) [Antiverse]
1:31:54 INTT — Sanna (Original Mix) [Amphibian Records]
1:36:16 The Chronics — Mind Trip (Original Mix) [Raw Label]
1:40:49 Oprofessionell — Pull Up (HT Mix) [UTE]
1:43:38 Ki Dake — Fully (Original Mix) [Betonstrand]
1:46:27 Narciss — Purple Fiction (Original Mix) [Space Trax]
1:50:59 Cybernet — Enceladus (Original Mix) [Gestalt Records]
1:54:40 Modus — Harbu Darb (Original Mix) [Stereo Society]
 No comments    127   5 mo   Daniel Lesden   Music   Rave Podcast

Underground Trance Essentials Vol.8

As a Beatport curator, every once in a while, I create DJ charts within the ongoing series titled Underground Trance Essentials to highlight some of the best tracks from the newly added Trance (Raw / Deep / Hypnotic) genre. Whether they were released yesterday or a decade ago, these tracks unquestionably deserve appreciation.

In this instalment, I featured music from Wata Igarashi, Ipeo, Adam Pits, Volte-Face, Atlas, VBK, Mac Declos, Not Even Noticed, Samoh, and Reflex Blue.

Don’t miss out on this selection – take a listen: beatport.com/chart/underground-trance-essentials-vol8/801277

I’m also curating a Spotify playlist featuring tracks from these charts, making it convenient to enjoy them all in one consolidated place.

Earlier Ctrl + ↓
© Daniel Sokolovskiy, 2024
Powered by Aegea