% Listing 5: view_stks.c

/* view_stks.c */
/* Andrew Davison, June 1996 (ad@cs.mu.oz.au) */

/* Usage:

   view_stks prompts for a Nim sticks configuration, of
   the form:

   rowK is the number of sticks in the Kth row. Rows
   are numbered from 0. The unusual input format is close
   to the input received by the CGI script (step_nim.c)
   for playing Nim.

   The input is used to generate a GIF in the TEST_GIF file,
   which consists of sticks in rows. The GIF for a single
   stick is assumed to be in STICK_GIF.
/* Makes use of the gd graphics library, by
   Thomas Boutell and the Quest Protein Database Center
   at Cold Spring Harbor Labs.
/* compilation: 
      \gcc -Wall view_stks.c -o view_stks -L/home/staff/ad/gd1.2 -lgd -lm

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "/home/staff/ad/gd1.2/gd.h"
#include "/home/staff/ad/gd1.2/gdfontg.h"   /* text uses a Giant font */

#define ROWS 5            /* number of rows */
#define XOFFSET 8         /* X distance between sticks */
#define YOFFSET 12        /* Y distance between sticks */
#define TEXT_START 15     /* Start of Row ?: text */
#define MAXLEN 120        /* max length of a string */
#define DIGLEN 3          /* max length of a digit string */

#define STICK_GIF "/home/staff/ad/www_public/code/nim/stick.gif"
#define TEST_GIF  "/home/staff/ad/www_public/code/nim/test.gif"

void init_sticks(int sticks[]);
void get_sticks(char ln[], int sticks[]);
int get_digit(char ln[], int *i);
void display_game(int sticks[]);

/* GIF creation */
void visualise_nim(int sticks[], char *gifnm);
void load_stick(gdImagePtr *stick);
void make_canvas(gdImagePtr *canvas, gdImagePtr stick, int max,
                              int *fst_offset);
void draw_row(gdImagePtr canvas, int fst_offset, int r, int black, 
                              int numsticks, gdImagePtr stick);
void save_canvas(gdImagePtr canvas, char *gifnm);

int main()
  int sticks[ROWS] = {0};

  visualise_nim(sticks, TEST_GIF);
  return 0;

void init_sticks(int sticks[])
  char line[MAXLEN];

  printf("\nEnter Nim state (row0-row1-...-row%d): ", ROWS-1);
  get_sticks(line, sticks);

void get_sticks(char ln[], int sticks[])
/* Read a sequence of digits, in the format:
         digit ['-'digit]*
  int i = 0, sno = 0;

  sticks[sno++] = get_digit(ln, &i);
  while (ln[i] == '-') {
    sticks[sno++] = get_digit(ln, &i);

int get_digit(char ln[], int *i)
/* Extract a digit from the line; not much error checking */
  char num[DIGLEN];
  int j = 0;

  while (isdigit(ln[*i]))
    num[j++] = ln[(*i)++];
  num[j] = '\0';

  return atoi(num);

void display_game(int sticks[])
  int r;

  printf("\nThe stick configuration is now:\n");
  for (r = 0; r < ROWS; r++)
    printf("Row %d: %d sticks\n", r, sticks[r]);

/* GIF Creation */

void visualise_nim(int sticks[], char *gifnm)
/* A white canvas is created, consisting of rows of
   sticks. The width of the canvas is based on max, the
   maximum number of sticks in the sticks array.
   The final image is stored in gifnm.
  int black;
  int r, max = 0, fst_offset;
  gdImagePtr canvas, stick;

  for (r = 0; r < ROWS; r++)
    if (max < sticks[r])
      max = sticks[r];

  make_canvas(&canvas, stick, max, &fst_offset);
  black = gdImageColorAllocate(canvas, 0, 0, 0);
  for (r = 0; r < ROWS; r++)
    draw_row(canvas, fst_offset, r, black, sticks[r], stick);

  save_canvas(canvas, gifnm);

void load_stick(gdImagePtr *stick)
/* Load the image representing a stick, which is
   assumed to be in the STICK_GIF file.
  FILE *fp;

  if ((fp = fopen(STICK_GIF, "rb")) == NULL) {
    printf("Cannot read gif image from %s\n", STICK_GIF);
  printf("Reading gif image from %s\n", STICK_GIF);
  *stick = gdImageCreateFromGif(fp);

void make_canvas(gdImagePtr *canvas, gdImagePtr stick, int max,
                              int *fst_offset)
/* Create a blank, white canvas, which is wide enough to
   hold the words Row <ROWS-1>: and max sticks. Its depth
   must be enough to contain ROWS rows.

   The offsets between sticks in the X and Y directions are
   obtained from XOFFSET and YOFFSET. TEXT_START contains 
   the offset from the left edge to the text. fst_offset 
   holds the offset from the left edge to the first stick.
  char row_title[MAXLEN];
  int xlen, ylen, white;

  sprintf(row_title, "Row %d: ", ROWS-1);
  *fst_offset = TEXT_START +
                (strlen(row_title) * gdFontGiant->w) + XOFFSET;

  xlen = *fst_offset + ((stick->sx + XOFFSET) * max);
  ylen = YOFFSET + ((stick->sy + YOFFSET) * ROWS);

  *canvas = gdImageCreate(xlen, ylen);
  /* first colour created becomes the background */
  white = gdImageColorAllocate(*canvas, 255, 255, 255);

void draw_row(gdImagePtr canvas, int fst_offset, int r, int black,
                              int numsticks, gdImagePtr stick)
/* Draw a row of sticks onto the canvas. The row number is r, 
   the number of sticks is numsticks. The text before the
   sticks is drawn in a black Giant font.
  char row_title[MAXLEN];
  int i, xposn, yposn;

  yposn = YOFFSET + ((stick->sy + YOFFSET) * r);
  sprintf(row_title, "Row %d: ", r);
  gdImageString(canvas, gdFontGiant, TEXT_START, yposn, row_title, black);

  xposn = fst_offset;          /* start of sticks on canvas */
  for (i = 0; i < numsticks; i++) {
    gdImageCopy(canvas, stick, xposn, yposn, 0, 0, stick->sx, stick->sy);
    xposn = xposn + (stick->sx + XOFFSET);
    /* debugging printf() */
    printf("r=%d; xposn=%d yposn=%d\n", r, xposn, yposn);

void save_canvas(gdImagePtr canvas, char *gifnm)
/* Save the canvas image in the GIF file gifnm */
  FILE *fp;

  if ((fp = fopen(gifnm, "wb")) == NULL) {
    printf("Cannot write gif image to %s\n", gifnm);
  printf("Writing gif image to %s\n", gifnm);
  gdImageGif(canvas, fp);