Solutions#

Lunar lander control#

Solution to Activity 2

  1. air_conditioner = 1;

  2. if (...) else ...

  3. a > b

  4. 5

  5. success = a > b;

Solution to Activity 3

#include <stdio.h>

int Control(int altitude) {
  int thruster = 0;

  //// BEGIN SOLUTION
  if (altitude > 100)
    thruster = 0;
  else if (altitude > 0)
    thruster = 1;
  else
    thruster = 0;
  //// END SOLUTION

  return thruster;
}

void Test(int altitude) {
  int thruster = Control(altitude);
  int behaviorCorrect = (altitude > 100 && thruster == 0) ||
                        (altitude <= 100 && altitude > 0 && thruster == 1) ||
                        (altitude <= 0 && thruster == 0);
  char *behaviorCorrectIcon = behaviorCorrect ? "✅" : "❌";
  printf("For altitude %3d, your thruster is %d |%s|\n", altitude, thruster,
         behaviorCorrectIcon);
}

int main(void) {
  Test(150);
  Test(100);
  Test(50);
  Test(0);
  Test(-1);
}

Conveyor belt capacity check#

Solution to Activity 8

#include <stdio.h>

const double packageWeightPerMotor = 5.6;

int main(void) {
  printf("How many motors are carrying the packages? ");
  int motorCount;
  scanf("%d", &motorCount);

  printf("How many kg of packages do we expect? ");
  double packageWeight;
  scanf("%lf", &packageWeight);

  puts(packageWeight / motorCount <= packageWeightPerMotor
           ? "Yes! The conveyor belt can carry the packages."
           : "No. The conveyor belt cannot carry the packages.");
}

Spare parts inventory assistant#

Solution to Activity 12

  1. int array[];

  2. int array;

  3. char[] choices = {"black bird", "great tit", "falcon"};

  4. char names[] = {"ird", "grea", "con"};

  5. int part_ids[]= {3093, -49318, 3092.812};

     You will get a warning about implicit type conversion of 3092.812.
    
  6. string names[] = [];

     1. We need curly brackets 2. `string` does not exist
    
  7. double intensity[X][Y][Z];

Solution to Activity 19

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Configurable data
#define LINE_SIZE 100

const char *PARTS[] = {"hydraulic pump", "PLC module", "servo motor"};
const char ASSISTANT_GREETING[] =
    "Hej. Welcome to the spare parts inventory!\n";
const char ASSISTANT_QUESTION[] = "Which part do you need? ";
const char ASSISTANT_REPLY_POSITIVE[] = "I have got %s here for you 😊. Bye!\n";
const char ASSISTANT_REPLY_NEGATIVE[] =
    "I am afraid we don't have any %s in the inventory 😔\n";
const char *USER_QUESTIONS[] = {
    "Do you actually have any parts?",
    "Is there anything in stock at all?",
};
const char ASSISTANT_REPLY_NUMBER_OF_PARTS[] = "We have %lu part(s)!\n";

// Program logic
bool user_asked_a_question = false;

int main(void) {
  printf(ASSISTANT_GREETING);
  while (true) { // Program exits through return
    printf("%s ", ASSISTANT_QUESTION);
    char line[LINE_SIZE];
    fgets(line, sizeof line, stdin);
    line[strcspn(line, "\n")] = '\0'; // replace newline

    // Part check
    for (size_t i = 0; i < _Countof PARTS; ++i) {
      if (strcmp(PARTS[i], line) == 0) {
        printf(ASSISTANT_REPLY_POSITIVE, line);
        return EXIT_SUCCESS;
      }
    }

    // Other question check
    for (size_t i = 0; i < _Countof(USER_QUESTIONS); ++i) {
      if (strcmp(USER_QUESTIONS[i], line) == 0) {
        user_asked_a_question = true;
        printf(ASSISTANT_REPLY_NUMBER_OF_PARTS, _Countof(PARTS));
        for (size_t i = 0; i < _Countof(PARTS); ++i)
          puts(PARTS[i]);
        break;
      }
    }

    // If it was not a user question, then it must have been a part
    if (!user_asked_a_question) {
      printf(ASSISTANT_REPLY_NEGATIVE, line);
      user_asked_a_question = false;
    }
  }
}

Rock paper scissors lizard Spock#

Solution to Activity 29 (RPS resolution logic)

#include <stdio.h>
enum { ROCK, PAPER, SCISSORS, SHAPE_COUNT } p1, p2;
const char *SHAPE_STRINGS[] = {"🪨", "️🗒️", "✂️"};

int main() {
  for (size_t p1 = 0; p1 < SHAPE_COUNT; ++p1) {
    for (size_t p2 = 0; p2 < SHAPE_COUNT; ++p2) {
      printf("If p1: %s  and p2: %s  => ", SHAPE_STRINGS[p1],
             SHAPE_STRINGS[p2]);
      //// BEGIN SOLUTION
      if (p1 == ROCK && p2 == PAPER)
        puts("p1 looses.");
      else if (p1 == ROCK && p2 == SCISSORS)
        puts("p1 wins.");
      else if (p1 == PAPER && p2 == ROCK)
        puts("p1 wins.");
      else if (p1 == PAPER && p2 == SCISSORS)
        puts("p1 looses.");
      else if (p1 == SCISSORS && p2 == ROCK)
        puts("p1 looses.");
      else if (p1 == SCISSORS && p2 == PAPER)
        puts("p1 wins.");
      else
        puts("Tie.");
      //// END SOLUTION
    }
    puts("");
  }
}
#include <stdio.h>
enum { ROCK, PAPER, SCISSORS, SHAPE_COUNT } p1, p2;
const char *SHAPE_STRINGS[] = {"🪨", "️🗒️", "✂️"};

int main() {
  for (size_t p1 = 0; p1 < SHAPE_COUNT; ++p1) {
    for (size_t p2 = 0; p2 < SHAPE_COUNT; ++p2) {
      printf("If p1: %s  and p2: %s  => ", SHAPE_STRINGS[p1],
             SHAPE_STRINGS[p2]);
      //// BEGIN SOLUTION
      switch (p1) {
      case ROCK:
        switch (p2) {
        case PAPER:
          puts("p1 looses.");
          break;
        case SCISSORS:
          puts("p1 wins.");
          break;
        default:
          puts("Tie.");
        }
        break;
      case PAPER:
        switch (p2) {
        case ROCK:
          puts("p1 wins.");
          break;
        case SCISSORS:
          puts("p1 looses.");
          break;
        default:
          puts("Tie.");
        }
        break;
      case SCISSORS:
        switch (p2) {
        case ROCK:
          puts("p1 looses.");
          break;
        case PAPER:
          puts("p1 wins.");
          break;
        default:
          puts("Tie.");
        }
        break;
      }
      //// END SOLUTION
    }
    puts("");
  }
}
#include <stdio.h>
enum { ROCK, PAPER, SCISSORS, SHAPE_COUNT } p1, p2;
const char *SHAPE_STRINGS[] = {"🪨", "️🗒️", "✂️"};

int main() {
  for (size_t p1 = 0; p1 < SHAPE_COUNT; ++p1) {
    for (size_t p2 = 0; p2 < SHAPE_COUNT; ++p2) {
      printf("If p1: %s  and p2: %s  => ", SHAPE_STRINGS[p1],
             SHAPE_STRINGS[p2]);
      //// BEGIN SOLUTION
      const enum {
        W,
        L,
        T
      } resolution_table[][T + 1] = {
          {T, L, W},
          {W, T, L},
          {L, W, T},
      }; // (p1, p2, resolution)

      switch (resolution_table[p1][p2]) {
      case T:
        puts("Tie.");
        break;
      case W:
        puts("p1 wins.");
        break;
      case L:
        puts("p1 loses.");
      }
      //// END SOLUTION
    }
    puts("");
  }
}

Solution to Activity 21 (Flowchart v1 – overview)

        flowchart TD
_s(start) -->b[Welcome user and show menu]

b --> c{menu}
c -->|game| s["print *Starting Game*"]
c -->|exit| f[Say goodbye]--> _e
c -->|else| t[Error message]--> c

s --> g[Game] --> _e

_e(end)

    

Solution to Activity 30 (RPSSL resolution logic using difference between shapes)

#include <stdio.h>
enum { SPOCK, SCISSORS, PAPER, ROCK, LIZARD, SHAPE_COUNT } player1, player2;

const char *SHAPE_STRINGS[] = {"🖖", "✂️", "️🗒️", "🪨", "🦎"};

int main() {

  for (size_t player1 = SPOCK; player1 < SHAPE_COUNT; ++player1) {
    for (size_t player2 = SPOCK; player2 < SHAPE_COUNT; ++player2) {
      printf("If player1: %s  and player2: %s  => ", SHAPE_STRINGS[player1],
             SHAPE_STRINGS[player2]);
      //// BEGIN SOLUTION
      switch (player2 - player1) {
      case 0:
        puts("Tie.");
        break;
      case -4:
      case -2:
      case 1:
      case 3:
        puts("Player1 wins.");
        break;
      default:
        puts("Player1 looses.");
      }
      //// END SOLUTION
    }
    puts("");
  }
}

Refinement for the Game process:

        flowchart TD
  _s(start)--> w{winning score reached?}

  w -->|no| m[ask the player to act]
  m --> s[save players input]
  s --> m2[generate a random shape]
  m2 --> s2[compare shapes]
  s2 --> c{outcome}
  c -->|Human won| c1[++human_score] --> p
  c -->|Agent won| c2[++agent_score] --> p
  c -->|"else (tie)"| p
  p[announce scores] --> w
  w -->|yes| p2[Announce the winner] --> _e

  _e(end)
    
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

const unsigned WINNING_SCORE = 5;

// String constants
const char WELCOME[] = R"(
Welcome to 🪨  ✂️  🗒️  🖖  🦎 !
(s) Single player
(e) Exit
)";
const char SELECT_MENU_ITEM[] = "Select an item: ";
const char SELECT_SHAPE[] = R"(
Select a shape:
0🪨  || 1🦎  || 2✂️  || 3🗒️  || 4🖖 : )";
const char AGENT_PLAYED[] = "Agent: %d\n";
const char STARTING_GAME[] = "Starting game 🎉";
const char EXITING_GAME[] = "Bye 👋";
const char GAME_SHAPE_KEY_DOES_NOT_EXIST[] =
    "❌ Shape key %c does not exist. Try again.\n";
const char HUMAN_REPR[] = "👫 Human";
const char AGENT_REPR[] = "🤖 Agent";
const char SCORE_STATUS[] = "👫:🤖  %d:%d\n";
const char MENU_USE_RIGHT_KEYS[] = "Use the keys`s`, or `e`.";

// Datatypes
typedef enum { ROCK, LIZARD, SCISSORS, PAPER, SPOCK, SHAPE_COUNT } shape_t;

int main() {
  srand(time(NULL)); // Use another random sequence every time
  printf(WELCOME);

  int input_char; // for getchar()

  // Menu
  bool invalid_menu_key_used;
  do {
    invalid_menu_key_used = false;
    printf(SELECT_MENU_ITEM);
    switch (input_char = getchar()) {
    case 's':
      puts(STARTING_GAME);
      break;
    case 'e':
      puts(EXITING_GAME);
      return EXIT_SUCCESS;
    default:
      invalid_menu_key_used = true;
      puts(MENU_USE_RIGHT_KEYS);
    }
    while (getchar() != '\n')
      ;
  } while (invalid_menu_key_used);

  // Game
  unsigned human_score = 0, agent_score = 0;
  while (human_score < WINNING_SCORE && agent_score < WINNING_SCORE) {

    shape_t human_shape, agent_shape;
    printf(SELECT_SHAPE);

    auto input_char = getc(stdin);
    human_shape = input_char - '0';
    // Go back to 0, so the chars '0', '1', '2' ... correspond to 0, 1, 2 ...

    while (getchar() != '\n')
      ; // Flush \n and other characters

    if (human_shape >= SHAPE_COUNT) {
      printf(GAME_SHAPE_KEY_DOES_NOT_EXIST, input_char);
      continue;
    }
    agent_shape = rand() % SHAPE_COUNT;
    printf(AGENT_PLAYED, agent_shape);

    // Resolution
    auto difference = (agent_shape - human_shape + SHAPE_COUNT) % SHAPE_COUNT;
    if (difference == 0) {
      ;
    } else if (difference <= 2)
      ++human_score;
    else
      ++agent_score;
    printf(SCORE_STATUS, human_score, agent_score);
  }

  // Announce the winner
  printf("%s won!\n", human_score > agent_score ? HUMAN_REPR : AGENT_REPR);
}

Knight’s tour#

Solution to Activity 35 (A shorter tour on a 4x4 board)

Here is a solution with 15 squares generated by ChatGPT after thinking ~40s. It is not a closed-loop, so it applies only to starts from corners.

10  7 12  3
13  4  9  6
 8 11  2 15
 1 14  5  .

Maze#

Solution to Activity 42 (Meaning of pointer operators)

  1. a == 42

  2. *a == 42

  3. &a == 42

  4. &*b == 42

  5. *&b == 42

Output of the last two expressions and repeated dereferencing:

#include <stdio.h>

int main() {
  int b = 42;
  int *a = &b;

  // printf("&*b: %d\n", &*b); ❌ Does not work, `b` must be a pointer
  printf("*&b: %d\n", *&b);

  // For the curious:
  // printf("&&b: %p\n", &(&b));❌ Does not work, `&` expects a variable (an
  // lvalue)

  // printf("&&b: %p\n", &&b);❌ Does not work, `**` gets the address of a
  // label, `b` must be a label
}
*&b: 42

Solution to Activity 41 (Pointing to a collage vs recreating it)

Advantages of 1

  1. Only one poster exists

  2. No synchronization needed

  3. You save space

  4. Each friend must be cautious when they work with the poster. They will be stressed about breaking something.

Disadvantages of 1

  1. Your friends must come to your room

  2. You have to recreate the poster for each friend

Solution to Activity 44 (Pointer arithmetic with char vs int)

If we increment a pointer, then it points to the start of the next data. The start of the next data is dependent on the size, in our case sizeof(int).

So pointer arithmetic scales automatically based on the size of the type.

Solution to Activity 45 (Calculating pointer + n)

address + index * size_of(datatype)

Warning

Following code scales twice: we have to convert the pointer to a number to make it work:

double arr[] = {100.1, 2, 3.43};

double *target_addr(double *base_addr, size_t index) {
  return base_addr + index * sizeof(double);
}

Solution to Activity 46 (Pass by address vs value using scalar values and array)

#include <stdio.h>

void set_to_42(int *n) {
  //// BEGIN SOLUTION
  *n = 42;
  //// END SOLUTION
}
void set_to_58(int n) {
  //// BEGIN SOLUTION
  n = 58;
  //// END SOLUTION
}

void set_first_element_to_58(int *arr) { // Same as int arr[]
  //// BEGIN SOLUTION
  arr[0] = 58;
  //// END SOLUTION
}

int main() {
  int n;
  int arr[10];

  // Use the first function
  //// BEGIN SOLUTION
  set_to_42(&n);
  //// END SOLUTION
  printf("n: %d\n", n);

  // Use the second function
  //// BEGIN SOLUTION
  set_to_58(n);
  //// END SOLUTION
  printf("n: %d\n", n);

  // Use the third function
  //// BEGIN SOLUTION
  set_first_element_to_58(arr);
  //// END SOLUTION
  printf("arr[0]: %d\n", arr[0]);
}

Filter CSV by age#

Solution to Activity 59 (Processing temperature data)

#include <stdio.h>
#include <stdlib.h>
#define LINE_SIZE 20
#define TEMPERATURE_MAX 19

void p1() {
#define LINES_READ 5
  auto filename = "temperatures.txt";
  auto fp = fopen("temperatures.txt", "r");
  if (!fp)
    perror("file cannot be opened.");

  char line[LINE_SIZE];

  for (auto _ = 0; _ < LINES_READ; ++_) {
    fgets(line, LINE_SIZE, fp);
    fputs(line, stdout);
    // same as
    // 1. fprintf(stdout, "%s", line);
    // 2. puts(line) => puts extra newline
  }
  fclose(fp);
}

void p2() {
  auto fp = fopen("temperatures-today.txt", "w");
  fputs("25\n", fp);
  fputs("24\n", fp);
  fputs("21\n", fp);
  fclose(fp);
}

void p3() {
  auto fp = fopen("temperatures-today.txt", "a");
  fputs("19\n", fp);
  fclose(fp);
}

void p4() {
  auto fp = fopen("measurements-done.txt", "w");
  fclose(fp);
}

void p5() {
  auto fpi = fopen("temperatures.txt", "r");
  auto fpo = fopen("temperatures-filtered-some.txt", "w");
  char line[LINE_SIZE];

  for (auto _ = 0; _ < LINES_READ; ++_) {
    fgets(line, LINE_SIZE, fpi);
    int temp = strtol(line, nullptr, 10);
    if (temp > TEMPERATURE_MAX)
      fputs(line, fpo);
    // or fscanf and then fprintf(fpo, "%i\n", temp);
  }
  fclose(fpi);
  fclose(fpo);
}

void p6() {
  auto fpi = fopen("temperatures.txt", "r");
  auto fpo = fopen("temperatures-filtered-all.txt", "w");
  char line[LINE_SIZE];

  while (fgets(line, LINE_SIZE, fpi)) {
    fgets(line, LINE_SIZE, fpi);
    int temp = strtol(line, nullptr, 10);
    if (temp > TEMPERATURE_MAX)
      fputs(line, fpo);
  }
  fclose(fpi);
  fclose(fpo);
}

int main() {
  p1();
  p2();
  p3();
  p4();
  p5();
  p6();
}

Solution to Activity 60 (An error in a file processing program)

  1. Using the call stack or iteratively placing breakpoints from bottom to the top.

    Looking at the values of variables

  2. Problem: fp is nullptr, so the FILE struct does not exist.

fopen returns a nullptr if the file cannot be opened.

perror() is useful for printing the last happened error.

Error handling code:

--- /builds/fpga-lab/c-programming/code-error/file-opening-error.c
+++ /builds/fpga-lab/c-programming/code-error/file-opening-error-with-error-check.c
@@ -1,9 +1,14 @@
 #include <stdio.h>
+#include <stdlib.h>
 #define LINE_MAX 100
 #define FILE_ "data.txt"
 
 int main() {
   auto fp = fopen(FILE_, "r");
+  if (!fp) {
+    perror(FILE_ " cannot be opened.");
+    return EXIT_FAILURE;
+  }
   char line[LINE_MAX];
   fgets(line, LINE_MAX, fp);
   puts(line);

Output:

data.txt cannot be opened.: No such file or directory

Data structures#

Solution to Activity 63 (Appropriate data structure for FrankenText)

  1. According to:

    #define MAX_WORD_COUNT 15'000
    #define MAX_SUCCESSOR_COUNT MAX_WORD_COUNT / 2
    
    char book[] = {
    #embed "pg84.txt" /// Stores the content of the file as an array of chars.
        , '\0'};      /// Makes `book` a string.
    
    /// Array of tokens registered so far.
    /// No duplicates are allowed.
    char *tokens[MAX_WORD_COUNT];
    /// `tokens`'s current size
    size_t tokens_size = 0;
    
    /// Array of successor tokens
    /// One token can have many successor tokens. `succs[x]` corresponds to
    /// `token[x]`'s successors.
    /// We store directly tokens instead of token_ids, because we will directly
    /// print them. If we wanted to delete the book, then it would make more sense
    /// to store `token_id`s
    char *succs[MAX_WORD_COUNT][MAX_SUCCESSOR_COUNT];
    /// `succs`'s current size
    size_t succs_sizes[MAX_WORD_COUNT];
    
    • book

      • file explorer: 448.9 kB

      • or can be obtained in Debug Console after starting the program in debugging mode: p sizeof(book) 448930 byte

    • tokens

      • (MAX_WORD_COUNT) 15,000 pointers = 8 byte (64 bit) * 15,000 = 120,000 byte = 120 kB

    • succs

      • 15,000 * 7,500 pointers = 900,000,000 bytes = 900 MB

    • succs_sizes

      • 15,000 * 8 = 120,000 byte = 120 kB

    In total, our program will use about 1 GB of memory. The actual memory usage can be visualized using htop and searching for the process id. Process id can be found on the Debug Console.

  2. This memory is placed mostly on the RAM. OS may put part of the memory to the virtual memory. Typically hard drive is used as virtual memory.

    If one program uses large amount of memory, then memory may become scarce. This could make the program self or other programs slower or even stop them.

  3. We could reduce MAX_WORD_COUNT and MAX_SUCCESSOR_COUNT, but especially the word the requires many successors.

    The largest memory is used by succs, which we should focus on.

    In FrankenText, we reserve fixed amount of successors for each word, but most words have probably much less successors than the. Having dynamic size for each word would decrease the memory consumption.

    The same idea could be applied also to tokens.

    Two viable options and their trade-offs:

    1. Linked list

    2. Arrays that grow in chunks, e.g., each array starts with 16 elements, and grow by 16 elements if needed.

    Linked list is good for dynamic scenarios where deletions occur, but is slow to access elements. Dynamically grown arrays are much faster, but do not support deletion in the middle of an array, which we don’t need in FrankenText.

    All in all option 2 will be probably the best performance-wise.

Solution to Activity 62 (Implementing a singly linked list)

// TODO this code is not complete. I just copy pasted from the lecture.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node {
  int data;
  struct node *next;
} Node;

struct node b, c;
struct node a = {12, &b}, b = {99, &c}, c = {37, nullptr};

struct node *head = &a;

void print_elements() {
  auto address = head;
  while (address) {
    printf("%d ", address->data);
    address = address->next;
  }
}

void add_first_node(Node **head, int val) {
  //   Node n = {43, nullptr};
  Node *node_ptr = malloc(sizeof(Node));
  node_ptr->data = val;

  *head = node_ptr;
}

void insert(Node **head, size_t index, Node *nodeptr) {
  Node *current = *head;

  // for navigating to the node where we want to insert
  for (size_t i = 0; i < index; ++i)
    current = current->next;

  // We insert the node
  nodeptr->next = current->next;
  current->next = nodeptr;
}

int main() {
  print_elements();
  Node n = {10, nullptr};
  insert(&head, 2, &n);
  ;
}

Review problems#

Solution to Activity 65 (Typical layout of a C program)

  1. included libraries, constants, global variable & function declarations (and initialization & definitions), main, [function definitions]

  2. Definition: we set the type of an identifier and allocate space for the object it represents.

    Initialization: the value stored in the storage when a program a starts.

  3. Using include we can include functionality implemented by others. Technically #include includes the content of a file, i.e., function declarations (and sometimes definitions) in case of a header file.

  4. It iterates over the elements of arr and prints each character; finally a newline at the end.

    • tmp is not meaningful

    • What is 4?

    • int => size_t, because it is used to iterate over an array

    • int i can be directly declared inside the for below

    • #include should be used on the top of the file. This is a wide convention.

  5.         flowchart LR
    s( ) --> init[i = 0] --> condition{i < array size} --> body[print array i]
    body --> condition --> n[print newline] --> e( )
        

Solution to Activity 68 (Implementable flowcharts)

  1. Contains a process that leads to two processing blocks as follows:

            flowchart LR
    s( ) --> body[ ] --> p1[ ] --> e( )
    body[ ] --> p2[ ] --> e
    
        

    Even this is possible to implement in C, it is not part of structured programming rules, which only contains (1) sequence (concatenation) (2) selection (branching), and (3) iteration (loops).

  2. Contains a decision that branches to the parent decision as follows:

            flowchart LR
    s( ) --> if1{ } --> if2{ } --> e( )
    if2 --> if1
    if1 --> e
        

    This can only be implemented with a goto

    #include <stdlib.h>
    
    int main() {
    if1:
      if (rand() % 2) {
        if (rand() % 2)
          ;
        else
          goto if1;
      }
    }
    
  3. Contains a process that has two output branches as follows:

            flowchart LR
    s( ) --> p1[ ] --> p2[ ] --> e( )
    p2 --> p1
        

    Output branches are only possible in decisions.

  4. is an if-else

  5. is an if-else-if

Solution to Activity 71 (Two dice sum distribution)

Idea for the analytical solution:

The unit occurrences for the sums:

1x 2: 11
2x 3: 12 21
3x 4: 13 22 31
..
6x 7: 16 .. 61
5x 8: 26 35 44 53 62
..
1x 12: 66

Total: 36x units

50000 / 36x should equal to the occurrence count of 2 or 12 in the long run. If we increase the the number of simulation, we will almost reach this number.

// Simulates the distribution of two dice sum
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define THROW_COUNT 50000

#define BIN_COUNT 12 - 2 + 1
/// Sum can be 2 to 12. For each of them we have a bin that counts their
/// occurrences.

/// Convert sum to bin id and vice-versa
size_t sum_to_bin(int sum) { return sum - 2; }
int bin_to_sum(size_t bin) { return bin + 2; }

unsigned dice_sum_occurrences[BIN_COUNT];
int throw_dice() { return rand() % 6 + 1; }

int main() {
  srand(time(nullptr));

  // Throw dice
  for (int i = 0; i < THROW_COUNT; ++i) {
    ++dice_sum_occurrences[sum_to_bin(throw_dice() + throw_dice())];
    asm("nop");
  }
  // Show results
  for (size_t bin = 0; bin < BIN_COUNT; ++bin)
    printf("%2d: %4d\n", bin_to_sum(bin), dice_sum_occurrences[bin]);
}

Review problems 2#

Solution to Activity 72 (Substitute words in a dictionary)

// Substitutes strings in the stdin according to a dictionary
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define LINE_SIZE 100
char line_in[LINE_SIZE], line_out[LINE_SIZE];

typedef struct {
  char *key;
  char *val; // value
} Entry;

Entry dict[] = {
    {"imho", "in my humble opinion"},
    {"ig", "Instagram"},
    {"worried", "😟"},
    {"happy", "☺️"},

};

#define KEY_NOT_FOUND SIZE_MAX

size_t key2index(char *key) {
  for (size_t i = 0; i < _Countof(dict); ++i)
    if (!strcasecmp(key, dict[i].key))
      return i;
  return KEY_NOT_FOUND;
}

void substitute_if_word_is_in_dict(char *word) {
  auto index = key2index(word);

  if (index == KEY_NOT_FOUND)
    strcat(line_out, word);
  else
    strcat(line_out, dict[index].val);
  strcat(line_out, " ");
}

int main() {
  while (fgets(line_in, LINE_SIZE, stdin)) {
    if (line_in[0] == '\n')
      continue;
    *strchr(line_in, '\n') = '\0'; // Replace newline

    auto word = strtok(line_in, " ");
    substitute_if_word_is_in_dict(word);

    while ((word = strtok(nullptr, " "))) {
      substitute_if_word_is_in_dict(word);
    }
    puts(line_out);
    line_out[0] = '\0'; // Initialize buffer for next run.
  };
}

Solution to Activity 73 (Is there a bug?)

  1. & => &&.

    & is a bitwise operation. If length is 2, 4 or even in general, then the result cannot be true.

  2. Correct. For lunch and and dinner, you eat the same

    • There is no memory reserved for line.

    • stdout => stdin.

  3. puts(a > b ? "yes!" : "no!")

  4. There is no bug, just the code style is not readable. putcs should be better in the body of the for loop.

Additional exercises#

Solution to Activity 78 (Fizz buzz)

// Finds the Fizz buzz numbers less than N
#include <stdio.h>
#define N 40
int main() {
  for (int i = 0; i < N; ++i) {
    if (!(i % 3) && !(i % 5))
      printf("Fizz buzz💚, ");
    else if (!(i % 3))
      printf("Fizz💙, ");
    else if (!(i % 5))
      printf("Buzz💛, ");
    else
      printf("%d, ", i);
  }
}

Solution to Activity 79 (Is there a bug? (2))

  1. 0-indexing, strlen should be used

  2. sizeof => strlen, "a" => 'a'