Pannyx

devel

Request #12

Oui, comme ça!


~/devel/proto/aej
% pp vterm.c vterm.h
#define VTERM_C
#include <vterm.h>
#include <termios.h>
#include <sys/ioctl.h>

int_t screen_width;
int_t screen_height;

static struct termios termios_st;
static struct termios b_termios_st;
static struct winsize winsize_st;


void
run_vterm (vterm_pass_proc_vterm_pass_t p)
{
   vterm_pass_t r;

   if (!( isatty(STDIN_FILENO))) { return; }

   tcgetattr(STDIN_FILENO, & termios_st);
   b_termios_st = termios_st;
   termios_st.c_lflag &= ~(ICANON | ECHO | IEXTEN | ISIG);
   termios_st.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
   termios_st.c_cflag &= ~(CSIZE | PARENB);
   termios_st.c_cflag |= CS8;
   termios_st.c_cc[VMIN] = 1;
   termios_st.c_cc[VTIME] = 0;
   tcsetattr(STDIN_FILENO, TCSAFLUSH, & termios_st);
   ioctl(1, TIOCGWINSZ, & winsize_st);
   screen_width = int_c( winsize_st.ws_col);
   screen_height = int_c( winsize_st.ws_row);
   if (p == NULL) { ouch(); return; }

   r = p(NULL);
   while (!( r == NULL)) {
      r = r->p(r);
   }
   return;

}

void
close_vterm (void)
{

   tcsetattr(STDIN_FILENO, TCSAFLUSH, & b_termios_st);
   return;

}

uint64_t
getk (void)
{
…
}

// vterm.c
#ifndef VTERM_H
#define VTERM_H
#include <daejana.h>

                 struct vterm_pass_struct;
typedef          struct vterm_pass_struct vterm_pass_st;
typedef                                   vterm_pass_st * vterm_pass_t;
typedef    vterm_pass_t (* vterm_pass_proc_vterm_pass_t) (vterm_pass_t);
struct vterm_pass_struct { vterm_pass_proc_vterm_pass_t p; int_t v []; };

void run_vterm (vterm_pass_proc_vterm_pass_t);
void close_vterm (void);

#endif
// vterm.h
% cdm
~/devel/sleek
% pp vterm_m.c
#include <chars.h>
#include <vterm.h>

static vterm_pass_t pass_01 (vterm_pass_t);
static vterm_pass_t pass_02 (vterm_pass_t);
static vterm_pass_t pass_03 (vterm_pass_t);


static vterm_pass_t
pass_01 (vterm_pass_t r)
{
   vterm_pass_t q;

   if (!( r == NULL)) { ouch(); return NULL; }

   if (( q = malloc(sizeof (vterm_pass_st) + sizeof (int_t) * 2)) == NULL) { ouch(); return NULL; }

   q->p = pass_02;
   q->v[0] = screen_width;
   q->v[1] = screen_height;
   return q;

}

static vterm_pass_t
pass_02 (vterm_pass_t r)
{
   int_t n;
   vterm_pass_t q;

   if (r == NULL) { ouch(); return NULL; }

   printf("%lld;\n", r->v[0]);
   printf("%lld;\n", r->v[1]);
   {
      char k;
      char s [512];

      printf("%s\x08", ellipsis);
      while ( (k = getchar()) == ' ' || k == '\t') { fputc(k, stdout); }
      s[0] = k; fputc(s[0], stdout);
      n = 1; while (!( (s[n] = getchar()) == '\r' || k == '\n')) { fputc(s[n], stdout); n += 1; }
      while (s [n - 1] == ' ' || s [n - 1] == '\t') { n -= 1; }
      s[n] = '\0';
      fputs("\r\x1b[2K", stdout);
      if (( q = realloc(r, sizeof (vterm_pass_st) + sizeof (int_t) * (n + 8) / 8)) == NULL) { ouch(); return NULL; }

      q->p = pass_03;
      strncpy(char_ptr_c( & q->v[0]), s, n);
   }
   return q;

}

static vterm_pass_t
pass_03 (vterm_pass_t r)
{

   if (r == NULL) { ouch(); return NULL; }

   printf("%s;\n", char_ptr_c( & r->v[0]));
   free(r);
   return NULL;

}

int
main (int argc, char * argv [])
{

   error_flag = false;
   printf("\n");

   run_vterm(pass_01);

   close_vterm();
   printf("\n");
   if (error_flag) { exit(EXIT_FAILURE); }

   return EXIT_SUCCESS;

}

// vterm_m.c
% cmr vterm_m chars vterm

160;
46;
x x;

%

Looks good as a first draft, coming up with a stub version.  We could spend more time later using the extensions for Guile and Ocaml.  It is more likely, though, to do more with Swift from now on.  But there are more games to come which will be more interesting.  For now, it was just enough to see an adequate first result using C.  Reducing the size of the board, instead of having it extended to the whole width, was a good idea to make it more playable.  By only using the tab key, you achieve as a result a game played with equal chances for you, as the game user, and your opponent, for whom the next move is always chosen randomly, with some essential, yet simple heuristics as suggested for the game already implemented.  Alright, one could still do more there, but the emphasis here was to get it going in a very adequate way, not becoming experts in how it is played.  Which should be left for those who would rather spend their time for things like that.


~/devel/sleek
% cda
~/devel/proto/aej
% pp vterm.c vterm.h
#include <termios.h>
#include <sys/ioctl.h>
#define VTERM_C
#include <vterm.h>

static void sig_winch (int);
static void mvto (int_t, int_t);

int_t screen_width;
int_t screen_height;

char s_error [m_pad];

static struct termios termios_st;
static struct termios b_termios_st;
static struct winsize winsize_st;

static int_t where_x;
static int_t where_y;
static int_t whence_x;
static int_t whence_y;
static int_t min_x;
static int_t min_y;
static int_t max_x;
static int_t max_y;
static uint_t * a_screen;
static uint_t getk_b;
static char s_pad [m_pad];


static void
sig_winch (int n) {
      ioctl(1, TIOCGWINSZ, & winsize_st);
      return; }

#ifndef REQUIRED_SCREEN_WIDTH
#define REQUIRED_SCREEN_WIDTH 160
#endif
#ifndef REQUIRED_SCREEN_HEIGHT
#define REQUIRED_SCREEN_HEIGHT 46
#endif

void
run_vterm (vterm_pass_proc_vterm_pass_t p)
{
   int_t k; int_t n;
   vterm_pass_t r;

   if (!( isatty(STDIN_FILENO))) { return; }

   tcgetattr(STDIN_FILENO, & termios_st);
   b_termios_st = termios_st;
   termios_st.c_lflag &= ~(ICANON | ECHO | IEXTEN | ISIG);
   termios_st.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
   termios_st.c_cflag &= ~(CSIZE | PARENB);
   termios_st.c_cflag |= CS8;
   termios_st.c_cc[VMIN] = 1;
   termios_st.c_cc[VTIME] = 0;
   tcsetattr(STDIN_FILENO, TCSAFLUSH, & termios_st);
   if (signal(SIGWINCH, sig_winch) == SIG_ERR) { ouch(); return; }

   sig_winch(SIGWINCH);
   if (winsize_st.ws_col < REQUIRED_SCREEN_WIDTH || winsize_st.ws_row < REQUIRED_SCREEN_HEIGHT) {
      printf("To contine, you must first increase the size of your terminal.\n");
      printf("At least 160x46 is required."); fflush(stdout);
      pause();
      if (winsize_st.ws_col < REQUIRED_SCREEN_WIDTH || winsize_st.ws_row < REQUIRED_SCREEN_HEIGHT) {
         printf("\r\x1b[2K   Must be : 160x46\x1b[1A"); fflush(stdout);
         while (winsize_st.ws_col < REQUIRED_SCREEN_WIDTH || winsize_st.ws_row < REQUIRED_SCREEN_HEIGHT) {
            printf("\r\x1b[2KChanged to : %3dx%2d", winsize_st.ws_col, winsize_st.ws_row); fflush(stdout);
            pause();
         }
      }
      fputs("\r\x1b[2K\n\x1b[2K", stdout); fflush(stdout);
   }
   screen_width = int_c( winsize_st.ws_col) - 1;
   screen_height = int_c( winsize_st.ws_row) - 1;
   if (p == NULL) { ouch(); return; }

   n = screen_width * screen_height;
   if (( a_screen = malloc(sizeof (uint_t) * n)) == NULL) { ouch(); return; }

   where_x = 0; where_y = 0; whence_x = 0; whence_y = 0;
   min_x = screen_width; max_x = -1;
   min_y = screen_height; max_y = -1;
   k = 0; while (k < n) { a_screen[k] = uint_c( 0x20); k += 1; }
   fputs("\x1b[1;1H\x1b[2J", stdout);
   r = p(NULL);
   while (!( r == NULL)) {
      r = r->p(r);
   }
   return;

}

void
close_vterm (void)
{

   free(a_screen); a_screen = NULL;
   tcsetattr(STDIN_FILENO, TCSAFLUSH, & b_termios_st);
   mvto(screen_width, 0);
   return;

}

uint_t
getk (void)
{
   uchar_t k [sizeof (uint_t) / sizeof (uchar_t)];
   uchar_t u;
   int_t j;
   int_t n;

   if (!( getk_b == 0)) { * (uint_t *) k = getk_b; getk_b = 0; return * (uint_t *) k; }

   if (fread(& u, sizeof (uchar_t), 1, stdin) < 1) { * (uint_t *) k = 0x000000000000001b; goto Ll; }

   * (uint_t *) k = 0;
   if (u < 0x80) {
      k[0] = u;
      if (u == 0x1b) {
         j = 0;
         if (fread(& u, sizeof (uchar_t), 1, stdin) < 1) { * (uint_t *) k = 0x000000000000001b; goto Ll; }

         j += 1;
         switch (u) {

            case 0x5b:
               k[j] = u;
               if (fread(& u, sizeof (uchar_t), 1, stdin) < 1) { * (uint_t *) k = 0x000000000000001b; goto Ll; }

               j += 1;
               switch (u) {

                  case 0x31:
                     k[j] = u;
                     if (fread(& u, sizeof (uchar_t), 1, stdin) < 1) { * (uint_t *) k = 0x000000000000001b; goto Ll; }

                     j += 1;
                     if (u == 0x3b) {
                        k[j] = u;
                        if (fread(& u, sizeof (uchar_t), 1, stdin) < 1) { * (uint_t *) k = 0x000000000000001b; goto Ll; }

                        j += 1;
                        if (u == 0x32) {
                           k[j] = u;
                           if (fread(& u, sizeof (uchar_t), 1, stdin) < 1) { * (uint_t *) k = 0x000000000000001b; goto Ll; }

                           j += 1;
                           if (u == 0x43) {  // right_shift ← 0x000043323b315b1b
                              k[j] = u;
                              goto Ll;
                           }
                           else if (u == 0x44) {  // left_shift ← 0x000044323b315b1b
                              k[j] = u;
                              goto Ll;
                           }
                        }
                     }
                     * (uint_t *) k = 0x000000000000001b; goto Ll;

                  case 0x33:
                     k[j] = u;
                     if (fread(& u, sizeof (uchar_t), 1, stdin) < 1) { * (uint_t *) k = 0x000000000000001b; goto Ll; }

                     j += 1;
                     switch (u) {

                        case 0x3b:
                           k[j] = u;
                           if (fread(& u, sizeof (uchar_t), 1, stdin) < 1) { * (uint_t *) k = 0x000000000000001b; goto Ll; }

                           j += 1;
                           if (u == 0x32) {
                              k[j] = u;
                              if (fread(& u, sizeof (uchar_t), 1, stdin) < 1) { * (uint_t *) k = 0x000000000000001b; goto Ll; }

                              j += 1;
                              if (u == 0x7e) {  // delete_shift ← 0x00007e323b335b1b
                                 k[j] = u;
                                 goto Ll;
                              }
                           }
                           * (uint_t *) k = 0x000000000000001b; goto Ll;

                        case 0x7e:  // delete ← 0x000000007e335b1b
                           k[j] = u;
                           goto Ll;

                        default:
                           * (uint_t *) k = 0x000000000000001b; goto Ll;
                     }

                  case 0x41:  // up ← 0x0000000000415b1b
                  case 0x42:  // down ← 0x0000000000425b1b
                  case 0x43:  // right ← 0x0000000000435b1b
                  case 0x44:  // left ← 0x0000000000445b1b
                  case 0x5a:  // tab_shift ← 0x00000000005a5b1b
                     k[j] = u;
                     goto Ll;

                  default:
                     * (uint_t *) k = 0x000000000000001b; goto Ll;
               }

            case 0x62:  // left_option ← 0x000000000000621b
            case 0x66:  // right_option ← 0x000000000000661b
               k[j] = u;
               goto Ll;

            default:
               * (uint_t *) k = 0x000000000000001b; goto Ll;
         }
      }
      goto Ll;  // nonescape_7bit
   }
   else if (u < 0xc0) {
      n = 0;  // control_8bit
   }
   else if ((u & 0xe0) == 0xc0) {
      // 110xxxxx u
      // 11100000 e0
      // 11000000 c0
      n = 1;  // utf8 (2 byte)
   }
   else if ((u & 0xf0) == 0xe0) {
      // 1110xxxx u
      // 11110000 f0
      // 11100000 e0
      n = 2;  // utf8 (3 byte)
   }
   else {
      // (u & 0xf8) == 0xf0
      //    11110xxx u
      //    11111000 f8
      //    11110000 f0
      // 0xef < u
      //    11101111 ef
      n = 3;  // utf8 (4 byte)
   }
   k[0] = u;
   j = 0;
   while (0 < n) {
      if (fread(& u, sizeof (uchar_t), 1, stdin) < 1) { * (uint_t *) k = 0x000000000000001b; goto Ll; }

      j += 1;
      if ((u & 0xc0) == 0x80) {
         // 10xxxxxx u
         // 11000000 c0
         // 10000000 80

         k[j] = u;
      }
      else {
         * (uint_t *) k = 0x000000000000001b; goto Ll;

      }
      n -= 1;
   }
Ll:
   return * (uint_t *) k;

}

void
ungetk (uint_t k) {
      getk_b = k;
      return; }

void
move (int_t x, int_t y)
{

   whence_x = where_x; whence_y = where_y;
   where_x = x; where_y = y;
   if (max_x < min_x || max_y < min_y) { mvto(where_x, where_y); return; }
   return;

}

void
whence (void) {
      move(whence_x, whence_y);
      return; }

void
print_char (uint_t k)
{
   int_t j;

   if (where_x < 0 || screen_width - 1 < where_x || where_y < 0 || screen_height - 1 < where_y) { error_flag = true; mvto(screen_width, where_y);
   ↪ fputs("\xe2\x9a\xa0", stdout); snprintf(s_pad, m_pad, "%lld, %lld, 0x%08llx; ", where_x, where_y, k); strncat(s_error, s_pad, m_pad);
   ↪ return; }

   a_screen [where_y * screen_width + where_x] = k;
   min_x = int_min(where_x, min_x);
   move(where_x + 1, where_y);
   max_x = int_max(max_x, where_x);
   min_y = int_min(where_y, min_y); max_y = int_max(max_y, where_y);
   return;

}

void
print_string (const char * s)
{
   int_t h; int_t j; int_t m; int_t n;
   uint_t k;

   if (where_x < 0 || screen_width - 1 < where_x || where_y < 0 || screen_height - 1 < where_y) { error_flag = true; mvto(screen_width, where_y);
   ↪ fputs("\xe2\x9a\xa0", stdout); snprintf(s_pad, m_pad, "%lld, %lld, \"%s\"; ", where_x, where_y, s); strncat(s_error, s_pad, m_pad);
   ↪ return; }

   h = 0;
   j = 0;
   while (!( s[j] == '\x00')) {
      if (screen_width - 1 < where_x + h) { error_flag = true; mvto(screen_width, where_y); fputs("\xe2\x9a\xa0", stdout); goto L1; }

      mbs_getk(& s [j], & n, & k);
      a_screen [where_y * screen_width + where_x + h] = k;
      h += 1;
      j += n;
   }
L1:
   min_x = int_min(where_x, min_x);
   move(where_x + h, where_y);
   max_x = int_max(max_x, where_x);
   min_y = int_min(where_y, min_y); max_y = int_max(max_y, where_y);
   return;

}

void
refresh (void)
{
   int_t x; int_t y;

   if (max_x < min_x || max_y < min_y) { mvto(where_x, where_y); return; }

   y = min_y;
   while (y < max_y + 1) {
      x = min_x;
      while (x < max_x + 1) {
         mvto(x, y);
         fputs(char_ptr_c( & a_screen [y * screen_width + x]), stdout);
         x += 1;
      }
      y += 1;
   }
   min_x = screen_width; max_x = -1;
   min_y = screen_height; max_y = -1;
   move(where_x, where_y);
   return;

}

void
acknowledge (const char * s, int_t m, char * t)
{
   int_t n;
   char k;

   mvto(0, -1); fputs(s, stdout); fflush(stdout);
   if (t == NULL) { while (!( (k = getchar()) == '\x0a' || k == '\x0d')) { fputc(k, stdout); } return; }

   while ( (k = getchar()) == '\x20' || k == '\x09') { fputc(k, stdout); }

   n = 0;
   while (!( k == '\x0a' || k == '\x0d')) {
      t[n] = k;
      n += 1;
      fputc(k, stdout);
      if (!( n < m)) { goto L1; }

      k = getchar();
   }
   while (t [n - 1] == '\x20' || t [n - 1] == '\x09') { n -= 1; }

L1:
   t[n] = '\x00';
   fputs("\r\x1b[2K", stdout); fputs(t, stdout);
   return;

}

static void
mvto (int_t x, int_t y) {
      printf("\x1b[%lld;%lldH", screen_height - y, x + 1);
      return; }

// vterm.c
#ifndef VTERM_H
#define VTERM_H
#include <daejana.h>

                 struct vterm_pass_struct;
typedef          struct vterm_pass_struct vterm_pass_st;
typedef                                   vterm_pass_st * vterm_pass_t;
typedef    vterm_pass_t (* vterm_pass_proc_vterm_pass_t) (vterm_pass_t);
struct vterm_pass_struct { vterm_pass_proc_vterm_pass_t p; int_t v []; };


#ifndef VTERM_C

extern int_t screen_width;
extern int_t screen_height;
extern char s_error [m_pad];

#ifdef VTERM_GUILE_C


#endif
#ifdef VTERM_OCAML_C


#endif
#endif

void run_vterm (vterm_pass_proc_vterm_pass_t);
void close_vterm (void);

uint_t getk (void);
void ungetk (uint_t);

void move (int_t, int_t);
void whence (void);
void print_char (uint_t);
void print_string (const char *);
void refresh (void);

void acknowledge (const char *, int_t, char *);

#endif
// vterm.h
% cdm
~/devel/sleek
% pp maneuver_vterm.c
#include <chars.h>
#include <vterm.h>

static vterm_pass_t start_game (vterm_pass_t);
static void anchor_fleet (uchar_t *);
static void steer (int_t);
static int_t use_guess (int_t *);
void make_guess_x (uchar_t *, int_t, int_t, int_t *);
void make_guess_y (uchar_t *, int_t, int_t, int_t *);
static vterm_pass_t shoot_0 (vterm_pass_t);
static vterm_pass_t shoot_1 (vterm_pass_t);
static vterm_pass_t end_game (vterm_pass_t);


// enum {                               Carrier,   Battleship,   Destroyer,   Submarine,   Patrol_boat, N_ships };
static const char * ship_label [] = {  "Carrier", "Battleship", "Destroyer", "Submarine", "Patrol boat" };
static const int_t ship_length_e [] = { 5,         4,            3,           3,           2 };
static const int_t n_ships = 5;
static int_t ship_length_0 [n_ships];
static int_t ship_length_1 [n_ships];
static int_t pending_0 [n_ships * 5];
static int_t pending_1 [n_ships * 5];

static const int_t board_x = 18;
static const int_t board_0_y = 2;
static const int_t board_1_y = 24;
static const int_t grid_width = 13;  // 35
static const int_t grid_height = 10;

static uchar_t grid_0 [grid_width * grid_height];
static uchar_t grid_1 [grid_width * grid_height];

static int_t grid_1_where_x;
static int_t grid_1_where_y;
static int_t grid_1_whence_x;
static int_t grid_1_whence_y;

static const int_t control_panel_x = 10;
static const int_t control_panel_0_y = 0;
static const int_t control_panel_1_y = 22;

static const int_t score_panel_0_y = 3;
static const int_t score_panel_1_y = 25;
static const int_t score_panel_label_x = 3;

static char s_pad [m_pad];


static vterm_pass_t
start_game (vterm_pass_t r)
{
   vterm_pass_t q;
   int_t j; int_t l;

   if (!( r == NULL)) { error_flag = true; return NULL; }

   j = 0;
   snprintf(s_pad, m_pad, "%s%s%s%s%s", s_h_bar, s_center_cross, s_h_bar, s_h_bar, s_h_bar); j += 15;  // "─┼───"
   l = 1;
   while (l < grid_width) {
      snprintf(& s_pad [j], m_pad, "%s%s%s%s", s_north_fork, s_h_bar, s_h_bar, s_h_bar); j += 12;  // "┬───"
      l += 1;
   }
   strncpy(& s_pad [j], s_center_cross, 3); j += 3;
   strncpy(& s_pad [j], s_h_bar, 3); j += 3;  // "┼─"
   s_pad[j] = '\x00';

   move(board_x - 2, board_0_y - 1); print_string(s_pad);
   move(board_x - 2, board_1_y - 1); print_string(s_pad);

   j = 0;
   snprintf(s_pad, m_pad, "%s%s%s%s%s", s_h_bar, s_center_cross, s_h_bar, s_h_bar, s_h_bar); j += 15;  // "─┼───"
   l = 1;
   while (l < grid_width) {
      snprintf(& s_pad [j], m_pad, "%s%s%s%s", s_south_fork, s_h_bar, s_h_bar, s_h_bar); j += 12;  //  "┴───"
      l += 1;
   }
   strncpy(& s_pad [j], s_center_cross, 3); j += 3;
   strncpy(& s_pad [j], s_h_bar, 3); j += 3;  // "┼─"
   s_pad[j] = '\x00';

   move(board_x - 2, board_0_y - 1 + 2 * grid_height); print_string(s_pad);
   move(board_x - 2, board_1_y - 1 + 2 * grid_height); print_string(s_pad);

   j = 0;
   strncpy(s_pad, s_v_bar, 3); j += 3;  // "|"
   l = 0;
   while (l < grid_width) {
      s_pad[j] = '\x20';                j += 1;
      s_pad[j] = '\x20';                j += 1;
      s_pad[j] = '\x20';                j += 1;
      strncpy(& s_pad [j], s_v_bar, 3); j += 3;  // "   |"
      l += 1;
   }
   s_pad[j] = '\x00';

   move(board_x - 2, board_0_y); print_char(0x4a); print_string(s_pad); print_char(0x4a);
   move(board_x - 2, board_1_y); print_char(0x4a); print_string(s_pad); print_char(0x4a);
   l = 1;
   while (l < grid_height) {
      move(board_x - 2, board_0_y + l * 2); print_char(0x4a - l); print_string(s_pad); print_char(0x4a - l);
      move(board_x - 2, board_1_y + l * 2); print_char(0x4a - l); print_string(s_pad); print_char(0x4a - l);
      l += 1;
   }

   j = 0;
   strncpy(s_pad, s_h_bar, 3);           j += 3;
   strncpy(& s_pad [j], s_east_fork, 3); j += 3;
   s_pad[j] = '\x20';                    j += 1;
   strncpy(& s_pad [j], s_h_bar, 3);     j += 3;
   s_pad[j] = '\x20';                    j += 1;  // "─┤  ─ "
   l = 1;
   while (l < grid_width) {
      s_pad[j] = '\x20';                j += 1;
      s_pad[j] = '\x20';                j += 1;
      strncpy(& s_pad [j], s_h_bar, 3); j += 3;
      s_pad[j] = '\x20';                j += 1;  // "  ─ "
      l += 1;
   }
   strncpy(& s_pad [j], s_west_fork, 3); j += 3;
   strncpy(& s_pad [j], s_h_bar, 3);     j += 3;  // "├─"
   s_pad[j] = '\x00';

   l = 1;
   while (l < grid_height) {
      move(board_x - 2, board_0_y + l * 2 - 1); print_string(s_pad);
      move(board_x - 2, board_1_y + l * 2 - 1); print_string(s_pad);
      l += 1;
   }

   j = 0;
   strncpy(s_pad, s_v_bar, 3); j += 3;  // "|"
   l = 0;
   while (l < grid_width) {
      l += 1;
      snprintf(& s_pad [j], m_pad, " %02lld%s", l, s_v_bar); j += 6;  // " 30|"
   }
   s_pad[j] = '\x00';

   move(board_x - 1, board_0_y - 2); print_string(s_pad);
   move(board_x - 1, board_0_y + 2 * grid_height); print_string(s_pad);
   move(board_x - 1, board_1_y + 2 * grid_height); print_string(s_pad);

   j = 0;
   while (j < n_ships) {
      ship_length_0[j] = ship_length_e[j];
      move(score_panel_label_x - 2, score_panel_0_y + j * 4 - 1); print_char(0x30 + ship_length_0[j]);
      move(score_panel_label_x - 2, score_panel_0_y + j * 4);     print_char(k_check_mark);
      move(score_panel_label_x,     score_panel_0_y + j * 4);     print_string(ship_label[j]);
      ship_length_1[j] = ship_length_e[j];
      move(score_panel_label_x - 2, score_panel_1_y + j * 4 - 1); print_char(0x30 + ship_length_1[j]);
      move(score_panel_label_x - 2, score_panel_1_y + j * 4);     print_char(k_check_mark);
      move(score_panel_label_x,     score_panel_1_y + j * 4);     print_string(ship_label[j]);
   // pending_0 [j * 5] = 0; pending_1 [j * 5] = 0;
      j += 1;
   }

   move(control_panel_x - 2, control_panel_0_y); print_char(0x5b); move(control_panel_x + 4, control_panel_0_y); print_char(0x5d);
   move(control_panel_x - 2, control_panel_1_y); print_char(0x5b); move(control_panel_x + 4, control_panel_1_y); print_char(0x5d);

   anchor_fleet(grid_0);
   anchor_fleet(grid_1);

   l = 0;
   while (l < grid_height) {
      j = 0;
      while (j < grid_width) {
         if (!( grid_0 [l * grid_width + j] == 0)) {
            move(board_x + 1 + j * 4, board_0_y + l * 2); print_char(k_check_mark);
         }
         j += 1;
      }
      l += 1;
   }

   grid_1_where_x = rand() % grid_width; grid_1_where_y = rand() % grid_height;
   grid_1_whence_x = grid_1_where_x; grid_1_whence_y = grid_1_where_y;

   j = board_x + 1 + grid_1_where_x * 4; l = board_1_y + grid_1_where_y * 2;
   move(j + 1, l); print_char(0x5d); move(j - 1, l); print_char(0x5b);

   refresh();

   if (( q = malloc(sizeof (vterm_pass_st) + sizeof (int_t) * 0)) == NULL) { error_flag = true; return NULL; }

   if (rand() % 2 == 0) { q->p = shoot_0; } else { q->p = shoot_1; }
   return q;

}

static void
anchor_fleet (uchar_t * g)
{
   int_t e; int_t h; int_t j; int_t k; int_t n; uchar_t u; int_t x; int_t y;

   e = 0;
   while (e < n_ships) {
      n = ship_length_e[e]; u = 0x02 << e;
      while (true) {
         if (2 < rand() % 7) {
            x  = rand() % (grid_width - n); y = rand() % grid_height;
            h = y * grid_width + x; j = h; k = 0;
            while (k < n) {
               if (!( g[j] == 0)) { goto Ll_Next; }

               j += 1; k += 1;
            }
            j = h; k = 0; while (k < n) { g[j] = u; j += 1; k += 1; }
         }
         else {
            x = rand() % grid_width; y  = rand() % (grid_height - n);
            h = y * grid_width + x; j = h; k = 0;
            while (k < n) {
               if (!( g[j] == 0)) { goto Ll_Next; }

               j += grid_width; k += 1;
            }
            j = h; k = 0; while (k < n) { g[j] = u; j += grid_width; k += 1; }
         }
         goto Ll_Exit;
Ll_Next:
         ;
      }
Ll_Exit:
      e += 1;
   }
   return;
}

static void
steer (int_t j)
{
   int_t x; int_t y;

   x = board_x + 1 + grid_1_where_x * 4; y = board_1_y + grid_1_where_y * 2;
   move(x + 1, y); print_char(0x20); move(x - 1, y); print_char(0x20); refresh();
   grid_1_where_x = j % grid_width; grid_1_where_y = j / grid_width;
   move(control_panel_x, control_panel_0_y);
   print_char(0x41 + (grid_height - 1 - grid_1_where_y));
   print_char(0x30 + (grid_1_where_x + 1) / 10); print_char(0x30 + (grid_1_where_x + 1) % 10); refresh();
   x = board_x + 1 + grid_1_where_x * 4; y = board_1_y + grid_1_where_y * 2;
   move(x + 1, y); print_char(0x5d); move(x - 1, y); print_char(0x5b); refresh(); move(x, y); refresh();
   return;

}

static int_t
use_guess (int_t * p)
{
   int_t j; int_t k; int_t n;

   n = p[0];
   k = rand() % n;
   j = p [1 + k];
   while (k < n - 1) {
      p [1 + k] = p [1 + k + 1];
      k += 1;
   }
   p[0] = n - 1;
   return j;

}

void
make_guess_x (uchar_t * grid, int_t where_x, int_t where_y, int_t * pending)
{
   uchar_t h; int_t j; int_t n; int_t x; int_t y;

   n = 0;
   j = where_y * grid_width + where_x;
   h = grid[j];
   if (where_x < grid_width - 1) {
      x = where_x + 1; y = where_y; j += 1;
      while (x < grid_width - 1 && grid[j] == h) { x += 1; j += 1; }
      if ((grid[j] & 0x01) == 0) { pending [1 + n] = j; n += 1; }
   }
   j = where_y * grid_width + where_x;
   if (0 < where_x) {
      x = where_x - 1; y = where_y; j -= 1;
      while (0 < x && grid[j] == h) { x -= 1; j -= 1; }
      if ((grid[j] & 0x01) == 0) { pending [1 + n] = j; n += 1; }
   }
   pending[0] = n;
   return;

}

void
make_guess_y (uchar_t * grid, int_t where_x, int_t where_y, int_t * pending)
{
   uchar_t h; int_t j; int_t n; int_t x; int_t y;

   n = 0;
   j = where_y * grid_width + where_x;
   h = grid[j];
   if (where_y < grid_height - 1) {
      x = where_x; y = where_y + 1; j += grid_width;
      while (y < grid_height - 1 && grid[j] == h) { y += 1; j += grid_width; }
      if ((grid[j] & 0x01) == 0) { pending [1 + n] = j; n += 1; }
   }
   j = where_y * grid_width + where_x;
   if (0 < where_y) {
      x = where_x; y = where_y - 1; j -= grid_width;
      while (0 < y && grid[j] == h) { y -= 1; j -= grid_width; }
      if ((grid[j] & 0x01) == 0) { pending [1 + n] = j; n += 1; }
   }
   pending[0] = n;
   return;

}

static vterm_pass_t
shoot_0 (vterm_pass_t r)
{
   int_t e; int_t g; uint_t h; int_t j; uint_t k; int_t n; uchar_t u; int_t x; int_t y;

   r->p = shoot_1;

   move(control_panel_x, control_panel_0_y); print_char(k_ellipsis); print_char(0x20); print_char(0x20); refresh();
   move(board_x + 1 + grid_1_where_x * 4, board_1_y + grid_1_where_y * 2);
   k = getk(); ungetk(k);
   move(control_panel_x, control_panel_1_y); print_char(0x20); print_char(0x20); print_char(0x20); refresh();
   j = grid_1_where_y * grid_width + grid_1_where_x;
   g = 0;
   while (true) {
      switch (g) {

         case 0:
            steer(j);
            k = getk();
            if (int_within('A', k & 0xffffffffffffffcf, 'Z')) { g = 1; break; }

            switch (k) {

               case uint_c( 0x0000000000000004):  // ctrl-d
                  r->p = end_game;
                  return r;

               case uint_c( 0x0000000000000009):  // ctrl-i
                  e = 0;
                  while (e < n_ships) {
                     while (0 < pending_1 [e * 5]) {
                        j = use_guess(& pending_1 [e * 5]); u = grid_1[j];
                        if ((u & 0x01) == 0) { goto Lll1_Exit; }

                     }
                     e += 1;
                  }
               Lll1_Exit:
                  if (e == n_ships) {
                     n = grid_width * grid_height; j = rand() % n;
                     while (true) {
                        u = grid_1[j]; x = j % grid_width; y = j / grid_width;
                        if ((u & 0x01) == 0 && (
                              x < grid_width - 1  && (grid_1 [j + 1]          & 0x01) == 0
                           || y < grid_height - 1 && (grid_1 [j + grid_width] & 0x01) == 0
                           || 0 < x               && (grid_1 [j - 1]          & 0x01) == 0
                           || 0 < y               && (grid_1 [j - grid_width] & 0x01) == 0)) { goto Lll2_Exit; }

                        j += 1; j %= n;
                     }
                  Lll2_Exit:
                     ;
                  }
                  steer(j);
                  goto Ll_Exit;

               case uint_c( 0x000000000000000d): case uint_c( 0x000000000000000a):
                  u = grid_1[j];
                  if ((u & 0x01) == 0) {
                     goto Ll_Exit;
                  }

               case uint_c( 0x0000000000000015):  // ctrl-u
                  j = grid_1_whence_y * grid_width + grid_1_whence_x;
                  break;

               case uint_c( 0x0000000000435b1b):  // right
                  if (grid_1_where_x < grid_width - 1) { j += 1; }
                  break;

               case uint_c( 0x0000000000415b1b):  // up
                  if (grid_1_where_y < grid_height - 1) { j += grid_width; }
                  break;

               case uint_c( 0x0000000000445b1b):  // left
                  if (0 < grid_1_where_x) { j -= 1; }
                  break;

               case uint_c( 0x0000000000425b1b):  // down
                  if (0 < grid_1_where_y) { j -= grid_width; }
                  break;

               default:
                  ;
            }
            break;

         case 1:
            h = (k & 0xffffffffffffffcf) - 'A';
            move(control_panel_x, control_panel_0_y); print_char('A' + h); print_char(0x20); print_char(0x20);
            if (! int_within(0, h, grid_height - 1)) { move(control_panel_x, control_panel_0_y); refresh(); break; }

            move(control_panel_x + 1, control_panel_0_y); print_char(k_ellipsis); whence(); refresh();
            j = (grid_height - 1 - h) * grid_width;
            g = 2; break;

         case 2:
            k = getk();
            if (! int_within('0', k, '9')) { break; }

            move(control_panel_x + 1, control_panel_0_y); print_char(k);
            h = (k - '0') * 10;
            if (! int_within(0, h, (grid_width + 9) / 10 * 10 - 1)) { move(control_panel_x + 1, control_panel_0_y); refresh(); break; }

            move(control_panel_x + 2, control_panel_0_y); print_char(k_ellipsis); whence(); refresh();
            g = 3; break;

         case 3:
            k = getk();
            if (! int_within('0', k, '9')) { break; }

            move(control_panel_x + 2, control_panel_0_y); print_char(k);
            u = k - '0' - 1;
            while (! int_within(0, u, grid_width - 1)) {
               refresh();
               move(control_panel_x + 2, control_panel_0_y); print_char(k);
               k = getk();
               u = k - '0' - 1;
            }
            h += u;
            j += h;
            g = 0; break;

         default:
            error_flag = true; r->p = end_game; return r;
      }
   }
Ll_Exit:
   grid_1_whence_x = grid_1_where_x; grid_1_whence_y = grid_1_where_y;
   grid_1[j] |= 0x01;
   if (!( u == 0x00)) {
      if      ((u & 0x02) == 0x02) { e = 0; }
      else if ((u & 0x04) == 0x04) { e = 1; }
      else if ((u & 0x08) == 0x08) { e = 2; }
      else if ((u & 0x10) == 0x10) { e = 3; }
      else if ((u & 0x20) == 0x20) { e = 4; }
      ship_length_1[e] -= 1;
      print_char(k_x_cross); refresh();
      if (0 < ship_length_1[e]) {
         if (ship_length_e[e] - 2 < ship_length_1[e]) {
            n = 0;
            if (grid_1_where_x < grid_width - 1) {
               j = grid_1_where_y * grid_width + grid_1_where_x + 1;
               if ((grid_1[j] & 0x01) == 0) { pending_1 [e * 5 + 1 + n] = j; n += 1; }
            }
            if (grid_1_where_y < grid_height - 1) {
               j = (grid_1_where_y + 1) * grid_width + grid_1_where_x;
               if ((grid_1[j] & 0x01) == 0) { pending_1 [e * 5 + 1 + n] = j; n += 1; }
            }
            if (0 < grid_1_where_x) {
               j = grid_1_where_y * grid_width + grid_1_where_x - 1;
               if ((grid_1[j] & 0x01) == 0) { pending_1 [e * 5 + 1 + n] = j; n += 1; }
            }
            if (0 < grid_1_where_y) {
               j = (grid_1_where_y - 1) * grid_width + grid_1_where_x;
               if ((grid_1[j] & 0x01) == 0) { pending_1 [e * 5 + 1 + n] = j; n += 1; }
            }
            pending_1 [e * 5] = n;
            move(score_panel_label_x - 2, score_panel_1_y + e * 4); print_char(k_x_cross); refresh();
         }
         else {
            if (grid_1_where_x < grid_width - 1 && grid_1 [j + 1] == grid_1[j] || 0 < grid_1_where_x && grid_1 [j - 1] == grid_1[j]) {
               make_guess_x(grid_1, grid_1_where_x, grid_1_where_y, & pending_1 [e * 5]);
            }
            else if (grid_1_where_y < grid_height - 1 && grid_1 [j + grid_width] == grid_1[j] || 0 < grid_1_where_y && grid_1 [j - grid_width]
   ↪ == grid_1[j]) {
               make_guess_y(grid_1, grid_1_where_x, grid_1_where_y, & pending_1 [e * 5]);
            }
         }
         move(score_panel_label_x - 2, score_panel_1_y + e * 4 - 1); print_char(0x30 + ship_length_1[e]); refresh();
      }
      else {
         pending_1 [e * 5] = 0;
         move(score_panel_label_x - 2, score_panel_1_y + e * 4 - 1); print_char(0x20);
         move(score_panel_label_x - 2, score_panel_1_y + e * 4); print_char(k_skull); refresh();
         e = 0;
         while (e < n_ships) {
            if (0 < ship_length_1[e]) {
               move(control_panel_x + 3, control_panel_0_y); refresh(); return r;

            }
            e += 1;
         }
         r->p = end_game;
      }
   }
   else {
      print_char(k_bullet); refresh();
   }
   move(control_panel_x + 3, control_panel_0_y); refresh();
   return r;

}

static vterm_pass_t
shoot_1 (vterm_pass_t r)
{
   int_t e; int_t j; int_t n; uchar_t u; int_t x; int_t y;

   r->p = shoot_0;

   e = 0;
   while (e < n_ships) {
      while (0 < pending_0 [e * 5]) {
         j = use_guess(& pending_0 [e * 5]); u = grid_0[j];
         if ((u & 0x01) == 0) { goto Ll1_Exit; }

      }
      e += 1;
   }
Ll1_Exit:
   if (e == n_ships) {
      n = grid_width * grid_height; j = rand() % n;
      while (true) {
         u = grid_0[j]; x = j % grid_width; y = j / grid_width;
         if ((u & 0x01) == 0 && (
               x < grid_width - 1  && (grid_0 [j + 1]          & 0x01) == 0
            || y < grid_height - 1 && (grid_0 [j + grid_width] & 0x01) == 0
            || 0 < x               && (grid_0 [j - 1]          & 0x01) == 0
            || 0 < y               && (grid_0 [j - grid_width] & 0x01) == 0)) { goto Ll2_Exit; }

         j += 1; j %= n;
      }
   Ll2_Exit:
      ;
   }

   x = j % grid_width; y = j / grid_width;
   move(control_panel_x, control_panel_1_y);
   print_char(0x41 + (grid_height - 1 - y));
   print_char(0x30 + (x + 1) / 10); print_char(0x30 + (x + 1) % 10); refresh();
   move(board_x + 1 + x * 4, board_0_y + y * 2);

   grid_0[j] |= 0x01;
   if (!( u == 0x00)) {
      if      ((u & 0x02) == 0x02) { e = 0; }
      else if ((u & 0x04) == 0x04) { e = 1; }
      else if ((u & 0x08) == 0x08) { e = 2; }
      else if ((u & 0x10) == 0x10) { e = 3; }
      else if ((u & 0x20) == 0x20) { e = 4; }
      print_char(k_x_cross); refresh();
      ship_length_0[e] -= 1;
      if (0 < ship_length_0[e]) {
         if (ship_length_e[e] - 2 < ship_length_0[e]) {
            n = 0;
            if (x < grid_width - 1) {
               j = y * grid_width + x + 1;
               if ((grid_0[j] & 0x01) == 0) { pending_0 [e * 5 + 1 + n] = j; n += 1; }
            }
            if (y < grid_height - 1) {
               j = (y + 1) * grid_width + x;
               if ((grid_0[j] & 0x01) == 0) { pending_0 [e * 5 + 1 + n] = j; n += 1; }
            }
            if (0 < x) {
               j = y * grid_width + x - 1;
               if ((grid_0[j] & 0x01) == 0) { pending_0 [e * 5 + 1 + n] = j; n += 1; }
            }
            if (0 < y) {
               j = (y - 1) * grid_width + x;
               if ((grid_0[j] & 0x01) == 0) { pending_0 [e * 5 + 1 + n] = j; n += 1; }
            }
            pending_0 [e * 5] = n;
            move(score_panel_label_x - 2, score_panel_0_y + e * 4); print_char(k_x_cross); refresh();
         }
         else {
            if (x < grid_width - 1 && grid_0 [j + 1] == grid_0[j] || 0 < x && grid_0 [j - 1] == grid_0[j]) {
               make_guess_x(grid_0, x, y, & pending_0 [e * 5]);
            }
            else if (y < grid_height - 1 && grid_0 [j + grid_width] == grid_0[j] || 0 < y && grid_0 [j - grid_width] == grid_0[j]) {
               make_guess_y(grid_0, x, y, & pending_0 [e * 5]);
            }
         }
         move(score_panel_label_x - 2, score_panel_0_y + e * 4 - 1); print_char(0x30 + ship_length_0[e]); refresh();
      }
      else {
         pending_0 [e * 5] = 0;
         move(score_panel_label_x - 2, score_panel_0_y + e * 4 - 1); print_char(0x20);
         move(score_panel_label_x - 2, score_panel_0_y + e * 4); print_char(k_skull); refresh();
         e = 0;
         while (e < n_ships) {
            if (0 < ship_length_0[e]) {
               move(control_panel_x + 3, control_panel_1_y); refresh(); return r;

            }
            e += 1;
         }
         r->p = end_game;
      }
   }
   else {
      print_char(k_bullet); refresh();
   }
   move(control_panel_x + 3, control_panel_1_y); refresh();
   return r;

}

static vterm_pass_t
end_game (vterm_pass_t r)
{
   int_t x; int_t y;

   x = board_x + 1 + grid_1_where_x * 4; y = board_1_y + grid_1_where_y * 2;
   move(x + 1, y); print_char(0x20); move(x - 1, y); print_char(0x20); refresh();
   move(control_panel_x, control_panel_0_y); print_char(0x20); print_char(0x20); print_char(0x20);
   move(control_panel_x, control_panel_0_y);
   refresh();
   free(r);
   return NULL;

}

int
main (int argc, char * argv [])
{

   error_flag = false;
   if (1 < argc) {
      if (argc < 3 && argv[1][0] == '-' && argv[1][1] == 'q') {
         fputs("\n", stdout);
         fputs("      You, as the game user, can see your fleet; and the steering in the grid of your opponent.\n", stdout);
         fputs("\n", stdout);
         fputs("      You can use the cursor keys to move around.  Instead, you can enter one letter plus two digits from the control panel.\n",
   ↪ stdout);
         fputs("\n", stdout);
         fputs("      This can be made undone with control-u.\n", stdout);
         fputs("\n", stdout);
         fputs("      Type the return key each time to commit your next move; i.e. trigger a shot.  Instead, you can type tab, if you want it
   ↪ be chosen randomly; in the way it is done to determine the next move of the other.\n", stdout);
         fputs("\n", stdout);
         fputs("      The results are shown in the score panel.  The game is won by the player who has still ships left when the other has
   ↪ none.\n", stdout);
         fputs("\n", stdout);
         fputs("      The game can be quit with control-d; instead of being played on to the end.\n", stdout);
      }
      else {
      // error_flag = true;
         fprintf(stderr, "\nUsage: ./maneuver_vterm [-q]\n");
      }
      goto Exit_M1;

   }
   srand(rseed());
   run_vterm(start_game);

   close_vterm();
Exit_M1:
   printf("\n");
   if (error_flag) { fprintf(stderr, "\n** Ouch! \xe2\x98\xa0 : %s\n\n", s_error); exit(EXIT_FAILURE); }

   return EXIT_SUCCESS;

}

// maneuver_vterm.c
% cmr maneuver_vterm chars vterm

                 │ 01│ 02│ 03│ 04│ 05│ 06│ 07│ 08│ 09│ 10│ 11│ 12│ 13│
                ─┼───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┼─
                A│   │ • │ • │ • │ • │ • │ ✕ │ ✕ │ ✕ │ • │ • │ • │   │A
 ☠ Patrol boat  ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                B│ • │ • │ • │ • │   │ • │   │ • │ • │ • │   │ • │ • │B
                ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                C│   │   │   │   │ • │ • │ ✕ │ ✕ │ ✕ │ ✕ │ ✕ │ • │ • │C
 ☠ Submarine    ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                D│   │   │   │ • │   │ • │ • │ • │   │   │   │   │   │D
                ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                E│   │   │   │   │ • │   │ • │ ✕ │   │   │   │   │ • │E
 ☠ Destroyer    ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                F│ • │   │   │   │ ✕ │   │   │ ✕ │   │   │   │ • │ • │F
                ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                G│   │   │ • │   │ ✕ │   │ • │ • │ • │   │   │   │   │G
 ☠ Battleship   ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                H│ • │ • │ • │ • │ ✕ │ • │   │ • │ • │ • │ • │ • │ • │H
                ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                I│ ✕ │ ✕ │ ✕ │ ✕ │ • │ • │ • │ • │ • │ • │ • │ • │ • │I
 ☠ Carrier      ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                J│ • │ • │ • │   │ • │   │ • │ • │ • │ • │   │ • │ • │J
                ─┼───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┼─
        [     ]  │ 01│ 02│ 03│ 04│ 05│ 06│ 07│ 08│ 09│ 10│ 11│ 12│ 13│
                ─┼───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┼─
                A│ • │   │ • │ • │ • │   │ • │ ✕ │ ✕ │ ✕ │ ✕ │ ✕ │ • │A
 ✓ Patrol boat  ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
 2              B│ • │ • │   │   │ • │ • │   │   │   │ • │   │ • │ • │B
                ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                C│ • │   │ • │ • │ • │ ✕ │ ✕ │ ✕ │ ✕ │ • │ • │ • │   │C
 ☠ Submarine    ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                D│   │ • │   │   │   │ • │ • │   │   │   │ • │ • │ • │D
                ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                E│ • │ • │ • │   │ • │   │   │ • │ • │ • │   │ • │ • │E
 ☠ Destroyer    ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                F│   │   │   │   │ • │ • │ • │ • │ • │ • │ • │ • │ • │F
                ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                G│   │   │   │   │   │ • │ • │   │ • │ ✓ │ ✓ │   │   │G
 ☠ Battleship   ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                H│ • │ • │ • │ • │ • │   │ • │   │ • │   │ • │ • │ • │H
                ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                I│   │   │ • │ • │ ✕ │ ✕ │ ✕ │ • │ • │ • │ • │ • │   │I
 ☠ Carrier      ─┤ ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─   ─ ├─
                J│ ✕ │ ✕ │ ✕ │ • │   │ • │ • │   │ • │   │   │   │   │J
                ─┼───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┼─
        [     ]  │ 01│ 02│ 03│ 04│ 05│ 06│ 07│ 08│ 09│ 10│ 11│ 12│ 13│
%