Bonus Videos

Bugs!

Throughout the "C Programming" video series, I've been careful to model good programming behaviors. I've stayed inside array boundaries, freed memory after allocating it, and so on. But what happens if you don't follow those programming rules? Let's find out!

Indenting

Throughout the "C Programming" video series, I've used a particular code indenting style. I wanted to do a quick video about the different ways you might see code indented.

Graphics Mode

Throughout the video series, I've been in text mode. Either I'm writing programs that scroll text from the bottom of the screen, or I'm using conio to display text directly to different areas of the screen. Some of you have asked how to do graphics in C, so here's a short video showing how to draw the rectangle graphics primitive in OpenWatcom.

Code Review

In 1994, as a novice computer programmer, I wrote the first utilities for FreeDOS. My coding style has changed a lot since then, but it's interesting to go back and look at that code I wrote in 1994 and see what works well and what I could probably change if I wrote it again.

Spoken Languages

It's really annoying when you try to understand something and you don't know the language. That's what it's like when you write your program in English and someone from Spain or Germany tries to use it, but they don't speak English. Fortunately, we have the Kitten library to support different spoken languages.

Before You Code

As I wrap up the "C Programming" video series, I wanted to share some thoughts about the important time before you code. Let's look at a game I'm writing, the Royal Game of Ur. This presents some interesting challenges in how I display the squares, but it's a lot easier if I order the board to make the math easy.

Writing a Solitaire Game

Someone asked me about how to write a Solitaire game, so let's explore how you could write Solitaire on DOS. This doesn't cover the tricky parts like card selection, but this should help get you started. Explores how to shuffle a deck (simple shuffle) and how to display card suit colors.

Sample code:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <graph.h>

typedef struct {
    int value;
    int suit;
} card_t;

int
is_black(int suit)
{
    /*
        hearts   0  00
        diamonds 1  01
        clubs    2  10
        spades   3  11
    */

    return (suit & 2);
}

void
init_deck(card_t *deck)
{
    int card;
    int s, v;

    /* build the deck */

    card = 0;
    for (s = 0; s < 4; s++) {
        for (v = 0; v < 13; v++) {
            deck[card].value = v;
            deck[card].suit = s;
            card++;
        }
    }
}

void
shuffle_card(card_t *deck, int i, int j)
{
    card_t swapval;

    swapval = deck[i];
    deck[i] = deck[j];
    deck[j] = swapval;
}

void
shuffle_deck(card_t *deck)
{
    int card, rand_card;

    /* init the rand num gen */

    srand( time(NULL) );

    /* walk through each card and exchange it with another random card */

    for (card = 0; card < 52; card++) {
        rand_card = (int) rand() % 52;
        shuffle_card(deck, card, rand_card);
    }
}

void
show_deck(card_t *deck, const char *label)
{
    int card;
    char tmpstr[4]; /* "%c%c \0" */
    char cardval[] = "A23456789XJQK";

    /* iterate through the deck and display each card */

    _settextcolor(15); /* br white */
    _outtext(label);

    for (card = 0; card < 52; card++) {
        if (is_black(deck[card].suit)) {
            _settextcolor(7); /* white */
        }
        else {
            _settextcolor(4); /* red */
        }

        sprintf(tmpstr, "%c%c ", cardval[ deck[card].value ], deck[card].suit + 3);
        _outtext(tmpstr);
    }
}

int
main()
{
    card_t deck[52];

    /* initalize values in the deck */

    init_deck(deck);

    /* print out the deck */

    show_deck(deck, "Before shuffing: ");

    /* shuffle the deck */

    shuffle_deck(deck);

    /* print the deck */

    show_deck(deck, "\nAfter shuffling: ");

    return 0;
}

I should note that the card-shuffling algorithm isn't the best. Whenever you rely on modulo to pick a random number in a range, you can have unfairness in the edge cases. But for shuffling cards in a Solitaire game, this is good enough.

A better card-shuffling algorithm would assign a random number to each card in the deck, then sort the cards so the random numbers were in order. That provides a more fair card shuffle. Refer to Part 7½. Advanced Programming to learn about qsort() to sort arrays.

See also the Fisher-Yates Shuffle described in Wikipedia. It's a simpler shuffle that doesn't require using sorting.

Displaying Control Characters

In my GW-BASIC video, I showed how GW-BASIC "tokenizes" the BASIC code to save space. For example, GW-BASIC can save four bytes if it replaces the common programming instruction PRINT (5 characters = 5 bytes) with a single character. Also in that video, I wrote my own variant of the TYPE command that would display nonprinting control characters (CC's) as a numeric code in brackets.

Here's the CCTYPE program:

#include <stdio.h>

void
showctrlchars(FILE *pfile)
{
    int ch;

    /* loop through the file one ch at a time */

    while ((ch = fgetc(pfile)) != EOF) {
        /* ASCII reserves 0-31 as ctrl codes */
        if (ch == '\n') {
            /* print newline */
            putchar(ch);
        }
        else if (ch < 32) {
            /* print the ctrl code */
            printf("<%d>", ch);
        }
        else {
            /* print the character */
            putchar(ch);
        }
    }
}

int
main(int argc, char **argv)
{
    FILE *pfile;
    int iter;

    /* scan each arg as a file */

    for (iter = 1; iter < argc; iter++) {
        pfile = fopen(argv[iter], "r");

        if (pfile) {
            showctrlchars(pfile);
            fclose(pfile);
        }
    }

    /* if no args, then read stdin */

    if (argc == 1) {
        showctrlchars(stdin);
    }

    /* done */

    return 0;
}

The showctrlchars() function uses a very simple method to read the file: it reads a single character at a time using fgetc(), parses it, then displays each character one at a time. This is not very efficient for large files on slow media, such as a floppy disk or over a network. But part of programming is realizing how the program will be used and in this case, I knew I would run the program once to display control characters.

A more efficient method would be to read the file into memory buffers, and process the buffer. When writing a program that others will use, you should do the extra work to utilize buffers instead. See Part 6. Files in the programming guide for how to use buffers.

Guess the Number Game

Over on OpenSource.com, I started an article series about writing a "guess the number" game in different programming languages. In this simple math game, the computer picks a random number between 1 and 100, and you have to guess the number. At each guess, the computer only tells you if your guess is too low or too high, or when you've guess correctly. This game exercises several key concepts in programming:

Sample code:

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

int
main()
{
    int randnum; /* between 1 and 100 */
    int guess; /* the user's guess */

    /* pick a random number */

    srand( time(NULL) );
    randnum = (rand() % 100) + 1;

    /* guess the random number */

    puts("Pick a number between 1 and 100");

    do {
        scanf("%d", &guess);

        /* give feedback */

        if (guess < randnum) {
            puts("Too low");
        }
        else if (guess > randnum) {
            puts("Too high");
        }
    } while (guess != randnum);

    puts("That's right!");

    return 0;
}

If you were to write this program on Linux, you'd probably pick random numbers using getrandom() instead. This fills a variable with random bits, using the kernel's random generator. So you don't need to initialize or "seed" the random generator when using that method.

But FreeDOS doesn't support a kernel-based or system-based random generator, so you need to use the standard C library functions instead.

Gem-matching Game

I wanted to write a gem-matching game for a while now. I figured I'd start writing a demo version of such a game, and record an ASMR video at the same time. So if you like the sound of clicky keys, or just want to watch someone code a gem-matching game, I hope you enjoy this video:

Sample code:

/* okay, it looks like the game is working now! I'll leave this here and post
the code for you to look at. */

/* This is an incomplete game. So far, it just draws gems on the screen and
finds matches. But if you want to write a simple game to match gems, this is
a good starting point. :-) */

/* and I hope you enjoyed the ASMR ... the soothing sounds of a clicky
keyboard as I wrote a program. :-) */

/* and now, thanks to everyone who supports me on Patreon. You really do make
this channel happen. Some of you are sponsoring me at a higher level and I
want to thank you especially for that. */

/* Visit our website, join us on Facebook, and follow us on Twitter */

/* And for more great content, consider supporting me on Patreon */
/* Thank you!! */



#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include <conio.h>
#include <graph.h> /* graphics */

#define BOARD_SIZE 11
#define NUM_GEMS 7

#define SCR_RES 480
#define SQ_SIZE (SCR_RES/BOARD_SIZE)

/* I need a flag to indicate if a gem has been matched. Thinking about this,
    I could create a separate array to track matches, but I already have
    a char array. But each char is only a value between 0...6. I can use
    a bit that's further to the left to track "matched gems." */

#define MATCH_FLAG 1<<7 /* 10000000 */
#define GEM_MASK   15   /* 00001111 */


void
show_board(char **board)
{
    int row, col;

    /* draw the board to the graphics screen */

    /* create an empty board */

    _setcolor(8); /* bright black .. same as a dim gray */
    _rectangle(_GFILLINTERIOR, 0,0, 480,480);

    /* not sure how big to make my gems, but we'll figure it out */

    for (row = 0; row < BOARD_SIZE; row++) {
	for (col = 0; col < BOARD_SIZE; col++) {
	    /* board[row][col] contains an integer that's 0...6, so that's
		color 1...7 */
	    _setcolor(board[row][col] + 1);

	    _ellipse(_GFILLINTERIOR, SQ_SIZE*col, SQ_SIZE*row,
		SQ_SIZE*col + SQ_SIZE, SQ_SIZE*row + SQ_SIZE);
	    /* that creates a circle starting at the upper left of the
		"square" it is in, going to the lower right of the sq. */
	}
    }
    /* that should draw the board */
}

int
find_matches(char **board)
{
    int row, col;
    int matches; /* number of matches I found */

    /* I like functions to return a value if I can. In this case, we're
	finding matches, so the function should return a count of the
	matches it's found. */

    /* look for horizontal matches. To do that, we'll search for each row
	starting at column 1, going to column BOARD_SIZE-1, and look for
	matches on either side of the gem. */

    /* that's why we need the mask and the flag. We only need to check
	if the gems are the same (after masking) and if so, we set each
	of the three gems to have the "Matched" flag. */

    for (row = 0; row < BOARD_SIZE; row++) {
	for (col = 1; col < (BOARD_SIZE-1); col++) {
	    /* since we're only looking at cols=1...BOARD_SIZE-1, we can
		safely look at array values on either side, and know we
		won't go outside the array bounds. */
	    if ( ( (board[row][col-1] & GEM_MASK) == (board[row][col] & GEM_MASK) ) &&
		( (board[row][col+1] & GEM_MASK) == (board[row][col] & GEM_MASK) ) ) {
		/* all three gems match .. mark them with flags */
		board[row][col-1] = board[row][col-1] | MATCH_FLAG;
		board[row][col+1] = board[row][col+1] | MATCH_FLAG;
		board[row][col] = board[row][col] | MATCH_FLAG;

		/* I was going to add something here to increment the number
		    of matches found, but I think that's better for the end
		    and count everything on the board at once. so I guess
		    this isn't really counting "Number of matches" but
		    really "Number of matched gems." */
	    }
	}
    }

    /* find vertical matches */

    for (row = 1; row < (BOARD_SIZE-1); row++) {
	for (col = 0; col < BOARD_SIZE; col++) {
	    if ( ( (board[row-1][col] & GEM_MASK) == (board[row][col] & GEM_MASK) ) &&
		( (board[row+1][col] & GEM_MASK) == (board[row][col] & GEM_MASK) ) ) {
		/* all three gems match .. mark them with flags */
		board[row-1][col] = board[row-1][col] | MATCH_FLAG;
		board[row+1][col] = board[row+1][col] | MATCH_FLAG;
		board[row][col] = board[row][col] | MATCH_FLAG;
	    }
	}
    }

    /* count the total number of "matched gems" that are on the board */

    matches = 0;

    _setcolor(14); /* bright yellow */

    for (row = 0; row < BOARD_SIZE; row++) {
	for (col = 0; col < BOARD_SIZE; col++) {
	    if (board[row][col] & MATCH_FLAG) {
		matches++;

		/* let's add it here .. at this point in the code, we have
		    already found a match. let's mark the square so we can
		    see it */

		/* this just adds a bright yellow border around the gem
		    that we matched. then I can see the color too. */

		_ellipse(_GBORDER, SQ_SIZE*col, SQ_SIZE*row,
		    SQ_SIZE*col + SQ_SIZE, SQ_SIZE*row + SQ_SIZE);
	    }
	}
    }

    return matches;
}

int
main()
{
    char **board; /* gem board */
    int row;
    int col;
    int matches;

    /* let's allocate memory */

    /* how big do I want this? Let's define the board in terms of the
	maximum size of the screen resolution .. 640x480 */

    board = (char **) malloc(BOARD_SIZE * sizeof(char *));

    if (board == NULL) {
	/* failed to allocate memory */
	fputs("Not enough memory", stderr);
	return 1;
    }

    for (row = 0; row < BOARD_SIZE; row++) {
	board[row] = (char *) malloc(BOARD_SIZE * sizeof(char));

	if (board[row] == NULL) {
	    /* failed to allocate memory */
	    fputs("Not enough memory to allocate row", stderr);
	    return 2;
	}
    }

    /* So that just allocated a square board of 11 rows and 11 columns */

    /* Generate random data in the board */

    srand(time(NULL));

    for (row = 0; row < BOARD_SIZE; row++) {
	for (col = 0; col < BOARD_SIZE; col++) {
	    /* generate random numbers to put in the array position */
	    board[row][col] = rand() % NUM_GEMS;
	}
    }

    /* play game */
    /* set the video mode to 640x480, show the board. then wait for
	the user to press a key before you reset the video mode back. */

    _setvideomode(_VRES16COLOR);

    show_board(board); /* assume BOARD_SIZE is known */

    getch();
    matches = find_matches(board); /* assume BOARD_SIZE is known */

    getch();
    _setvideomode(_DEFAULTMODE);

    /* free memory before exiting */

    for (row = 0; row < BOARD_SIZE; row++) {
	free(board[row]);
    }

    free(board);

    /* done */
    /* let's print the number of matched gems so I know this worked */

    printf("matches found: %d\n", matches);
    return 0;
}

Feel free to use this as a starting point for your own gem-matching game!

Graphics Programming

You don't have to program everything in character mode. DOS supports graphics mode, too! If you want to get started with programming in graphics mode using OpenWatcom, start with this sample program to generate a "color bar" test pattern:

#include <stdio.h>
#include <graph.h>
#include <conio.h>

#define XRES 640
#define YRES 480

int
isodd(int n)
{
  return (n & 1);
}

int
main()
{
  int color;

  _setvideomode(_VRES16COLOR);

  for (color = 0; color < 8; color++) {
    _setcolor(color);
    _rectangle(_GFILLINTERIOR, (XRES / 8) * color, 0, (XRES - 1),
               (YRES - 1));

    _setcolor(color + 8);              /* bright color */
    _rectangle(_GFILLINTERIOR, (XRES / 8) * color, (YRES / 2), (XRES - 1),
               (YRES - 1));

    _setcolor((isodd(color) ? 0 : 15)); /* black or br white */
    _rectangle(_GFILLINTERIOR, (XRES / 8) * color, (YRES / 2) - 20,
               (XRES - 1), (YRES / 2) + 20);
  }

  getch();
  _setvideomode(_DEFAULTMODE);

  puts("Ok");
  return 0;
}

Ready to take things to the next level? Use the base functions for rectangles and ellipses to draw a board game, such as the Simple Senet board game, in graphics mode:

#include <stdio.h>
#include <conio.h>
#include <graph.h>

#define XRES 640
#define YRES 480

#define SIZE 64

int
isodd(int n)
{
  return (n & 1);                      /* 1, 3, 5, ... */
}

int
main()
{
  int sq;

  _setvideomode(_VRES16COLOR);         /* 640x480 @ 16 colors */

  _setcolor(2);                        /* green */
  _rectangle(_GFILLINTERIOR, 0, 0, XRES - 1, YRES - 1);

  _setcolor(8);                        /* br black */
  _rectangle(_GFILLINTERIOR, 0, 0, XRES - 1, SIZE * 3);

  /* draw the squares */

  _setcolor(7);                        /* gray/white */

  for (sq = 0; sq < 10; sq++) {
    if (isodd(sq)) {
      /* 1st and 3rd rows */
      _rectangle(_GFILLINTERIOR, SIZE * sq, 0, SIZE * (sq + 1), SIZE);  /* 1st row */
      _rectangle(_GFILLINTERIOR, SIZE * sq, SIZE * 2, SIZE * (sq + 1), SIZE * 3);       /* 3rd row */
    }
    else {
      /* 2nd row */
      _rectangle(_GFILLINTERIOR, SIZE * sq, SIZE, SIZE * (sq + 1), SIZE * 2);
    }
  }

  /* draw pieces */

  for (sq = 0; sq < 10; sq++) {
    if (isodd(sq)) {
      _setcolor(1);                    /* blue */
    }
    else {
      _setcolor(4);                    /* red */
    }
    _ellipse(_GFILLINTERIOR, (SIZE * sq) + 10, 10,
             (SIZE * (sq + 1)) - 10, SIZE - 10);
  }

  /* highlight the first square */

  _setcolor(14);                       /* br yellow */
  _rectangle(_GBORDER, 0, 0, SIZE, SIZE);       /* first square */

  /* done */

  getch();
  _setvideomode(_DEFAULTMODE);

  puts("Ok");
  return 0;
}