UP | HOME

Structs
COP-3223H

Table of Contents

Structs bundle variables

Instead of having to define separate variables for one player, e.g.,

int player_x;
int player_y;
char player_c;

Bundle these instead a single variable

struct {
  int x;
  int y;
  char c;
} player;

Accessing struct fields

Structs create namespaces, e.g., x can be used both in the struct and as a local variable.

struct {
  int x;
  int y;
  char c;
} player;

player.x = 2;

int x = 10;

Struct declarations create new datatypes

// datatype is int     name is x
int                    x;

//  datatype is a new struct         name is sprite
struct { int x; int y; char c; }     sprite;

Reusing struct datatypes

You can name a struct and use it for many variables.

//  the new struct type is called "sprite".  no variable name needed
struct sprite { int x; int y; char c; };


// no need to repeat the full struct declaration to use it
struct sprite player;
struct sprite enemy;

// now all variable have the same constituent variables
player.x = 1;
player.y = 10
enemy.x = 2;
enemy.y = 3;

Chase game with structs

// https://sourceware.org/glibc/manual/latest/html_mono/libc.html#Noncanon-Example

#include <stdio.h>
#include <stdbool.h>

#include "term.c"

int main(int argc, char **argv) {
  set_input_mode ();
  printf("\033[?25l"); // hide cursor

  int width = 40;
  int height = 20;


  struct character {
    int state;
    int x;
    int y;
  };

  struct character player;
  struct character enemy;

  enum { vulnerable, invincible, dead };
  player.state = vulnerable;
  player.x = width / 2;
  player.y = height / 2;

  enum { chasing, running, defeated };
  enemy.state = chasing;
  enemy.x = 1;
  enemy.y = 1;

  struct {
    struct character character;
    int counter;
    int random;
  } potion;

  enum { available, using, done };
  potion.character.state = available;
  potion.character.x = 29;
  potion.character.y = 11;
  potion.counter = 0;
  potion.random = 1;

  while (1) {
    printf("\x1b[2J");
    printf("\x1b[H");
    // 0 available
    // 1 using
    // 2 done

    // initial state is 0
    // switch (potion_state) {
    //   case 0:
    // }
    switch (potion.character.state) {
    case available:
      printf("\033[%d;%dH", potion.character.y, potion.character.x);
      printf("/\\");
      printf("\033[%d;%dH", potion.character.y + 1, potion.character.x);
      printf("\\/");
      break;
    case using:
      // intentionally empty
      break;
    }

    printf("\033[%d;%dH", enemy.y, enemy.x);
    switch (enemy.state) {
    case chasing: // case 0:
      putchar('@');
      break;
    case running: // case 1:
      putchar('@');
      break;
    case defeated: // case 2:
      putchar('X');
    }

    printf("\033[%d;%dH", player.y, player.x);
    switch (player.state) {
    case vulnerable:
      putchar('h');
      break;
    case invincible:
      putchar('H');
      break;
    case dead:
      printf(":(");
    }

    // debugging output
    printf("\033[%d;%dH", height + 1, 0);
    printf("player.y: %d\n", player.y);
    printf("player.x: %d\n", player.x);
    printf("enemy.y: %d\n", enemy.y);
    printf("enemy.x: %d\n", enemy.x);
    printf("potion.character.y: %d\n", potion.character.y);
    printf("potion.character.x: %d\n", potion.character.x);

    // read input

    int direction_x = 0;
    int direction_y = 0;
    bool valid = true;
    do {
      char c = '\0';
      scanf("%c", &c);
      switch (c) {
      case 'i':
        direction_y = -2;
        break;
      case 'k':
        direction_y = 2;
        break;
      case 'j':
        direction_x = -2;
        break;
      case 'l':
        direction_x = 2;
        break;
      default:
        valid = false;
        break;
      }
    } while (! valid);

    switch(player.state) {
    case vulnerable:   // case 0:
      player.x += direction_x;
      player.y += direction_y;
      break;
    case invincible:  // case 1:
      player.x += direction_x;
      player.y += direction_y;
      if (enemy.x - 1 <= player.x && player.x <= enemy.x + 1 &&
          enemy.y - 1 <= player.y && player.y <= enemy.y + 1) {
        player.state = vulnerable;
        enemy.state = defeated;
        potion.character.state = done;
      }
      break;
    }

    switch (enemy.state) {
    case chasing: // chasing
      if (player.x > enemy.x) {
        enemy.x += 1;
      }
      if (player.x < enemy.x) {
        enemy.x -= 1;
      }
      if (player.y > enemy.y) {
        enemy.y += 1;
      }
      if (player.y < enemy.y) {
        enemy.y -= 1;
      }

      if ((player.x == enemy.x || player.x == enemy.x + 1) &&
          (player.y == enemy.y || player.y == enemy.y + 1)) {
        player.state = dead;
      }
      break;
    case running:
      if (player.x > enemy.x) {
        enemy.x -= 1;
      }
      if (player.x < enemy.x) {
        enemy.x += 1;
      }
      if (player.y > enemy.y) {
        enemy.y -= 1;
      }
      if (player.y < enemy.y) {
        enemy.y += 1;
      }
      if (enemy.x < 0) {
        enemy.x = 0;
      }
      if (enemy.x > width) {
        enemy.x = width;
      }
      if (enemy.y < 0) {
        enemy.y = 0;
      }
      if (enemy.y > height) {
        enemy.y = height;
      }
      break;
    }

    switch (potion.character.state) {
    case available:
      if ((player.x == potion.character.x || player.x == potion.character.x + 1) &&
          (player.y == potion.character.y || player.y == potion.character.y + 1)) {
        player.state = invincible;
        enemy.state = running;
        potion.character.state = using;
        potion.counter = 10;
      }
      break;
    case using:
      potion.counter -= 1;
      if (potion.counter == 1) {
        player.state = vulnerable;
        enemy.state = chasing;
        potion.character.state = available;
        potion.random = (potion.random * 97) % 65533;
        potion.character.x = potion.random % width;
        potion.random = (potion.random * 97) % 65533;
        potion.character.y = potion.random % height;
      }
      break;
    }
  }
}

Author: Paul Gazzillo

Created: 2026-04-01 Wed 11:37

Validate