現在進行中のまだ煮詰まっていないゲーム、"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ベースのウィンドウプログラムへ移行することにします。