テキストエディタはC言語プログラミングに不可欠な道具です. 世の中には様々なエディタが存在しますが, この授業では,Unix の伝統的なエディタ「 vi 」の改良版「 Vim 」(Vi IMproved)を推奨します. Vim の操作方法を修得し,プログラミングの作業効率を高めましょう.
前回と同様に, 端末を開き,作業用ディレクトリを準備しよう:
$ cd # ホームディレクトリへ移動 $ mkdir c-0427 # 本日の作業用ディレクトリを作成 $ cd c-0427 # 作業用ディレクトリへ移動 # または $ cd !$ # !$ は直前のコマンドの引数 c-0427 に置き換わる
まず,反省文「I'm sorry」を 100回書け!! 一般人向けのエディタ gedit や nano では:
$ gedit sorry.txt & # または $ nano sorry.txt
I'm sorry. I'm sorry. I'm sorry. ...
gedit や nano でもコピー/カット・アンド・ペースト すれば多少は楽に実行できますが...
選択は [Shift]+カーソル, コピーは [Ctrl]+[C],ペーストは [Ctrl]+[V], 保存は [Ctrl]+[S],終了は [Ctrl]+[Q].
選択は [Shift]+カーソル, カットは [Ctrl]+[K],ペーストは [Ctrl]+[U], 保存は [Ctrl]+[O],終了は [Ctrl]+[X].
より,効率的な作業方法として,Vim を使ってみよう:
$ vim sorry.txt
まず,1行だけ入力しよう. 最初に [i] キーを押して挿入モードへ移ってから...:
I'm sorry. # 末尾で改行([Enter] or [Return] キー)
次に,カーソルキー(矢印キー)を使って,第1行へカーソルを移動し, 続けて [Esc][y][y][9][9][p] キーを操作しよう:
一瞬で,こうなるハズ:
I'm sorry. I'm sorry. I'm sorry. ... I'm sorry.
これで1行が 100行に簡単に増えた. 素晴らしい. もし,1,000行とか 1,000,000行にしたくても, 労力はほとんど変わらない.
そして最後に [:][w][q][Enter] キーで保存・終了となる.
Vim の操作方法について,自習用プログラム vimtutor を利用して学習しよう:
$ vimtutor # 日本語版 # または $ LANG=C vimtutor # English version
最低限,レッスン3までは終わらせよう.
次のコマンドは是非ともおぼえよう:
また,コマンドによっては,回数を指定して繰り返せたりもする. 例:[1][0][x] で 10文字削除.
端末内でキャラクタを上下左右に操作できるようなプログラムを開発しよう. C言語のソースファイルの編集には Vim を利用し,効率的に作業を進めよう.
ソースを編集:
$ vim game.c
#include <ncurses.h> // 端末制御に必要
// 制御対象は文字端末内の入出力装置(画面とキーボード)
int main(void)
{
int x = 10, y = 10; // キャラの位置(xは右方向,yは下方向)
initscr(); // 端末制御を開始
while (1) { // ひたすら繰り返す
move(y, x); // カーソル(表示位置)を移動
printw("(^o^)"); // キャラを表示
getch(); // キー入力を待つ
x++; y--; // キャラの位置を変更
}
endwin(); // 端末制御を終了
return (0);
}
コンパイルと実行:
$ cc game.c -lncurses -o game $ ./game
実行中に,何かキーを押すと,キャラが移動するよ.
しかし,移動方向は固定(右上だけ)だし, 画面からハミ出すと異常動作に陥ってしまう. また,画面にゴミ(キャラの跡,キーの文字)を残してしまうし, カーソルも目障りだ. 正常終了もできないので,[Ctrl]+[C] で強制終了しよう.
ソースを改造:
#include <ncurses.h>
int main(void)
{
int x = 10, y = 10;
int key; // 入力キーの文字が入る
initscr();
noecho(); // 入力キーは表示するなよ
curs_set(0); // カーソルは表示するなよ
while (1) {
erase(); // 画面全体をクリア(跡を残さない)
move(y, x);
printw("(^o^)");
key = getch(); // キー入力を待ち,文字も調べる
if (key == 'h') { x--; } // キーの文字に応じた方向へ移動
if (key == 'j') { y++; }
if (key == 'k') { y--; }
if (key == 'l') { x++; }
if (key == 'q') break; // [Q]キーで繰り返しを終了
// これら5個のほぼ同じ行を Vim で効率的に編集せよ.
// 1個は手動入力,他はコピペ(yy,4p)と置換(r や cw)等で
}
endwin();
return (0);
}
コンパイルして実行...
キャラを [h],[j],[k],[l] キーで上下左右に移動できるようになった.
画面からゴミは消えたし,[q] キーで正常終了も可能になった. しかしまだ,ハミ出すとダメ.
#include <ncurses.h>
int main(void)
{
int x = 10, y = 10;
int key;
int w, h; // 画面の幅と高さ(width,height)
char *face = "(^o^)"; // キャラの顔文字
int size = 5; // 顔文字の文字数
initscr();
noecho();
curs_set(0);
getmaxyx(stdscr, h, w); // 画面サイズを調べ,h と w に代入
while (1) {
erase();
move(y, x);
printw(face); // 顔文字は変数化したよ
key = getch();
if (key == 'h') { x--; }
if (key == 'j') { y++; }
if (key == 'k') { y--; }
if (key == 'l') { x++; }
if (key == 'q') break;
if (x < 0) x = 0; // ハミ出したら画面内に戻す
if (x + size >= w) x = w - size; // 「>=」は数学の不等号「≧」と同意
if (y < 0) y = 0;
if (y >= h) y = h - 1;
// これら4行も Vim で効率的に編集せよ
}
endwin();
return (0);
}
画面上の位置はゼロから数えることに注意しよう. 画面の幅が w なら,x = 0 〜 w-1 が画面内, x < 0 や x ≧ w は画面外となる.
#include <ncurses.h>
int main(void)
{
int x = 10, y = 10;
int key;
int w, h;
char *lt="d^ ))"; // 左向き顔(left)
char *rt="(( ^b"; // 右向き顔(right)
char *dn="(^o^)"; // 正面顔(下方向 down)
char *up="((*))"; // 後ろ頭(上方向 up)
// これら類似の4行も Vim で効率的に編集せよ
char *face = dn; // 最初は正面顔にしておく
int size = 5;
initscr();
noecho();
curs_set(0);
getmaxyx(stdscr, h, w);
while (1) {
erase();
move(y, x);
printw(face);
key = getch();
if (key == 'h') { x--; face = lt; } // 方向に応じて顔を変更
if (key == 'j') { y++; face = dn; }
if (key == 'k') { y--; face = up; }
if (key == 'l') { x++; face = rt; }
// これら類似の4行も Vim で...
if (key == 'q') break;
if (x < 0) x = 0;
if (x + size > w) x = w - size;
if (y < 0) y = 0;
if (y > h) y = h - 1;
}
endwin();
return (0);
}
ちょっとだけ面白くなった?
#include <ncurses.h>
#include <stdlib.h> // 乱数 rand() に必要
int main(void)
{
int x = 10, y = 10;
int key;
int w, h;
char *lt="d^ ))";
char *rt="(( ^b";
char *dn="(^o^)";
char *up="((*))";
char *face = dn;
int size = 5;
char *flag="__P__"; // 旗の絵文字
int f = 0; // 旗の有無
int fx, fy; // 旗の位置
initscr();
noecho();
curs_set(0);
getmaxyx(stdscr, h, w);
while (1) {
erase();
if (f == 0) { // 旗が無い場合...
fx = rand()%20; // ランダムな位置に...
fy = rand()%20;
f = 1; // 旗を立てる
}
move(fy, fx); printw(flag); // 旗を表示
move(y, x); printw(face);
key = getch();
switch (key) { // 処理の効率化,コードの適切化
case 'h': x--; face = lt; break;
case 'j': y++; face = dn; break;
case 'k': y--; face = up; break;
case 'l': x++; face = rt; break;
// これら4行も Vim で効率的に編集せよ
}
if (key == 'q') break;
if (x < 0) x = 0;
else if (x + size > w) x = w - size; // 処理の効率化
if (y < 0) y = 0;
else if (y > h) y = h - 1; // 同上
if ((x == fx) && (y == fy)) f = 0; // 旗をゲットしたら消す
}
endwin();
return (0);
}
さらにゲームっぽくなった?