SAI ||
Sommersemester 1997 ||
Systemnahe Software II ||
Übungen
<- Alle Module
Lösung zu Blatt 5 (Aufgabe 6): gbdisp.h + gbdisp.c
Aus Aufgabe 3:
Darstellung Gobang-ähnlicher Spiele auf dem Bildschirm.
/*
* 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);
/*
* Display whose turn it is: 0 no display, 1 black, 2 white.
*/
void gb_turn(int color);
/*
* Restore old terminal modes.
* Called automatically upon program termination.
*/
void gb_cleanup(void);
/*
* Redraw the screen.
*/
void gb_redraw(void);
/*
* 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 gb_init already been called? */
int sig;
void (* sigaction)(int);
if ( init_called )
return;
initscr();
init_called = must_cleanup = 1;
/*
* catch all ordinary signals leading to termination;
* keep any other signal reactions as they were.
*/
for ( sig=SIGHUP; sig<=SIGTERM; ++sig )
if ( sig != SIGKILL && (sigaction =
signal(sig, termination_handler)) != SIG_DFL )
(void) signal(sig, sigaction);
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;
if ( !x && !y ) {
result = GB_OK;
highlight(0, 0);
} else {
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;
}
/*
* Display whose turn it is: 0 no display, 1 black, 2 white.
*/
void gb_turn(int color)
{
if ( color == 1 )
mvaddch(2, info_column+12, ACS_LARROW);
else
mvaddch(2, info_column+12, ' ');
if ( color == 2 )
mvaddch(4, info_column+12, ACS_LARROW);
else
mvaddch(4, info_column+12, ' ');
(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, Juni 1997