本記事の構成および論理分析にはAI(人工知能)を使用しています。情報の正確性は、システム管理者(UNIXユーザー)による手動検証済みです。
【ベクトルで遊ぼう】C言語でマス目にひし形を描く|向きと大きさをコードで見てみる | UNIX Cafe

高校数学で最初に出てくる ベクトル は、少し難しく感じた方も多いのではないでしょうか。
でも、プログラムで考えるベクトルはかなりシンプルです。たとえば「右に1進む」「下に1進む」「左下に1進む」のように、どちらへ、どれだけ進むかを表すものとして見ることができます。
今回は、このベクトルを C言語 で遊びながら見ていきます。マス目の上に点を置き、移動する向きを変えながら、最後はひし形を描いてみます。
最後に、同じ考え方を Python で書くとどう表せるかも補足します。この記事ではC言語を主役にして、Pythonは見比べるための別表現として扱います。
ベクトルってなに?
ベクトルは、向きと大きさを持つ量です。
この記事では、難しい式から始めるのではなく、マス目の上の移動として考えます。
| ベクトル | 意味 |
|---|---|
(1, 0) | 右に1進む |
(0, 1) | 下に1進む |
(1, 1) | 右下に1進む |
(-1, 1) | 左下に1進む |
(1, -1) | 右上に1進む |
画面やマス目では、左上を (0, 0) として、右へ行くほど x が増え、下へ行くほど y が増える、と考えることが多いです。
まずは点を1つ動かす
最初に、1つの点をベクトルで動かしてみます。
x と y が現在の位置、dx と dy が移動する向きです。
vector_move.c という名前で、次のコードを作ってみます。
#include <stdio.h>
int main(void) {
int x = 2;
int y = 1;
int dx = 1;
int dy = 1;
for (int step = 0; step < 5; step++) {
printf("step %d: (%d, %d)\n", step, x, y);
x += dx;
y += dy;
}
return 0;
}main関数の読み方
dx = 1、dy = 1 なので、この点は毎回右下へ進みます。
x += dx; は、いまの x に dx を足す処理です。y += dy; も同じで、いまの y に dy を足します。
つまり、位置を更新する基本形は次の2行です。
x += dx;
y += dy;実行してみる
macOSやLinuxのターミナルなら、次のようにコンパイルして実行できます。
gcc -std=c99 vector_move.c -o vector_move
./vector_move実行結果は次のようになります。
step 0: (2, 1)
step 1: (3, 2)
step 2: (4, 3)
step 3: (5, 4)
step 4: (6, 5)x と y が、どちらも1ずつ増えているのがわかります。
マス目に点を置いてみる
次は、座標を文字のマス目として表示してみます。
ここでは、何もないマスを .、点があるマスを # で表します。
vector_point.c という名前で、次のコードを作ってみます。
#include <stdio.h>
#define SIZE 9
int main(void) {
int x = 4;
int y = 2;
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
if (col == x && row == y) {
printf("#");
} else {
printf(".");
}
}
printf("\n");
}
return 0;
}main関数の読み方
row は行、col は列です。外側の for 文で上から下へ進み、内側の for 文で左から右へ進みます。
col == x && row == y の場所だけ # を表示します。それ以外の場所は . を表示します。
実行してみる
gcc -std=c99 vector_point.c -o vector_point
./vector_point実行結果は次のようになります。
.........
.........
....#....
.........
.........
.........
.........
.........
.........左上から数えて、x = 4、y = 2 の場所に点が置かれました。
ベクトルで線を描く
点を1つ置けたので、今度はベクトルを使って線を描きます。
たとえば (1, 1) のベクトルを使うと、右下へ進む線になります。
vector_line.c という名前で、次のコードを作ってみます。
#include <stdio.h>
#define SIZE 9
int main(void) {
int start_x = 1;
int start_y = 1;
int dx = 1;
int dy = 1;
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
int x = start_x;
int y = start_y;
int found = 0;
for (int step = 0; step < 6; step++) {
if (col == x && row == y) {
found = 1;
}
x += dx;
y += dy;
}
if (found) {
printf("#");
} else {
printf(".");
}
}
printf("\n");
}
return 0;
}main関数の読み方
start_x と start_y は、線の始まりの場所です。dx と dy は、1回ごとに進む向きです。
内側の for (int step = 0; step < 6; step++) では、始点から6個ぶんの点を調べています。
dx = 1、dy = 1 なので、点は右下へ進みます。
実行してみる
gcc -std=c99 vector_line.c -o vector_line
./vector_line実行結果は次のようになります。
.........
.#.......
..#......
...#.....
....#....
.....#...
......#..
.........
.........斜めの線ができました。ただし、今回はこの線だけで終わらせず、複数のベクトルを組み合わせて図形を作ってみます。
4つのベクトルでひし形を描く
ひし形は、4本の斜め線を組み合わせると作れます。
| 辺 | 始点 | ベクトル | 向き |
|---|---|---|---|
| 左上の辺 | (4, 0) | (-1, 1) | 左下へ進む |
| 右上の辺 | (4, 0) | (1, 1) | 右下へ進む |
| 左下の辺 | (0, 4) | (1, 1) | 右下へ進む |
| 右下の辺 | (8, 4) | (-1, 1) | 左下へ進む |
この4本を同じマス目に置くと、ひし形になります。
vector_diamond.c という名前で、次のコードを作ってみます。
#include <stdio.h>
#define SIZE 9
void clear_grid(char grid[SIZE][SIZE]) {
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
grid[row][col] = '.';
}
}
}
void draw_line(char grid[SIZE][SIZE], int x, int y, int dx, int dy, int count) {
for (int step = 0; step < count; step++) {
if (0 <= x && x < SIZE && 0 <= y && y < SIZE) {
grid[y][x] = '#';
}
x += dx;
y += dy;
}
}
void print_grid(char grid[SIZE][SIZE]) {
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
printf("%c", grid[row][col]);
}
printf("\n");
}
}
int main(void) {
char grid[SIZE][SIZE];
clear_grid(grid);
draw_line(grid, 4, 0, -1, 1, 5);
draw_line(grid, 4, 0, 1, 1, 5);
draw_line(grid, 0, 4, 1, 1, 5);
draw_line(grid, 8, 4, -1, 1, 5);
print_grid(grid);
return 0;
}関数の読み方
このコードでは、マス目を char grid[SIZE][SIZE] という2次元配列で持っています。
clear_gridは、すべてのマスを.にする関数ですdraw_lineは、指定した始点とベクトルで線を描く関数ですprint_gridは、マス目を画面に表示する関数です
一番大事なのは、draw_line の中にある次の2行です。
x += dx;
y += dy;この2行によって、点がベクトルの向きに進みます。dx と dy を変えるだけで、線の向きが変わります。
範囲チェックの意味
draw_line には、次の条件があります。
if (0 <= x && x < SIZE && 0 <= y && y < SIZE) {
grid[y][x] = '#';
}これは、配列の外側に書き込まないためのチェックです。C言語では、配列の範囲を超えてアクセスすると危険なので、x と y がマス目の中にあるかを確認しています。
実行してみる
gcc -std=c99 vector_diamond.c -o vector_diamond
./vector_diamond実行結果は次のようになります。
....#....
...#.#...
..#...#..
.#.....#.
#.......#
.#.....#.
..#...#..
...#.#...
....#....4つの斜めベクトルを組み合わせることで、ひし形が描けました。
ベクトルの向きを変えてみる
draw_line に渡す dx と dy を変えると、線の向きが変わります。
| 呼び出し例 | 動き |
|---|---|
draw_line(grid, 0, 4, 1, 0, 9); | 横に右へ進む |
draw_line(grid, 4, 0, 0, 1, 9); | 縦に下へ進む |
draw_line(grid, 0, 0, 1, 1, 9); | 右下へ進む |
draw_line(grid, 8, 0, -1, 1, 9); | 左下へ進む |
このように、図形を描くコードでも、中心にある考え方は「現在地にベクトルを足す」だけです。
放射状の図形も描いてみる
もうひとつ、中心からいろいろな向きへ線を伸ばしてみます。
vector_star.c という名前で、次のコードを作ってみます。
#include <stdio.h>
#define SIZE 9
void clear_grid(char grid[SIZE][SIZE]) {
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
grid[row][col] = '.';
}
}
}
void draw_line(char grid[SIZE][SIZE], int x, int y, int dx, int dy, int count) {
for (int step = 0; step < count; step++) {
if (0 <= x && x < SIZE && 0 <= y && y < SIZE) {
grid[y][x] = '#';
}
x += dx;
y += dy;
}
}
void print_grid(char grid[SIZE][SIZE]) {
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
printf("%c", grid[row][col]);
}
printf("\n");
}
}
int main(void) {
char grid[SIZE][SIZE];
int center_x = 4;
int center_y = 4;
clear_grid(grid);
draw_line(grid, center_x, center_y, 1, 0, 5);
draw_line(grid, center_x, center_y, -1, 0, 5);
draw_line(grid, center_x, center_y, 0, 1, 5);
draw_line(grid, center_x, center_y, 0, -1, 5);
draw_line(grid, center_x, center_y, 1, 1, 5);
draw_line(grid, center_x, center_y, -1, 1, 5);
draw_line(grid, center_x, center_y, 1, -1, 5);
draw_line(grid, center_x, center_y, -1, -1, 5);
print_grid(grid);
return 0;
}実行すると、中心から8方向へ線が伸びます。
#...#...#
.#..#..#.
..#.#.#..
...###...
#########
...###...
..#.#.#..
.#..#..#.
#...#...#ここでは、横、縦、斜めのベクトルをまとめて使っています。ひし形と同じ draw_line 関数を使い回せるところもポイントです。
C言語で書くときの注意点
マス目をC言語で扱うときは、配列の範囲に注意します。
grid[y][x]の順番で書く。先に行、あとに列ですxは横方向、yは縦方向として扱いますxやyが0より小さくならないようにしますxやyがSIZE以上にならないようにします
特に grid[y][x] の順番は、初学者が混乱しやすいところです。座標としては (x, y) と書きますが、2次元配列では grid[行][列] なので、grid[y][x] になります。
Pythonではどう書ける?
同じひし形をPythonで書くと、次のようになります。
SIZE = 9
def clear_grid():
return [["." for _ in range(SIZE)] for _ in range(SIZE)]
def draw_line(grid, x, y, dx, dy, count):
for _ in range(count):
if 0 <= x < SIZE and 0 <= y < SIZE:
grid[y][x] = "#"
x += dx
y += dy
def print_grid(grid):
for row in grid:
print("".join(row))
grid = clear_grid()
draw_line(grid, 4, 0, -1, 1, 5)
draw_line(grid, 4, 0, 1, 1, 5)
draw_line(grid, 0, 4, 1, 1, 5)
draw_line(grid, 8, 4, -1, 1, 5)
print_grid(grid)考え方はC言語版と同じです。違うのは、Pythonではリストを使ってマス目を作り、for _ in range(count) のように短く繰り返しを書けるところです。
C言語版とPython版の違い
| 観点 | C言語 | Python |
|---|---|---|
| マス目 | char grid[SIZE][SIZE] のようにサイズを決める | リストの中にリストを入れて作る |
| 範囲チェック | 0 <= x && x < SIZE のように条件を分けて書く | 0 <= x < SIZE とまとめて書ける |
| 表示 | printf("%c", grid[row][col]) で1文字ずつ出す | "".join(row) で1行をまとめて出せる |
| 学びやすい点 | 配列、添字、関数の引数が見えやすい | 図形を短いコードで試しやすい |
改造して遊ぶポイント
コードを動かせたら、少し値を変えて遊んでみます。
SIZEを11や13に変えて、より大きなひし形を描くdraw_lineのcountを変えて、線の長さを変える#を*やOに変えて、見た目を変える- 横線と縦線を足して、ひし形の中に十字を入れる
dxとdyを入れ替えて、線の向きを変える
たとえば、ひし形の中に横線を入れたい場合は、次の1行を追加できます。
draw_line(grid, 0, 4, 1, 0, 9);(1, 0) は右へ進むベクトルなので、中央に横線が引かれます。
まとめ
ベクトルは、向きと大きさを持つ量です。マス目の上では、dx と dy を使うと「どちらへ進むか」をコードで表せます。
今回は、1つの点を動かすところから始めて、線を描き、4つのベクトルを組み合わせてひし形を作りました。
C言語で書くと、2次元配列、関数、条件分岐、ループをまとめて練習できます。まずは dx、dy、count の値を変えて、図形がどう変わるか試してみてください。







