[net.sources] Battleship source for Unix w/curses

holloway@drivax.UUCP (Bruce Holloway) (03/07/86)

I recently posted a version of a Battleship game for CP/M-68K. I really
meant to post the source for the Unix w/curses version. Here it is now.

/*----------cut here---------- ----------and here---------- */

#include "stdio.h"
#include "curses.h"
#include "signal.h"

#define	OTHER	1-turn

char numbers[] = "   0  1  2  3  4  5  6  7  8  9";

char carrier[] = "Aircraft Carrier";
char battle[] = "Battleship";
char sub[] = "Submarine";
char destroy[] = "Destroyer";
char ptboat[] = "PT Boat";

char name[] = "Bruce";

struct _ships {
    char *name;
    char symbol;
    char length;
    char start;		/* Coordinates - 0,0=0; 10,10=100. */
    char dir;		/* Direction - 0 = right; 1 = down. */
    char hits;		/* How many times has this ship been hit? (-1==sunk) */
    };

struct _ships plyship[] = {
    { carrier,'A',5,0,0,0 },
    { battle,'B',4,0,0,0 },
    { destroy,'D',3,0,0,0 },
    { sub,'S',3,0,0,0 },
    { ptboat,'P',2,0,0,0 },
};

struct _ships cpuship[] = {
    { carrier,'A',5,0,0,0 },
    { battle,'B',4,0,0,0 },
    { destroy,'D',3,0,0,0 },
    { sub,'S',3,0,0,0 },
    { ptboat,'P',2,0,0,0 },
};

char hits[2][100], board[2][100];	/* "Hits" board, and main board. */

int srchstep;
int cpuhits;
int cstart, cdir;
int plywon=0, cpuwon=0;			/* How many games has each won? */
int turn;				/* 0=player, 1=computer */

main(){
    intro();
    do{
	initgame();
	while(awinna() == -1){
	    while((turn) ? cputurn() : plyturn());
	    turn = OTHER;
	    }
	} while(playagain());
    uninitgame();
}

#define	PR	addstr

intro(){
    int uninitgame();

    signal(SIGINT,uninitgame);
    if(signal(SIGQUIT,SIG_IGN) != SIG_IGN) signal(SIGQUIT,uninitgame);

    initscr();
    saveterm();
    nonl(); cbreak(); noecho();
    clear();
    mvaddstr(4,29,"Welcome to Battleship!");
    move(8,0);
PR("                                                  \\\n");
PR("                           \\                     \\ \\\n");
PR("                          \\ \\                   \\ \\ \\_____________\n");
PR("                         \\ \\ \\_____________      \\ \\/            |\n");
PR("                          \\ \\/             \\      \\/             |\n");
PR("                           \\/               \\_____/              |__\n");
PR("           ________________/                                       |\n");
PR("           \\  S.S. Penguin                                         |\n");
PR("            \\                                                     /\n");
PR("             \\___________________________________________________/\n");
    mvaddstr(20,27,"Hit any key to continue..."); refresh();
    getch();
}

initgame(){
    int i;

    clear();
    mvaddstr(0,35,"BATTLESHIP");
    mvaddstr(4,12,"Main Board");
    mvaddstr(6,0,numbers);
    move(7,0);
    for(i=0; i<10; ++i){
	printw("%c  .  .  .  .  .  .  .  .  .  .  %c\n",i+'A',i+'A');
	}
    mvaddstr(17,0,numbers);
    mvaddstr(4,55,"Hit/Miss Board");
    mvaddstr(6,45,numbers);
    for(i=0; i<10; ++i){
	mvprintw(7+i,45,"%c  .  .  .  .  .  .  .  .  .  .  %c",i+'A',i+'A');
	}
    mvaddstr(17,45,numbers);
    for(turn=0; turn<2; ++turn)
	for(i=0; i<100; ++i){
	    hits[turn][i] = board[turn][i] = 0;
	    }
    for(turn=0; turn<2; ++turn){
	for(i=0; i<5; ++i)
	    if(!turn) plyplace(&plyship[i]);
	    else cpuplace(&cpuship[i]);
	}
    turn = rnd(2);
    cstart = cdir = -1;
    cpuhits = 0;
    srchstep = 3;
}

rnd(n)
int n;
{
    return(((rand() & 0x7FFF) % n));
}

plyplace(ss)
struct _ships *ss;
{
    int c, d;

    do{
	prompt();
	printw("Place your %s (ex.%c%d) ? ",ss->name,rnd(10)+'A',rnd(10));
	c = getcoord();
	d = getdir();
	} while(!checkplace(ss,c,d));
    placeship(ss,c,d);
}

getdir(){
    int d;

    prompt(); addstr("What direction (0=right, 1=down) ? ");
    return(sgetc("01")-'0');
}

placeship(ss,c,d)
struct _ships *ss;
int c, d;
{
    int x, y, l, i;

    for(l=0; l<ss->length; ++l){
	i = c + l * ((d) ? 10 : 1);
	board[turn][i] = ss->symbol;
	x = (i % 10) * 3 + 3;
	y = (i / 10) + 7;
	if(!turn) mvaddch(y,x,ss->symbol);
	}
    ss->start = c;
    ss->dir = d;
    ss->hits = 0;
}

checkplace(ss,c,d)
struct _ships *ss;
int c, d;
{
    int x, y, l;

    x = c%10; y = c/10;
    if(((x+ss->length) > 10 && !d) || ((y+ss->length) > 10 && d==1)){
	if(!turn)
	    switch(rnd(3)){
		case 0:
		    error("Ship is hanging from the edge of the world");
		    break;
		case 1:
		    error("Try fitting it on the board");
		    break;
		case 2:
		    error("Figure I won't find it if you put it there?");
		    break;
		}
	return(0);
	}
    for(l=0; l<ss->length; ++l){
	x = c + l * ((d) ? 10 : 1);
	if(board[turn][x]){
	    if(!turn)
		switch(rnd(3)){
		    case 0:
			error("There's already a ship there");
			break;
		    case 1:
			error("Collision alert! Aaaaaagh!");
			break;
		    case 2:
			error("Er, Admiral, what about the other ship?");
			break;
		    }
	    return(0);
	    }
	}
    return(1);
}

error(s)
char *s;
{
    prompt(); beep();
    printw("%s -- hit any key to continue --",s);
    refresh();
    getch();
}

prompt(){
    move(22,0); clrtoeol();
}

toupper(ch)
int ch;
{
    return((ch >= 'a' && ch <= 'z') ? ch-'a'+'A' : ch);
}

getcoord(){
    int ch, x, y, oldx, oldy;

redo:
    y = sgetc("ABCDEFGHIJ");
    do{
	ch = getch();
	if(ch == 0x7F || ch == 8){
	    mvcur(&oldy,&oldx,0,0);
	    mvaddch(oldy,oldx-1,' ');
	    move(oldy,oldx-1); refresh();
	    goto redo;
	    }
	} while(ch < '0' || ch > '9');
    addch(x=ch); refresh();
    return((y-'A')*10+x-'0');
}

cpuplace(ss)
struct _ships *ss;
{
    int c, d;

    do{
	c = rnd(100); d = rnd(2);
	} while(!checkplace(ss,c,d));
    placeship(ss,c,d);
}

awinna(){
    int i, j;
    struct _ships *ss;

    for(i=0; i<2; ++i){
	ss = (i) ? cpuship : plyship;
	for(j=0; j<5; ++j, ++ss)
	    if(ss->length != ss->hits)
		break;
	if(j == 5) return(OTHER);
	}
    return(-1);
}

plyturn(){
    int c, res, i;
    char *m;

    prompt();
    addstr("Where do you want to shoot? ");
    c = getcoord();
    if(!(res = hits[turn][c])){
	hits[turn][c] = res = (board[OTHER][c]) ? 'H' : 'M';
	mvaddch(7+c/10,48+3*(c%10),(res=='H') ? 'H' : 'o');
	if(c = hitship(c)){
	    prompt();
	    switch(rnd(3)){
		case 0:
		    m = "You sank my %s!";
		    break;
		case 1:
		    m = "I have this sinking feeling about my %s....";
		    break;
		case 2:
		    m = "Have some mercy for my %s!";
		    break;
		}
	    beep(); mvprintw(23,0,m,cpuship[c-1].name); refresh();
	    return(awinna() == -1);
	    }
	}
    prompt();
    move(23,0); clrtoeol();
    printw("You %s.",(res=='M')?"missed":"scored a hit"); refresh();
    return(res == 'H');
}

hitship(c)
int c;
{
    struct _ships *ss;
    int sym, i, j;

    ss = (turn) ? plyship : cpuship;
    if(!(sym = board[OTHER][c])) return(0);
    for(i=0; i<5; ++i, ++ss)
	if(ss->symbol == sym){
	    j = ss->hits; ++j; ss->hits = j;
	    if(j == ss->length) return(i+1);
	    return(0);
	    }
}

cputurn(){
    int c, res, x, y, i, d;

redo:
    if(cstart == -1){
	if(cpuhits){
	    for(i=0, c=rnd(100); i<100; ++i, c = (c+1) % 100)
		if(hits[turn][c] == 'H')
		    break;
	    if(i != 100){
		cstart = c;
		cdir = -1;
		goto fndir;
		}
	    }
	do{
	    i = 0;
	    do{
		while(hits[turn][c=rnd(100)]);
		x = c % 10; y = c / 10;
		if(++i == 1000) break;
		} while((x % srchstep) != (y % srchstep));
	    if(i == 1000) --srchstep;
	    } while(i == 1000);
	}
    else if(cdir == -1){
fndir:	for(i=0, d=rnd(4); i++ < 4; d = (d+1) % 4){
	    x = cstart%10; y = cstart/10;
	    switch(d){
		case 0: ++x; break;
		case 1: ++y; break;
		case 2: --x; break;
		case 3: --y; break;
		}
	    if(x<0 || x>9 || y<0 || y>9) continue;
	    if(hits[turn][c=y*10+x]) continue;
	    cdir = -2;
	    break;
	    }
	if(i == 4){
	    cstart = -1;
	    goto redo;
	    }
	}
    else{
	x = cstart%10; y = cstart/10;
	switch(cdir){
	    case 0: ++x; break;
	    case 1: ++y; break;
	    case 2: --x; break;
	    case 3: --y; break;
	    }
	if(x<0 || x>9 || y<0 || y>9 || hits[turn][y*10+x]){
	    cdir = (cdir+2) % 4;
	    for(;;){
		switch(cdir){
		    case 0: ++x; break;
		    case 1: ++y; break;
		    case 2: --x; break;
		    case 3: --y; break;
		    }
		if(x<0 || x>9 || y<0 || y>9){ cstart = -1; goto redo; }
		if(!hits[turn][y*10+x]) break;
		}
	    }
	c = y*10 + x;
	}

    for(;;){
	prompt();
	printw("I shoot at %c%d. Do I (H)it or (M)iss? ",c/10+'A',c%10);
	res = sgetc("HM");
	if((res=='H' && !board[OTHER][c]) || (res=='M' && board[OTHER][c])){
	    error("You lie!");
	    continue;
	    }
	break;
	}
    addch(res);
    hits[turn][c] = res;
    if(res == 'H'){
	++cpuhits;
	if(cstart == -1) cdir = -1;
	cstart = c;
	if(cdir == -2) cdir = d;
	mvaddch(7+(c/10),3+3*(c%10),'*');
	}
    else if(cdir == -2) cdir = -1;
    if(c=hitship(c)){
	cstart = -1;
	cpuhits -= plyship[c-1].length;
	x = plyship[c-1].start;
	d = plyship[c-1].dir;
	y = plyship[c-1].length;
	for(i=0; i<y; ++i){
	    hits[turn][x] = '*';
	    x += (d) ? 10 : 1;
	    }
	}
    if(awinna() != -1) return(0);
    return(res == 'H');
}

playagain(){
    int i, x, y, dx, dy, j;

    for(i=0; i<5; ++i){
	x = cpuship[i].start; y = x/10+7; x = (x % 10) * 3 + 48;
	dx = (cpuship[i].dir) ? 0 : 3;
	dy = (cpuship[i].dir) ? 1 : 0;
	for(j=0; j < cpuship[i].length; ++j){
	    mvaddch(y,x,cpuship[i].symbol);
	    x += dx; y += dy;
	    }
	}

    if(awinna()) ++cpuwon; else ++plywon;
    i = 18 + (sizeof name);
    if(plywon >= 10) ++i;
    if(cpuwon >= 10) ++i;
    mvprintw(2,(80-i)/2,"%s: %d     Computer: %d",name,plywon,cpuwon);

    prompt();
    printw((awinna()) ? "Want to be humiliated again, %s? "
		  : "Going to give me a chance for revenge, %s? ",name);
    return(sgetc("YN") == 'Y');
}

uninitgame(){
    refresh();
    resetterm(); echo(); endwin();
    exit(0);
}

sgetc(s)
char *s;
{
    char *s1;
    int ch;

    refresh();
    for(;;){
	ch = toupper(getch());
	if(ch == 3) uninitgame();
	for(s1=s; *s1 && ch != *s1; ++s1);
	if(*s1){
	    addch(ch); refresh();
	    return(ch);
	    }
	}
}
-- 

+----------------------------------------------------------------------------+
|Whatever I write are not the opinions or policies of Digital Research, Inc.,|
|and probably won't be in the foreseeable future.                            |
+----------------------------------------------------------------------------+

Bruce Holloway

....!ucbvax!hplabs!amdahl!drivax!holloway
(I'm not THAT Bruce Holloway, I'm the other one.)

cagordon@watnot.UUCP (Chris Gordon) (03/09/86)

Using the following compile command, I got the following undefines below:
cc -O -o bs bs.c -lcurses -ltermcap
Undefined:
_saveterm
_cbreak
_beep
_resetterm

Can someone (probably the original poster) help?

jrg@hpda.UUCP (Jeff Glasson) (03/10/86)

In article <11587@watnot.UUCP> cagordon@watnot.UUCP (Chris Gordon) writes:
>Using the following compile command, I got the following undefines below:
>cc -O -o bs bs.c -lcurses -ltermcap
>Undefined:
>_saveterm
>_cbreak
>_beep
>_resetterm
>
>Can someone (probably the original poster) help?

The undefined routines you see are from the AT&T version of curses.
BSD curses does not have those routines.

Here's a brief description of the routines (maybe someone out there has
BSD versions):

saveterm: save current modes as "in curses" state.
cbreak: set cbreak mode
beep: ring the terminal's bell
resetterm: set tty modes to "out of curses" state

Hope this helps,

Jeff Glasson
Hewlett-Packard ISO
ucbvax!hpda!jrg

thomas@utah-gr.UUCP (Spencer W. Thomas) (03/10/86)

In article <11587@watnot.UUCP> cagordon@watnot.UUCP (Chris Gordon) writes:
>cc -O -o bs bs.c -lcurses -ltermcap
>Undefined:
>_saveterm
>_cbreak
>_beep
>_resetterm

saveterm and resetterm are called savetty and resetty under BSD.  cbreak
is presumably crmode().

beep()
{
	write( 1, "\007", 1 );
}
-- 
=Spencer   ({ihnp4,decvax}!utah-cs!thomas, thomas@utah-cs.ARPA)

stevo@ucrmath.UUCP (Steve Groom) (03/11/86)

> Using the following compile command, I got the following undefines below:
> cc -O -o bs bs.c -lcurses -ltermcap
> Undefined:
> _saveterm
> _cbreak
> _beep
> _resetterm
> 
> Can someone (probably the original poster) help?

I got similar messages on our 4.2bsd system. I fixed them by making the
following substitutions to the source (hints found by doing "man curses") :
savetty() for saveterm()
crmode()  for cbreak()
resetty() for resetterm()

To fix beep(), I simply added a dummy procedure at the end of the program
that looked like this (hey, I know its a hack, but it works!) :

beep(){
}

If I decided I *liked* having the terminal make noises at me, I would 
simply add the code to beep() to make it send a ^G to the screen .... 
probably something like:

wprintw(stdscr,"%c",7);

(No warrantys given or implied about the above noisemaking fragment)

Steve Groom
Univ. of Calif., Riverside
{ucla-cs, ucbvax!ucdavis} !ucrmath!stevo

/* "waddaya want for nothin' ?!" */

holloway@drivax.UUCP (Bruce Holloway) (03/11/86)

In article <11587@watnot.UUCP> cagordon@watnot.UUCP (Chris Gordon) writes:
>Using the following compile command, I got the following undefines below:
>cc -O -o bs bs.c -lcurses -ltermcap
>Undefined:
>_saveterm
>_cbreak
>_beep
>_resetterm
>
>Can someone (probably the original poster) help?

All four of those functions are in both CURSES and MINICURSES here on our
System V implementation. According to the CURSES documentation, they are
standard functions which are used by "most interactive, screen-oriented
programs".

So you must have a different version of curses than we do.

Here's how I compile it:

	cc -o battle battle.c -lcurses

Which works fine.

-- 

+----------------------------------------------------------------------------+
|Whatever I write are not the opinions or policies of Digital Research, Inc.,|
|and probably won't be in the foreseeable future.                            |
+----------------------------------------------------------------------------+

Bruce Holloway

....!ucbvax!hplabs!amdahl!drivax!holloway
(I'm not THAT Bruce Holloway, I'm the other one.)

sma8465@ritcv.UUCP () (03/11/86)

In article <11587@watnot.UUCP> cagordon@watnot.UUCP (Chris Gordon) writes:
>Using the following compile command, I got the following undefines below:
>cc -O -o bs bs.c -lcurses -ltermcap
>Undefined:
>_saveterm
>_cbreak
>_beep
>_resetterm
>
>Can someone (probably the original poster) help?

Well, I'm not the original poster, but I can help.

First, use -ltermlib instead of -ltermcap.  This will clear up all errors
except for _beep (at least it did for me).  You just have to write
your own beep function which prints out a ctrl-G to the terminal.

ex:

beep()
{
   printf("%c",7);    /* beep! */
}

I worked with the curses version and beep was the only thing that didn't
work.  I hope your's works ok.

Steve Abbott
(c) Mad Wabbit Software, Inc.
Webster, New York 14580

cagordon@watnot.UUCP (Chris Gordon) (03/12/86)

In article <1358@hpda.UUCP> jrg@hpda.UUCP (Jeff Glasson) writes:
>In article <11587@watnot.UUCP> cagordon@watnot.UUCP (Chris Gordon) writes:
>>Using the following compile command, I got the following undefines below:
>>cc -O -o bs bs.c -lcurses -ltermcap
>>Undefined:
>>_saveterm
>>_cbreak
>>_beep
>>_resetterm
>>
>>Can someone (probably the original poster) help?
>
>The undefined routines you see are from the AT&T version of curses.
>BSD curses does not have those routines.
>
>Here's a brief description of the routines (maybe someone out there has
>BSD versions):
>
>saveterm: save current modes as "in curses" state.
>cbreak: set cbreak mode
>beep: ring the terminal's bell
>resetterm: set tty modes to "out of curses" state
>
>Hope this helps,
>
>Jeff Glasson
>Hewlett-Packard ISO
>ucbvax!hpda!jrg


Someone sent me the BSD versions of these commands. I just #define'd them as
so:

#define saveterm	savetty
#define cbreak		crmode
#define beep()		putchar('\7')  /* no equivalent */
#define resetterm	resetty

and all went well...

-------------------------------------------------------------------------------
|Chris Gordon	University of Waterloo                                        |
|watmath!watnot!cagordon                                                      |
|CSNET:cagordon%watnot@Waterloo.CSNet                                         |
|                                                                             |
|   "Welcome to the real world. There's so much to learn"                     |
|                                                  Mr. Mister                 |
-------------------------------------------------------------------------------

srt@ucla-cs.UUCP (03/12/86)

In article <11587@watnot.UUCP> cagordon@watnot.UUCP (Chris Gordon) writes:
>Using the following compile command, I got the following undefines below:
>cc -O -o bs bs.c -lcurses -ltermcap
>Undefined:
>_saveterm
>_cbreak
>_beep
>_resetterm

In the source code, replace:

        saveterm() --> savetty()
        cbreak() --> crmode()
        resetterm() --> resetty()

I don't know about "beep"...?

    Scott R. Turner
    ARPA:  (now) srt@UCLA-LOCUS.ARPA  (soon) srt@LOCUS.UCLA.EDU
    UUCP:  ...!{cepu,ihnp4,trwspp,ucbvax}!ucla-cs!srt
    FISHNET:  ...!{flounder,crappie,flipper}!srt@fishnet-relay.arpa

campbell@maynard.UUCP (Larry Campbell) (03/14/86)

Please take this discussion to net.sources.d.
-- 
Larry Campbell                                 The Boston Software Works, Inc.
ARPA: maynard.UUCP:campbell@harvard.ARPA       120 Fulton Street
UUCP: {harvard,cbosgd}!wjh12!maynard!campbell  Boston MA 02109

karen@wjvax.UUCP (Karen Pline) (03/18/86)

[munch munch.....]

Regarding the Battleship Source posted to the net by Bruce:

We solved the problem with curses on BSD 4.2 by adding four short routines
to the bottom of the program - fairly simple.

Also, whenever you play the game, it thinks you're Bruce (I took care of 
that, too).

My problem, however, is with the pattern that the ships are set up in.  
The first time you play the game (every time you play it) the pattern that
the ships are in is identical.  If you stay in the game when the computer asks 
if you want to play again, a new pattern will be set up.  But every time you 
restart the game, the same patterns repeat.  So, once you play through the 
patterns without getting out of the program, you know the patterns for the 
next time you play (I hope this makes sense).  There may be something wrong 
with the random pattern generator in the program (if there is one), but I 
haven't had time to look.

If someone looks into this, please post to the net or let me know.  

Anyway, I've never posted to this group before, but I thought this was
of general interest since so many people grabbed this piece of code.

And since we're talking battleship games, we've had one on our system for a
while which also came from the net (I think).  I can't remember who
posted it, but it also has a problem.  When it searches for your ships, it
always assumes you set yours up under the same strategy it sets its own
ships up with - i.e., good possibility of adjacency.  Then, it doesn't
eliminate logically.  By this I mean that even if it hits all the ships except
the Carrier, it still looks for adjacencies or guesses randomly instead of
just looking for openings where the five holes would fit.  Needless to say,
I've never lost (and believe me, I've played more than 100 times) even
when I try to accomodate it a little bit.

				Karen Pline

greg@utcsri.UUCP (Gregory Smith) (03/21/86)

[Hey Line Eater.......      eat a little line for me.....]

This is in reply to a posting that complained that the recently posted
battleships game always generated the same initial patterns for ships.

The rand() function, by default, always generates the same 'quasi-random'
sequence. This is a feature, not a bug; it is much easier to debug things
when you can reproduce the same sequence over and over again.
( I am assuming the program uses rand(); I don't know for sure ).

rand() has a buddy called srand(i) which 'spins the wheel' based on the
int i, so that different random sequences can be produced each time.

Try:		srand( (int) getpid());

This should be executed exactly once, and before any call to rand.
getpid() returns a long int which is the current process id. I 'casted'
the long to an int in the above fragment because the manual says that
getpid returns a long and that srand needs an int. On my machine, a vax,
this is redundant because they are both 32 bits, but it probably is
required on many other machines.

If this fix does not work, check that (1) you have srand and getpid available
(2) srand wants an int. If (2) is wrong, change the cast ( i.e. the (int) ).
If you do not have getpid, use something that returns a number based on the
time, or something like that. If you don't have srand(), you're on your own :-)

Hope this helps.
-- 
"No eternal reward will forgive us now for wasting the dawn" -J. Morrison
----------------------------------------------------------------------
Greg Smith     University of Toronto       ..!decvax!utzoo!utcsri!greg

holloway@drivax.UUCP (Bruce Holloway) (03/21/86)

I've posted fixes to all those problems to the net. Every single one of those
buggers. Here is that program again, the full, bug-free version that
KNOWS who you are, randomizes correctly, allows you to change your mind on
entering expressions, has three different #defines controlling type of play,
and can be used on both BSD and System V.

(Wait -- I'll post it Real Soon Now).

-- 

+----------------------------------------------------------------------------+
|Whatever I write are not the opinions or policies of Digital Research, Inc.,|
|and probably won't be in the foreseeable future.                            |
+----------------------------------------------------------------------------+

Bruce Holloway

....!ucbvax!hplabs!amdahl!drivax!holloway
(I'm not THAT Bruce Holloway, I'm the other one.)