

1対１の通信追跡ゲーム（鬼ごっこ）を開発しています。tagServer.c, tagClient.c, tagGame.c を用いて tagClient,
tagServer の実行ファイルを作成し行っています。

現在鬼ごっこにおいての追いかける機能は実装できましたが、二つの位置情報が同位置になった場合、つまり鬼が他人を捕まえたとき、ゲームを終了させるという機能の実装に困っています。

以下のtagGame.cソースコードの中でどこをいじればいいか教えてほしいです。

[code]

    #include <stdlib.h>
    #include <string.h>
    #include <signal.h>
    
    #include "tagGame.h"           // 鬼ごっこモジュールヘッダファイル
    
    #define MAINWIN_LINES   20     // メインウィンドウの高さ(行数)
    #define MAINWIN_COLUMS  40     // メインウィンドウの横幅(桁数)
    #define MAINWIN_SX      2      // メインウィンドウの左上座標
    #define MAINWIN_SY      1      // メインウィンドウの左上座標
    
    #define MOVE_UP         'i'    // 上に移動するキー
    #define MOVE_LEFT       'j'    // 左に移動するキー
    #define MOVE_DOWN       'k'    // 下に移動するキー
    #define MOVE_RIGHT      'l'    // 右に移動するキー
    
    // サーバーが送信するメッセージの最大長さ
    // 自分の座標情報(8byte) + 相手の座標情報(8byte) + '\0'
    #define SERVER_MSG_LEN   (8 + 8 + 1)
    
    // クライアントが送信するメッセージの最大長さ
    // 押しているキー情報 + '\0'
    #define CLIENT_MSG_LEN   (4 + 1)
    
    //--------------------------------------------------------------------
    //  鬼ごっこゲームモジュール内部で使用する構造体の定義
    //--------------------------------------------------------------------
    
    // サーバーに届く入力データ
    typedef struct {
      int myKey;                   // ユーザが押しているキー
      int itKey;                   // 相手の押しているキー(クライアントから届く)
      int quit;                    // ゲームを終了させるメッセージが届いた場合に TRUE
    } ServerInputData;
    
    // クライアントに届く入力データ
    typedef struct {
      int myKey;                   // ユーザが押しているキー
      int myX;                     // 自分の X 座標(サーバーから届く)
      int myY;                     // 自分の Y 座標(サーバーから届く)
      int itX;                     // 相手の X 座標(サーバーから届く)
      int itY;                     // 相手の Y 座標(サーバーから届く)
      int quit;                    // ゲームを終了させるメッセージが届いた場合に TRUE
    } ClientInputData;
    
    //--------------------------------------------------------------------
    //  鬼ごっこゲームモジュール内部で使用する関数のプロトタイプ宣言
    //--------------------------------------------------------------------
    static void getServerInputData(TagGame *game, ServerInputData *serverData);
    static void getClientInputData(TagGame *game, ClientInputData *clientData);
    static void updatePlayerStatus(TagGame *game, ServerInputData *serverData);
    static void copyGameState(TagGame *game, ClientInputData *clientData);
    static void printGame(TagGame *game);
    static void sendGameInfo(TagGame *game);
    static void sendMyPressedKey(TagGame *game, ClientInputData *clietData);
    static void die();
    
    
    //--------------------------------------------------------------------
    //  外部に公開する関数の定義
    //--------------------------------------------------------------------
    
    /*
     * 鬼ごっこゲームの初期化
     * 引数 :
     *   myChara - 自分を表すキャラクタ
     *   mySX    - 自分の開始 X 座標
     *   mySY    - 自分の開始 Y 座標
     *   itChara - 相手を表すキャラクタ
     *   itSX    - 相手の開始 X 座標
     *   itSY    - 相手の開始 Y 座標
     * 戻値 :
     *   鬼ごっこゲームオブジェクトへのポインタ
     */
    TagGame* initTagGame(char myChara, int mySX, int mySY,
                         char itChara, int itSX, int itSY)
    {
      TagGame* game = (TagGame *)malloc(sizeof(TagGame));
    
      // すべてのメンバを 0 で初期化
      bzero(game, sizeof(TagGame));
    
      //
      // ゲームの論理的データの初期化
      //
      game->my.chara = myChara;
      game->my.x     = mySX;
      game->my.y     = mySY;
      game->it.chara = itChara;
      game->it.x     = itSX;
      game->it.y     = itSY;
    
      // 前回のプレイヤー情報を初期化(現在のプレイヤー情報と同じにする)
      memcpy(&game->preMy, &game->my, sizeof(Player));
      memcpy(&game->preIt, &game->it, sizeof(Player));
    
      //
      // 画面の初期化
      //
      initscr();               // curses ライブラリの初期化
      signal(SIGINT, die);     // Ctrl-C 時に端末を復旧する関数 die を登録
      signal(SIGTERM, die);    // kill 時に端末を復旧する関数 die を登録
      noecho();                // エコーバックの中止
      cbreak();                // キーボードバッファリングの中止
    
      // ゲーム画面の作成
      game->mainWin = newwin(MAINWIN_LINES, MAINWIN_COLUMS, MAINWIN_SY, MAINWIN_SX);
      
      // 画面が小さい場合
      if (game->mainWin == NULL) {
        endwin();
        fprintf(stderr, "Error: terminal size is too small\n");
        exit(1);
      }
    
      // 各制御キーはエスケープシーケンスで表現されている
      // それを論理キーコードに変換する
      keypad(game->mainWin, TRUE);
    
      return game;
    }
    
    /*
     * 鬼ごっこゲームの準備
     * 引数 :
     *   game - 鬼ごっこゲームオブジェクトへのポインタ
     *   s    - 相手との会話用ファイルデスクリプタ
     */
    void setupTagGame(TagGame *game, int s)
    {
      //
      // データ入力のための準備
      //
      game->s = s;                    // 相手との会話用ファイルデスクリプタを登録
      FD_ZERO(&(game->fdset));        // 入力を監視するファイルデスクリプタマスクを初期化
      FD_SET(0, &(game->fdset));      // 標準入力(キーボード)を監視する
      FD_SET(s, &(game->fdset));      // 相手との会話用デスクリプタを監視する
      game->fdsetWidth = s + 1;       // fdset のビット幅(=最大デスクリプタ番号＋１)
      game->watchTime.tv_sec  = 0;    // 監視時間を 100 msec にセット
      game->watchTime.tv_usec = 100 * 1000;
    
      //
      // 画面の準備
      //
    
      // 画面の静的要素の描画
      box(game->mainWin, ACS_VLINE, ACS_HLINE);
    
      // 物理画面に描画
      wrefresh(game->mainWin);
    }
    
    /*
     * サーバー側鬼ごっこゲームの開始
     * 引数 :
     *   game - 鬼ごっこゲームオブジェクトへのポインタ
     */
    void playServerTagGame(TagGame *game)
    {
      ServerInputData serverData;
    
      while (1) {
        
        // ユーザのキー入力と相手から届いたキー入力データを読む
        getServerInputData(game, &serverData);
    
        // ユーザもしくは相手から終了のメッセージが届いた場合,終了する
        if (serverData.quit)
          break;
    
        // プレイヤーの状態を更新する
        updatePlayerStatus(game, &serverData);
    
        // 表示する
        printGame(game);
    
        // ゲームの状態を相手に知らせる
        sendGameInfo(game);
      }
    
      // 相手も終了するようメッセージを送る
      write(game->s, "quit", 5);  
    }
    
    
    /*
     * クライアント側鬼ごっこゲームの開始
     * 引数 :
     *   game - 鬼ごっこゲームオブジェクトへのポインタ
     */
    void playClientTagGame(TagGame *game)
    {
      ClientInputData clientData;
    
      while (1) {
        
        // ユーザのキー入力と, 相手から届いたゲームの状態を読む
        getClientInputData(game, &clientData);
    
        // ユーザもしくは相手から終了のメッセージが届いた場合,終了する
        if (clientData.quit)
          break;
    
        // ゲームの状態を更新する
        copyGameState(game, &clientData);
    
        // 表示する
        printGame(game);
    
        // 自分の押しているキーを相手に送る
        sendMyPressedKey(game, &clientData);
      }
    
      // 相手も終了するようメッセージを送る
      write(game->s, "quit", 5);  
    }
    
    /*
     * 鬼ごっこゲームの後始末
     * 引数 :
     *   game - 鬼ごっこゲームオブジェクトへのポインタ
     */
    void destroyTagGame(TagGame *game)
    {
      // ウィンドウを廃棄
      delwin(game->mainWin);
      // ファイルデスクリプタを閉じる
      close(game->s);
      // オブジェクトを解放する
      free(game);
      // 端末を元に戻して終了
      die();
    }
    
    
    //--------------------------------------------------------------------
    //  外部に公開しない関数の定義
    //--------------------------------------------------------------------
    
    /*
     * サーバー側: データが届いているファイルデスクリプタからデータを読む
     * 引数 :
     *   game       - 鬼ごっこゲームオブジェクトへのポインタ
     *   serverData - 届いたデータを格納する ServerInputData 構造体へのポインタ(出力)
     */
    static void getServerInputData(TagGame *game, ServerInputData *serverData)
    {
      fd_set  arrived   = game->fdset;        // データが届いたファイルデスクリプタの集合
      TimeVal watchTime = game->watchTime;    // ファイルデスクリプタの監視時間
      char    msg[CLIENT_MSG_LEN];            // 相手から届いたメッセージ
    
      // すべてのメンバを０で初期化
      // データが届いていない場合, メンバの値は０
      bzero(serverData, sizeof(ServerInputData));
    
      //
      // データが届いているファイルデスクリプタを調べる
      //
      select(game->fdsetWidth, &arrived, NULL, NULL, &watchTime);
      
      //
      // 標準入力 (キーボード, ０番) にデータが届いている場合
      //
      if (FD_ISSET(0, &arrived)) {
        serverData->myKey = wgetch(game->mainWin);    // 押されているキーを読み取る
        // 終了するかどうかチェック
        if (serverData->myKey == 'q')
          serverData->quit = TRUE;
      }
    
      //
      // 相手との会話用ファイルデスクリプタにデータが届いている場合
      //
      if (FD_ISSET(game->s, &arrived)) {
        read(game->s, msg, CLIENT_MSG_LEN);   // メッセージを読み取る
        
        // 終了するかどうかチェック
        if (strcmp(msg, "quit") == 0)
          serverData->quit = TRUE;
        // 届いたメッセージから押下情報を抽出
        else 
          sscanf(msg, "%3d ", &serverData->itKey);
      }
    
      // すでにデータが届いていた場合, select() は直ちに終了する
      // そこで余った監視時間だけ休止する(Linux でのみ有効)
      usleep(watchTime.tv_usec); 
      // 休止中にたまったキー入力をクリア
      if (serverData->myKey != 0)  
        flushinp(); 
    }
    
    /*
     * クライアント側: データが届いているファイルデスクリプタからデータを読む
     * 引数 :
     *   game       - 鬼ごっこゲームオブジェクトへのポインタ
     *   clientData - 届いたデータを格納する ClientInputData 構造体へのポインタ(出力)
     */
    static void getClientInputData(TagGame *game, ClientInputData *clientData)
    {
      fd_set  arrived   = game->fdset;        // データが届いたファイルデスクリプタの集合
      TimeVal watchTime = game->watchTime;    // ファイルデスクリプタの監視時間
      char    msg[SERVER_MSG_LEN];            // 相手から届いたメッセージ
    
      // すべてのメンバを０で初期化
      // データが届いていない場合, メンバの値は０
      bzero(clientData, sizeof(ClientInputData));
    
      //
      // データが届いているファイルデスクリプタを調べる
      //
      select(game->fdsetWidth, &arrived, NULL, NULL, &watchTime);
      
      //
      // 標準入力 (キーボード, ０番) にデータが届いている場合
      //
      if (FD_ISSET(0, &arrived)) {
        clientData->myKey = wgetch(game->mainWin);    // 押されているキーを読み取る
        // 終了するかどうかチェック
        if (clientData->myKey == 'q')
          clientData->quit = TRUE;
      }
    
      //
      // 相手との会話用ファイルデスクリプタにデータが届いている場合
      //
      if (FD_ISSET(game->s, &arrived)) {
        read(game->s, msg, SERVER_MSG_LEN);          // メッセージを読み取る
        lseek(game->s, 0, SEEK_END);
    
        // 終了するかどうかチェック
        if (strcmp(msg, "quit") == 0)
          clientData->quit = TRUE;
        // 届いたメッセージから座標を抽出
        else {
          // 自分と相手の座標情報を抽出
          sscanf(msg, "%3d %3d %3d %3d ", &clientData->itX, &clientData->itY, 
                                          &clientData->myX, &clientData->myY);
        }
      }
    
      // すでにデータが届いていた場合, select() は直ちに終了する
      // そこで余った監視時間だけ休止する(Linux でのみ有効)
      usleep(watchTime.tv_usec); 
      // 休止中にたまったキー入力をクリア
      if (clientData->myKey != 0)  
        flushinp(); 
    }
    
    /*
     * プレイヤーの状態を更新する
     * 引数 :
     *   game       - 鬼ごっこゲームオブジェクトへのポインタ
     *   serverData - 鬼ごっこゲームに対する入力データ
     */
    static void updatePlayerStatus(TagGame *game, ServerInputData *serverData)
    {
      Player *my = &game->my;    // ショートカット
      Player *it = &game->it;    // ショートカット
    
      // 前回のプレイヤー情報を保存
      memcpy(&game->preMy, &game->my, sizeof(Player));
      memcpy(&game->preIt, &game->it, sizeof(Player));
      
      // キーに応じて処理
      switch (serverData->myKey) {
      case KEY_UP: 
      case MOVE_UP: 
        if (my->y > 1) my->y--;
        break;
    
      case KEY_DOWN: 
      case MOVE_DOWN:
        if (my->y < MAINWIN_LINES - 2) my->y++;
        break;
    
      case KEY_LEFT: 
      case MOVE_LEFT:
        if (my->x > 1) my->x--;
        break;
    
      case KEY_RIGHT: 
      case MOVE_RIGHT:
        if (my->x < MAINWIN_COLUMS - 2) my->x++;
        break;
      }
    
      // キーに応じて処理
      switch (serverData->itKey) {
      case KEY_UP: 
      case MOVE_UP: 
        if (it->y > 1) it->y--;
        break;
    
      case KEY_DOWN: 
      case MOVE_DOWN:
        if (it->y < MAINWIN_LINES - 2) it->y++;
        break;
    
      case KEY_LEFT: 
      case MOVE_LEFT:
        if (it->x > 1) it->x--;
        break;
    
      case KEY_RIGHT: 
      case MOVE_RIGHT:
        if (it->x < MAINWIN_COLUMS - 2) it->x++;
        break;
      }
    }
    
    /*
     * ゲームの状態を更新する
     * 引数 :
     *   game       - 鬼ごっこゲームオブジェクトへのポインタ
     *   clientData - 鬼ごっこゲームに対する入力データ
     */
    static void copyGameState(TagGame *game, ClientInputData *clientData)
    {
      Player *my = &game->my;    // ショートカット
      Player *it = &game->it;    // ショートカット
    
      // データが届いていなければ, 何もする必要はない
      if (clientData->myX == 0) 
        return; 
    
      // 前回のプレイヤー情報を保存
      memcpy(&game->preMy, &game->my, sizeof(Player));
      memcpy(&game->preIt, &game->it, sizeof(Player));
      
      // 位置を更新
      my->x = clientData->myX;
      my->y = clientData->myY;
      it->x = clientData->itX;
      it->y = clientData->itY;
    }
    
    /*
     * ゲーム画面を表示する
     * 引数 :
     *   game - 鬼ごっこゲームオブジェクトへのポインタ
     */
    static void printGame(TagGame *game)
    {
      WINDOW *mw    = game->mainWin;    // ショートカット
      Player *my    = &game->my;        // ショートカット
      Player *preMy = &game->preMy;     // ショートカット
      Player *it    = &game->it;        // ショートカット
      Player *preIt = &game->preIt;     // ショートカット
    
      // 相手の描画 (もし自分と重なった場合, 自分を上に描画したいので相手が先)
      mvwaddch(mw, preIt->y, preIt->x, ' ');    // 消去
      mvwaddch(mw, it->y, it->x, it->chara);    // 表示
    
      // 自分の描画
      mvwaddch(mw, preMy->y, preMy->x, ' ');    // 消去
      mvwaddch(mw, my->y, my->x, my->chara);    // 表示
    
      // 物理画面へ描画
      wrefresh(mw);
    }
    
    /*
     * ゲームの状態を相手に知らせる
     * 引数 :
     *   game - 鬼ごっこゲームオブジェクトへのポインタ
     */
    static void sendGameInfo(TagGame *game)
    {
      Player *my = &game->my;        // ショートカット
      Player *it = &game->it;        // ショートカット
      char    msg[SERVER_MSG_LEN];   // 相手に送るメッセージ
    
      // 自分の情報が変化していなければ送る必要はない
      if (memcmp(&game->my, &game->preMy, sizeof(Player)) == 0 &&
          memcmp(&game->it, &game->preIt, sizeof(Player)) == 0)
        return;
    
      //
      // 位置座標をメッセージに変換
      //
    
      // プレイヤーの座標情報
      sprintf(msg, "%3d %3d %3d %3d", my->x, my->y, it->x, it->y);
    
      // 送る
      write(game->s, msg, SERVER_MSG_LEN);    
    }
    
    /*
     * 自分の押しているキーを相手に送る
     * 引数 :
     *   game      - 鬼ごっこゲームオブジェクトへのポインタ
     *   clietData - クライアントに対する入力データ
     */
    static void sendMyPressedKey(TagGame *game, ClientInputData *clietData)
    {
      char    msg[CLIENT_MSG_LEN];   // 相手に送るメッセージ
    
      // 何も押されていなければ送る必要はない
      if (clietData->myKey == 0)
        return;
    
      //
      // 位置座標をメッセージに変換
      //
    
      // プレイヤーの座標情報
      sprintf(msg, "%d", clietData->myKey);
    
      // 送る
      write(game->s, msg, CLIENT_MSG_LEN);    
    }
    
    /*
     * 端末を復元し終了する
     */
    static void die()
    {
      endwin();
      exit(1);
    }
    
[/code]


CC BY-SA 4.0
2021-01-08T02:53:12.793
73150
2021-01-08T04:43:40.947
2021-01-08T04:43:40.947
3060
43459
question
c game-development
1対１の通信追跡ゲーム（鬼ごっこ）の実装