static bool collidecheck(int b, int y, int x) /* is this location on the selected zboard adjacent to a ship? */ { bool collide; /* anything on the square */ if ((collide = IS_SHIP(board[b][x][y])) != FALSE) return (collide); /* anything on the neighbors */ if (!closepack) { int i; for (i = 0; i < 8; i++) { int xend, yend; yend = y + yincr[i]; xend = x + xincr[i]; if (ONBOARD(xend, yend) && IS_SHIP(board[b][xend][yend])) { collide = TRUE; break; } } } return (collide); }
/* is this location on the selected zboard adjacent to a ship? */ static int collidecheck(int b, int y, int x) { int collide; /* anything on the square */ if ((collide = IS_SHIP(board[b][x][y])) != 0) return(collide); /* anything on the neighbors */ if (!closepack) { int i; for (i = 0; i < 8; i++) { int xend, yend; yend = y + yincr[i]; xend = x + xincr[i]; if (ONBOARD(xend, yend)) collide += IS_SHIP(board[b][xend][yend]); } } return(collide); }
static bool checkplace(int b, ship_t *ss, int vis) { int l, xend, yend; /* first, check for board edges */ xend = ss->x + (ss->length - 1) * xincr[ss->dir]; yend = ss->y + (ss->length - 1) * yincr[ss->dir]; if (!ONBOARD(xend, yend)) { if (vis) 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(FALSE); } for(l = 0; l < ss->length; ++l) { if(collidecheck(b, ss->y+l*yincr[ss->dir], ss->x+l*xincr[ss->dir])) { if (vis) 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(FALSE); } } return(TRUE); }
static ship_t * hitship(int x, int y) /* register a hit on the targeted ship */ { ship_t *sb, *ss; char sym; int oldx, oldy; getyx(stdscr, oldy, oldx); sb = (turn) ? plyship : cpuship; if ((sym = board[OTHER][x][y]) == 0) return ((ship_t *) NULL); for (ss = sb; ss < sb + SHIPTYPES; ++ss) if (ss->symbol == sym) { if (++ss->hits < ss->length) /* still afloat? */ return ((ship_t *) NULL); else { /* sunk! */ int i, j; if (!closepack) for (j = -1; j <= 1; j++) { int bx = ss->x + j * xincr[(ss->dir + 2) % 8]; int by = ss->y + j * yincr[(ss->dir + 2) % 8]; for (i = -1; i <= ss->length; ++i) { int x1, y1; x1 = bx + i * xincr[ss->dir]; y1 = by + i * yincr[ss->dir]; if (ONBOARD(x1, y1)) { hits[turn][x1][y1] = MARK_MISS; if (turn % 2 == PLAYER) { cgoto(y1, x1); #ifdef A_COLOR if (has_colors()) attron(COLOR_PAIR(COLOR_GREEN)); #endif /* A_COLOR */ (void) addch(MARK_MISS); #ifdef A_COLOR (void) attrset(0); #endif /* A_COLOR */ } else { pgoto(y1, x1); (void) addch(SHOWSPLASH); } } } } for (i = 0; i < ss->length; ++i) { int x1 = ss->x + i * xincr[ss->dir]; int y1 = ss->y + i * yincr[ss->dir]; hits[turn][x1][y1] = ss->symbol; if (turn % 2 == PLAYER) { cgoto(y1, x1); (void) addch((chtype) (ss->symbol)); } else { pgoto(y1, x1); #ifdef A_COLOR if (has_colors()) attron(COLOR_PAIR(COLOR_RED)); #endif /* A_COLOR */ (void) addch(SHOWHIT); #ifdef A_COLOR (void) attrset(0); #endif /* A_COLOR */ } } (void) move(oldy, oldx); return (ss); } } (void) move(oldy, oldx); return ((ship_t *) NULL); }
/* a hit on the targeted ship */ static ship_t * hitship(int x, int y) { ship_t *sb, *ss; char sym; int oldx, oldy; getyx(stdscr, oldy, oldx); sb = (turn) ? plyship : cpuship; if(!(sym = board[OTHER][x][y])) return(NULL); for(ss = sb; ss < sb + SHIPTYPES; ++ss) if(ss->symbol == sym) { if (++ss->hits < ss->length) { /* still afloat? */ return(NULL); } else { /* sunk */ int i, j; if (!closepack) { for (j = -1; j <= 1; j++) { int bx = ss->x + j * xincr[(ss->dir + 2) % 8]; int by = ss->y + j * yincr[(ss->dir + 2) % 8]; for (i = -1; i <= ss->length; ++i) { int cx, cy; cx = bx + i * xincr[ss->dir]; cy = by + i * yincr[ss->dir]; if (ONBOARD(cx, cy)) { hits[turn][cx][cy] = MARK_MISS; if (turn % 2 == PLAYER) { cgoto(cy, cx); #ifdef A_COLOR if (has_colors()) attron(COLOR_PAIR(COLOR_GREEN)); #endif /* A_COLOR */ addch(MARK_MISS); #ifdef A_COLOR attrset(0); #endif /* A_COLOR */ } } } } } for (i = 0; i < ss->length; ++i) { int dx = ss->x + i * xincr[ss->dir]; int dy = ss->y + i * yincr[ss->dir]; hits[turn][dx][dy] = ss->symbol; if (turn % 2 == PLAYER) { cgoto(dy, dx); addch(ss->symbol); } } move(oldy, oldx); return(ss); } } move(oldy, oldx); return(NULL); }
/* * This code implements a fairly irregular FSM, so please forgive the rampant * unstructuredness below. The five labels are states which need to be held * between computer turns. */ static int cputurn(void) { static bool used[4]; static ship_t ts; int navail, x, y, d, n, hit = S_MISS; bool closenoshot = FALSE; switch(next) { case RANDOM_FIRE: /* last shot was random and missed */ refire: randomfire(&x, &y); if (!(hit = cpufire(x, y))) next = RANDOM_FIRE; else { ts.x = x; ts.y = y; ts.hits = 1; next = (hit == S_SUNK) ? RANDOM_FIRE : RANDOM_HIT; } break; case RANDOM_HIT: /* last shot was random and hit */ used[E/2] = used[W/2] = (!(cpushipcanfit(ts.x,ts.y,cpushortest,E))); used[S/2] = used[N/2] = (!(cpushipcanfit(ts.x,ts.y,cpushortest,S))); /* FALLTHROUGH */ case HUNT_DIRECT: /* last shot hit, we're looking for ship's long axis */ for (d = navail = 0; d < 4; d++) { x = ts.x + xincr[d*2]; y = ts.y + yincr[d*2]; if (!used[d] && POSSIBLE(x, y)) navail++; else used[d] = TRUE; } if (navail == 0) /* no valid places for shots adjacent... */ goto refire; /* ...so we must random-fire */ else { for (d = 0, n = rnd(navail) + 1; n; n--,d++) while (used[d]) d++; d--; x = ts.x + xincr[d*2]; y = ts.y + yincr[d*2]; if (!(hit = cpufire(x, y))) next = HUNT_DIRECT; else { ts.x = x; ts.y = y; ts.dir = d*2; ts.hits++; next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS; } } break; case FIRST_PASS: /* we have a start and a direction now */ x = ts.x + xincr[ts.dir]; y = ts.y + yincr[ts.dir]; if (POSSIBLE(x, y)) { if ((hit = cpufire(x, y))) { ts.x = x; ts.y = y; ts.hits++; next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS; } else next = REVERSE_JUMP; break; } else next = REVERSE_JUMP; /* FALL THROUGH */ case REVERSE_JUMP: /* nail down the ship's other end */ ts.dir = (ts.dir + 4) % 8; ts.x += (ts.hits-1) * xincr[ts.dir]; ts.y += (ts.hits-1) * yincr[ts.dir]; /* FALL THROUGH */ case SECOND_PASS: /* kill squares not caught on first pass */ x = ts.x + xincr[ts.dir]; y = ts.y + yincr[ts.dir]; if (POSSIBLE(x, y)) { if ((hit = cpufire(x, y))) { ts.x = x; ts.y = y; ts.hits++; next = (hit == S_SUNK) ? RANDOM_FIRE : SECOND_PASS; } else { /* The only way to get here is if closepack is on; otherwise, * we _have_ sunk the ship. I set hit to S_SUNK just to get * the additional closepack logic at the end of the switch. */ /*assert closepack*/ if (!closepack) error("Assertion failed: not closepack 1"); hit = S_SUNK; next = RANDOM_FIRE; } } else { /*assert closepack*/ if (!closepack) error("Assertion failed: not closepack 2"); hit = S_SUNK; closenoshot = TRUE; /* Didn't shoot yet! */ next = RANDOM_FIRE; } break; } /* switch(next) */ if (hit == S_SUNK) { /* Update cpulongest and cpushortest. We could increase srchstep * if it's smaller than cpushortest but that makes strategic sense * only if we've been doing continuous diagonal stripes, and that's * less interesting to watch. */ ship_t *sp = plyship; cpushortest = cpulongest; cpulongest = 0; for (d=0 ; d < SHIPTYPES; d++, sp++) { if (sp->hits < sp->length) { cpushortest = (cpushortest < sp->length) ? cpushortest : sp->length; cpulongest = (cpulongest > sp->length) ? cpulongest : sp->length; } } /* Now, if we're in closepack mode, we may have knocked off part of * another ship, in which case we shouldn't do RANDOM_FIRE. A * more robust implementation would probably do this check regardless * of whether closepack was set or not. * Note that MARK_HIT is set only for ships that aren't sunk; * hitship() changes the marker to the ship's character when the * ship is sunk. */ if (closepack) { ts.hits = 0; for (x = 0; x < BWIDTH; x++) for (y = 0; y < BDEPTH; y++) { if (hits[COMPUTER][x][y] == MARK_HIT) { /* So we found part of another ship. It may have more * than one hit on it. Check to see if it does. If no * hit does, take the last MARK_HIT and be RANDOM_HIT. */ ts.x = x; ts.y = y; ts.hits = 1; for (d = 0; d < 8; d += 2) { while ((ONBOARD(ts.x, ts.y)) && (hits[COMPUTER][(int)ts.x][(int)ts.y] == MARK_HIT)) { ts.x += xincr[d]; ts.y += yincr[d]; ts.hits++; } if ((--ts.hits > 1) && (ONBOARD(ts.x, ts.y)) && (hits[COMPUTER][(int)ts.x][(int)ts.y] == 0)) { ts.dir = d; ts.x -= xincr[d]; ts.y -= yincr[d]; d = 100; /* use as a flag */ x = BWIDTH; y = BDEPTH; /* end the loop */ } else { ts.x = x; ts.y = y; ts.hits = 1; } } } if (ts.hits) { next = (d >= 100) ? FIRST_PASS : RANDOM_HIT; } else next = RANDOM_FIRE; } } if (closenoshot) { return(cputurn()); } } /* check for continuation and/or winner */ if (salvo) { (void)refresh(); (void)sleep(1); } return(hit); }