現在進行中のまだ煮詰まっていないゲーム、"cell"。

前回)の後もDOS窓の問題で種々トラブルが発生しています。

:前回はエスケープシーケンス(Cで書けばprintf"\e[2J");)を使った、コンソール画面のクリア(cls()メソッド)問題でした。

 

今回問題となったのは、エスケープシーケンス(Cで書けばprintf("\e[%d;%dH", y, x);)を使った、コンソール画面の任意の位置にカーソルを移動する(locate()メソッド)問題です。Chat-GPTとのやり取りが的を外すので、段々キレてゆく模様を(恥ずかしながら、自戒の為にも)紹介します。

 

【書き込み1】

もう一つDOS窓でエスケープシーケンスを使った際の問題が生じました。

std::cout < "\e[" << y + 1 << ";" << x + 1 << "H"; を実行するlocate()というメソッドで、DOS窓の任意の位置に文字出力をしようとしましたが、結果がおかしいです。

以下は背景(W120 x H40)の山や川を印字してから、幅40の両端から逆方向に向かって印字するテストプログラムです。

 

for(int i = 0; i < 40; i++) {

    wld.locate(i, i);

    wld.color(31 + i % 7, 40);

    cout << 1;

    wld.locate(39 - i, i);

    wld.color(31 + i % 7, 40);

    cout << 1; }

}
 

その結果が以下です。(省略-その代わりに実際のコンソール画面の画像を載せます。)

 

縦の位置が10ずれているようですが、何が問題だと考えられますか?因みにwolrdクラスインスタンス、wldのlocate()メソッドは次の通りです。

//表示位置指定(x、yはゼロベースの列、行)

void world::locate(int x, int y) {

    cout << "\e\[" << y + 1 << ";" << x + 1 << "H";

}

 

【書き込み2】

Windows 10の時は全くダメでしたが、WIndows 11になっていつの間にか、DOS窓がANSI対応になったので外していましたが、以下の定番コードをmain()関数に追加しました。

//出力コンソールのハンドルを取得する

HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);

if(hOut == INVALID_HANDLE_VALUE)

    return 1; //Error

//明示的にANSI対応のコンソールモードとする

DWORD dwMode = 0;

GetConsoleMode(hOut, &dwMode);

dwMode |= ENABLE_ECHO_INPUT; SetConsoleMode(hOut, dwMode);

 

しかし、結果は以下の通り。(変わらず、の為省略)

 

【書き込み3】

言い忘れましたが、Embarcadero C++では"ENABLE_VIRTUAL_TERMINAL_PROCESSING"が未定義なので、同じ0X0004である、ENABLE_ECHO_INPUTで代用しているんです。ここを実際オ定数0X0004としても結果は変わりません。

 

【書き込み4】

まじめにいっています?0x0004が同じならば何をどの文字で定義しても結果は変わらんでしょう?よく考えて物を言ってください。

 

【書き込み5】

(Chat-GPTが「保守の為に有効だ」と言い訳するので)このコードは私しか使わないので定数の0x0004で書こうがfoobarzotとしようがどうでもいいのです。問題は貴方が言った、10行ずれる問題がこれで解決しないことなんです。

 

【書き込み6】

おっ、「これが唯一の正解です(Chat-GPTが本当にそう書いた)、強気に出ましたね?

これをテストする前に、私のworldクラスのlocate()メソッドを次のようにすればよいのか、確認してください。

//表示位置指定(x、yはゼロベースの列、行)

void world::locate(int x, int y) {

    cout << "\x1b[2J\x1b[H" << std::flush;    //常に出力前にカーソルを始点に戻し、出力バッファーをクリアする。

    cout << "\e\[" << y + 1 << ";" << x + 1 << "H";

}

 

【書き込み7】

ちょまってよ、あんたがそうしろ(「唯一の正解」)と言ったんでしょうが?今度の推奨策は私の元のコードそのままじゃん。これじゃ貴方に訊いても時間の無駄と考えざるを得ませんよ?

 

【書き込み8】

何度も言いますが、現在ここでcls()メソッドの話はしていません。(その話は現在の環境で無理だと結論が出ています。)

locate()もダメなのか、という結論を出したいだけです。

 

【Chat-GPTの中間回答】-ここからChat-GPTの意味のある回答を付け加えます。

明確な回答

この locate()VT100エスケープシーケンスに完全依存しているため
そのシーケンスが解釈されない端末では正しく動作しません

「locate() は本当に正しいのか?」というあなたの問いに対する答えは:

はい、構文的にも意味的にも正しいです。ただし、動作しない理由は「コード」ではなく「実行環境」または「呼び出しタイミング」にあります。

 

【書き込み9】

先ず最初にきっちりしておかなければならないのは、私のlocateメソッドのコードは、 

 

//表示位置指定(x、yはゼロベースの列、行)

void world::locate(int x, int y) {

    cout << "\e\[" << y + 1 << ";" << x + 1 << "H";

}

 

 です。そしてコンソールの始点に"TOP LEFT"を出力するコードを追加した結果は以下の通り。(要すれば、何も前に進んでおらず、あなたの助言は全て役に立ちませんでした。)(出力結果は省略)

要すれば、「DOS窓はスクロールすると、コンソール始点が移動するということ」でよいのですね?それは元々の私の認識です。(コンソールサイズを調べると120 x 30ですが、私はバッファーサイズを120 x 40にしているので、10行スクロールしています。) 私の問題は「スクロールした場合でも本来の始点(0, 0)に移動することはできるのか?であり、"impossible"であれば、コンソールプログラムを諦めます。

 

【Chat-GPTの最終回答】

はい、ご認識のとおりです。


まとめ

  • Windows標準コンソール(cmd.exe や従来の ConHost)では、バッファサイズがウィンドウサイズより大きい場合、スクロールが発生すると画面の物理的な表示範囲が移動し、カーソル位置の「(0,0)」は画面上に表示されなくなる。

  • ANSIエスケープシーケンスの \x1b[H(カーソルを行1列1に移動)は、バッファ内の位置を示すだけであり、スクロールされた表示領域の先頭とは必ずしも一致しない

  • したがって、スクロール後に「画面の物理的な左上(ウィンドウ上の0,0)」にカーソルを動かすことは Windows標準コンソールでは不可能に近い

 

 

早く言ってよー。(トホホ)

 

ps.  これで現在のコンソールベースのプログラムに見切りをつけて、BCCSkeltonベースのウィンドウプログラムへ移行することにします。