SAI || Sommersemester 1997 || Systemnahe Software II || Übungen

<- Alle Module

Lösung zu Blatt 3 (Aufgabe 3): gbdisp.h + gbdisp.c

Darstellung Gobang-ähnlicher Spiele auf dem Bildschirm.

gbdisp.h

/*
 *      gbdisp.h        - headerfile for displaying Gobang-like games
 *
 *      Martin Hasch, University of Ulm, May 1997.
 */

/*
 *      common return values:
 */
#define GB_OK           0               /* call was ok */
#define GB_DOMAIN       1               /* parameter(s) had illegal value */

/*
 *      Set board size and clear board.
 *      Must be called before any other function.
 *      If (size) is zero choose largest possible size.
 *      Return GB_OK if screen is large enough, otherwise GB_DOMAIN.
 */
int gb_reset(int size);

/*
 *      Return actual size of the board.
 */
int gb_size(void);

/*
 *      Move cursor to position (x,y).
 */
int gb_goto(int x, int y);

/*
 *      Set board at position (x,y) to color: 0 empty, 1 black, 2 white.
 */
int gb_set(int x, int y, int color);

/*
 *      Display status message s.
 */
void gb_status(char *s);

/*
 *      Restore old terminal modes.
 *      Called automatically upon program termination.
 */
void gb_cleanup(void);

/*
 *      Redraw the screen.
 */
void gb_redraw(void);

gbdisp.c

/*
 *      gbdisp.c        - displaying Gobang-like games
 *
 *      Martin Hasch, University of Ulm, May 1997.
 */

#include        <sys/types.h>
#include        <signal.h>
#include        <stdlib.h>
#include        <stdio.h>
#include        <unistd.h>
#include        <curses.h>
#include        "gbdisp.h"

#define STATUS_LEN      25              /* maximal length of status line */
#define STONE_COLORS    2               /* kinds of stones */
#define STATUS_ROW      8               /* row of status line */

static char headline[] = "*** Gobang Client 1.0 ***";
static char xletters[] = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjklmnopqrstuvwxyz";

/*
 *      Stone layout:
 *      Each stone occupies 3x2 characters, with its "center" in the lower row.
 */
static chtype stones[STONE_COLORS+1][2][4] = {
        {       { ' ', ' ', ' ', 0L },                  /* empty */
                { ' ', '.', ' ', 0L } 
        },
        {       { ' ', ' '|A_UNDERLINE, ' ', 0L},       /* black */
                { '('|A_REVERSE, '@'|A_REVERSE, ')'|A_REVERSE, 0L}
        },
        {       { ' ', ' '|A_UNDERLINE, ' ', 0L},       /* white */
                { '(', ' '|A_UNDERLINE, ')', 0L}
        },
};

#define XLETTERS_LEN    (sizeof xletters - 1)   /* maximal number of columns */

#define min(a, b)       ((a)>(b)?(b):(a))       /* minimum of two numbers */
#define max(a, b)       ((a)<(b)?(b):(a))       /* maximum of two numbers */

static int must_cleanup = 0;            /* cleanup necessary? */
static int current_size = 0;            /* board size */
static int info_column = 0;             /* leftmost column of status info */
static int cursor_line = 0;             /* expected line ... */
static int cursor_column = 0;           /* ... and column of cursor */

/*
 *      Restore old terminal modes.
 *      Called automatically upon program termination.
 */
void gb_cleanup(void)
{
        if ( must_cleanup ) {
                endwin();
                must_cleanup = 0;
        }
}

/*
 *      Call cleanup before termination signals hit.
 */
static void termination_handler(int sig)
{
        gb_cleanup();
        kill(getpid(), sig);
}

/*
 *      Initialize screen.  Arrange for cleanup at exit.
 */
static void gb_init(void)
{
        static int init_called = 0;     /* has bg_init already been called? */
        int sig;

        if ( init_called )
                return;
        initscr();
        init_called = must_cleanup = 1;
        /* catch all ordinary signals intended for controlled termination */
        for ( sig=SIGHUP; sig<=SIGTERM; ++sig )
                if ( sig != SIGKILL )
                        (void) signal(sig, termination_handler);
        if ( atexit(gb_cleanup) ) {
                gb_cleanup();
                fprintf(stderr, "atexit failed\n");
                exit(EXIT_FAILURE);
        }
        (void) cbreak();                        /* no line buffering */
        (void) noecho();                        /* no input echo */
        (void) keypad(stdscr, TRUE);            /* recognize function keys */
        (void) leaveok(stdscr, TRUE);           /* hide normal cursor */
        /*
         *      Replace dot by bullet (if feasible).  Since ACS_BULLET is
         *      not a constant it cannot be part of an initializer.
         */
        if ( ACS_BULLET != 'o' )
                stones[0][1][1] = ACS_BULLET;
}

/*
 *      Map board to screen coordinates.
 *      Return GB_OK if correct, otherwise keep *line and *columns untouched
 *      and return GB_DOMAIN.
 */
static int coordinates(int x, int y, int *line, int *column)
{
        if ( 1 <= x && x <= current_size && 1 <= y && y <= current_size ) {
                *line = (current_size+1-y) << 1;
                *column = (x << 2) + 1;
                return GB_OK;
        }
        return GB_DOMAIN;
}

/*
 *      Move highlight to some board position.  (line) == 0 turns it off.
 */
static void highlight(int line, int column)
{
        static int hi_line = 0;         /* current line of highlight */
        static int hi_column = 0;       /* current column of highlight */

        if ( hi_line ) {
                mvaddch(hi_line, hi_column-2, ' ');
                mvaddch(hi_line, hi_column+2, ' ');
        }
        hi_line = line, hi_column = column;
        if ( hi_line ) {
                mvaddch(hi_line, hi_column-2, '>');
                mvaddch(hi_line, hi_column+2, '<');
        }
        (void) move(hi_line, hi_column);
}

/*
 *      Draw one stone of color (color) with center at (line, column).
 */
static void put_stone(int line, int column, int color)
{
        mvaddchstr(line-1, column-1, stones[color][0]);
        mvaddchstr(line, column-1, stones[color][1]);
}

/*
 *      Draw complete screen layout with empty board of given size.
 *      (infocol) determines leftmost column of additional information.
 *      Does not call refresh nor care for highlight position.
 */
static void draw_board(int size, int infocol)
{
        int i, j;

        (void) erase();
        (void) move(0, 3);
        (void) hline(' '|A_UNDERLINE, (size << 2)+1);
        (void) move(1, 2);
        (void) vline(ACS_VLINE, (size << 1)+1);
        (void) move(1, (size << 2)+4);
        (void) vline(ACS_VLINE, (size << 1)+1);
        (void) move((size << 1)+1, 3);
        (void) hline(' '|A_UNDERLINE, (size << 2)+1);
        for ( i=0; i<size; ++i ) {
                mvaddch(0, (i << 2)+5, xletters[i]|A_UNDERLINE);
        }
        if ( size+size+2 < LINES ) {
                for ( i=0; i<size; ++i )
                        mvaddch(size+size+2, (i << 2)+5, xletters[i]);
        }
        for ( j=0; j<size; ++j ) {
                (void) mvprintw(j+j+2, 0, "%2d", size-j);
                (void) mvprintw(j+j+2, (size<<2)+5, "%2d", size-j);
                for ( i=0; i<size; ++i ) {
                        put_stone(j+j+2, (i << 2)+5, 0);
                }
        }
        (void) standout();
        (void) mvprintw(0, infocol, "%s", headline);
        (void) standend();
        (void) mvprintw(2, infocol, "Black: ");
        put_stone(2, infocol+8, 1);
        (void) mvprintw(4, infocol, "White: ");
        put_stone(4, infocol+8, 2);
}

/*
 *      Move cursor to position (x,y).
 */
int gb_goto(int x, int y)
{
        int result;

        result = coordinates(x, y, &cursor_line, &cursor_column);
        if ( result == GB_OK ) {
                mvprintw(6, info_column, "Field: %c%2d", xletters[x-1], y);
                highlight(cursor_line, cursor_column);
        }
        (void) refresh();
        must_cleanup = 1;
        return result;
}

/*
 *      Set board at position (x,y) to color: 0 empty, 1 black, 2 white.
 */
int gb_set(int x, int y, int color)
{
        int result;

        if ( 0 <= color && color <= STONE_COLORS ) {
                result = coordinates(x, y, &cursor_line, &cursor_column);
                if ( result == GB_OK ) {
                        mvprintw(6, info_column, "Field: %c%2d",
                                xletters[x-1], y);
                        put_stone(cursor_line, cursor_column, color);
                        highlight(cursor_line, cursor_column);
                }
        } else
                result = GB_DOMAIN;
        (void) refresh();
        must_cleanup = 1;
        return result;
}

/*
 *      Display status message s.
 */
void gb_status(char *s)
{
        int i;

        (void) move(STATUS_ROW, COLS-1-STATUS_LEN);
        for ( i=0; i<STATUS_LEN; ++i ) {
                if ( *s ) {
                        (void) addch(*s);
                        ++s;
                } else
                        (void) addch(' ');
        }
        (void) move(cursor_line, cursor_column);
        (void) refresh();
        must_cleanup = 1;
}

/*
 *      Return actual size of the board.
 */
int gb_size(void)
{
        return current_size;
}

/*
 *      Redraw the screen.
 */
void gb_redraw(void)
{
        (void) clearok(stdscr, TRUE);
        (void) refresh();
        must_cleanup = 1;
}

/*
 *      Set board size and clear board.
 *      Must be called before any other function.
 *      If (size) is zero or less choose largest possible size.
 *      Return GB_OK if screen is large enough, otherwise GB_DOMAIN.
 */
int gb_reset(int size)
{
        int need_lines, need_cols;

        if ( size > XLETTERS_LEN ) {
                fprintf(stderr, "size should be <= %d\n", XLETTERS_LEN);
                return GB_DOMAIN;
        }
        need_cols = (max(1, size) << 2) + STATUS_LEN+10;
        need_lines = (max(1, size) << 1) + 2;
        need_lines = max(need_lines, STATUS_ROW+1);

        gb_init();
        if ( need_lines > LINES || need_cols > COLS ) {
                gb_cleanup();
                fprintf(stderr, "lines: %d, cols: %d; needed: %d, %d\n",
                        LINES, COLS, need_lines, need_cols);
                return GB_DOMAIN;
        }
        info_column = COLS-1-STATUS_LEN;

        if ( size <= 0 ) {
                current_size = (info_column-9) >> 2;
                current_size = min(current_size, (LINES-2) >> 1);
                current_size = min(current_size, XLETTERS_LEN);
        } else
                current_size = size;

        draw_board(current_size, info_column);
        (void) gb_goto((current_size+1) >> 1, (current_size+1) >> 1);

        return GB_OK;
}
<- Alle Module
SAI || Sommersemester 1997 || Systemnahe Software II || Übungen

Martin Hasch, Mai 1997