/* Checks to see if there's room for a ship of a given length in a given * direction. If direction is negative, check in all directions. Note * that North and South are equivalent, as are East and West. */ static bool cpushipcanfit(int x, int y, int length, int direction) { int len = 1; int x1, y1; if (direction >= 0) { direction %= 4; while (direction < 8) { x1 = x + xincr[direction]; y1 = y + yincr[direction]; while (POSSIBLE(x1,y1)) { len++; x1 += xincr[direction]; y1 += yincr[direction]; } direction += 4; } return (len >= length); } else { return ((cpushipcanfit(x,y,length,E)) || (cpushipcanfit(x,y,length,S))); } }
/* * 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. * * The FSM is not externally reset to RANDOM_FIRE if the player wins. Instead, * the other states check for "impossible" conditions which signify a new * game, then if found transition to RANDOM_FIRE. */ static bool cputurn(void) { #define POSSIBLE(x, y) (ONBOARD(x, y) && !hits[COMPUTER][x][y]) #define RANDOM_FIRE 0 #define RANDOM_HIT 1 #define HUNT_DIRECT 2 #define FIRST_PASS 3 #define REVERSE_JUMP 4 #define SECOND_PASS 5 static int next = RANDOM_FIRE; static bool used[4]; static ship_t ts; int navail, x, y, d, n; int hit = S_MISS; 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[S / 2] = used[W / 2] = used[N / 2] = FALSE; /* 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 { n = rnd(navail) + 1; for (d = 0; used[d]; d++) ; /* used[d] is first that == 0 */ for (; n > 1; n--) while (used[++d]) ; /* used[d] is next that == 0 */ assert(d < 4); assert(used[d] == FALSE); used[d] = TRUE; x = ts.x + xincr[d * 2]; y = ts.y + yincr[d * 2]; assert(POSSIBLE(x, y)); 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) && (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; case REVERSE_JUMP: /* nail down the ship's other end */ d = (ts.dir + 4) % 8; x = ts.x + ts.hits * xincr[d]; y = ts.y + ts.hits * yincr[d]; if (POSSIBLE(x, y) && (hit = cpufire(x, y))) { ts.x = x; ts.y = y; ts.dir = d; ts.hits++; next = (hit == S_SUNK) ? RANDOM_FIRE : SECOND_PASS; } else next = RANDOM_FIRE; break; case SECOND_PASS: /* continue shooting after reversing */ x = ts.x + xincr[ts.dir]; y = ts.y + yincr[ts.dir]; if (POSSIBLE(x, y) && (hit = cpufire(x, y))) { ts.x = x; ts.y = y; ts.hits++; next = (hit == S_SUNK) ? RANDOM_FIRE : SECOND_PASS; break; } else next = RANDOM_FIRE; break; } /* pause between shots in salvo */ if (salvo) { (void) refresh(); (void) sleep(1); } #ifdef DEBUG (void) mvprintw(PROMPTLINE + 2, 0, "New state %d, x=%d, y=%d, d=%d", next, x, y, d); #endif /* DEBUG */ return ((hit) ? TRUE : FALSE); }
/* * 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 bool cputurn(void) { #define POSSIBLE(x, y) (ONBOARD(x, y) && !hits[COMPUTER][x][y]) #define RANDOM_FIRE 0 #define RANDOM_HIT 1 #define HUNT_DIRECT 2 #define FIRST_PASS 3 #define REVERSE_JUMP 4 #define SECOND_PASS 5 static int next = RANDOM_FIRE; static bool used[4]; static ship_t ts; int navail, x, y, d, n, hit = S_MISS; 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[S/2] = used[W/2] = used[N/2] = FALSE; /* 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--) while (used[d]) d++; assert(d <= 4); used[d] = FALSE; x = ts.x + xincr[d*2]; y = ts.y + yincr[d*2]; assert(POSSIBLE(x, y)); 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) && (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; case REVERSE_JUMP: /* nail down the ship's other end */ d = ts.dir + 4; x = ts.x + ts.hits * xincr[d]; y = ts.y + ts.hits * yincr[d]; if (POSSIBLE(x, y) && (hit = cpufire(x, y))) { ts.x = x; ts.y = y; ts.dir = d; ts.hits++; next = (hit == S_SUNK) ? RANDOM_FIRE : SECOND_PASS; } else next = RANDOM_FIRE; break; 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) && (hit = cpufire(x, y))) { ts.x = x; ts.y = y; ts.hits++; next = (hit == S_SUNK) ? RANDOM_FIRE: SECOND_PASS; break; } else next = RANDOM_FIRE; break; } /* check for continuation and/or winner */ if (salvo) { refresh(); sleep(1); } if (awinna() != -1) return(FALSE); #ifdef DEBUG mvprintw(PROMPTLINE + 2, 0, "New state %d, x=%d, y=%d, d=%d", next, x, y, d); #endif /* DEBUG */ return(hit); }
/* * 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); }