【ベクトルで遊ぼう】C言語でマス目にひし形を描く|向きと大きさをコードで見てみる | UNIX Cafe

* 当サイトでは、コンテンツの一部に広告を掲載しています。

System Note $ cat /proc/ai-disclosure

本記事の構成および論理分析には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つの点をベクトルで動かしてみます。

xy が現在の位置、dxdy が移動する向きです。

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 = 1dy = 1 なので、この点は毎回右下へ進みます。

x += dx; は、いまの xdx を足す処理です。y += dy; も同じで、いまの ydy を足します。

つまり、位置を更新する基本形は次の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)

xy が、どちらも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 = 4y = 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_xstart_y は、線の始まりの場所です。dxdy は、1回ごとに進む向きです。

内側の for (int step = 0; step < 6; step++) では、始点から6個ぶんの点を調べています。

dx = 1dy = 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行によって、点がベクトルの向きに進みます。dxdy を変えるだけで、線の向きが変わります。

範囲チェックの意味

draw_line には、次の条件があります。

if (0 <= x && x < SIZE && 0 <= y && y < SIZE) {
    grid[y][x] = '#';
}

これは、配列の外側に書き込まないためのチェックです。C言語では、配列の範囲を超えてアクセスすると危険なので、xy がマス目の中にあるかを確認しています。

実行してみる

gcc -std=c99 vector_diamond.c -o vector_diamond
./vector_diamond

実行結果は次のようになります。

....#....
...#.#...
..#...#..
.#.....#.
#.......#
.#.....#.
..#...#..
...#.#...
....#....

4つの斜めベクトルを組み合わせることで、ひし形が描けました。

ベクトルの向きを変えてみる

draw_line に渡す dxdy を変えると、線の向きが変わります。

呼び出し例動き
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 は縦方向として扱います
  • xy0 より小さくならないようにします
  • xySIZE 以上にならないようにします

特に 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行をまとめて出せる
学びやすい点配列、添字、関数の引数が見えやすい図形を短いコードで試しやすい

改造して遊ぶポイント

コードを動かせたら、少し値を変えて遊んでみます。

  • SIZE1113 に変えて、より大きなひし形を描く
  • draw_linecount を変えて、線の長さを変える
  • #*O に変えて、見た目を変える
  • 横線と縦線を足して、ひし形の中に十字を入れる
  • dxdy を入れ替えて、線の向きを変える

たとえば、ひし形の中に横線を入れたい場合は、次の1行を追加できます。

draw_line(grid, 0, 4, 1, 0, 9);

(1, 0) は右へ進むベクトルなので、中央に横線が引かれます。

まとめ

ベクトルは、向きと大きさを持つ量です。マス目の上では、dxdy を使うと「どちらへ進むか」をコードで表せます。

今回は、1つの点を動かすところから始めて、線を描き、4つのベクトルを組み合わせてひし形を作りました。

C言語で書くと、2次元配列、関数、条件分岐、ループをまとめて練習できます。まずは dxdycount の値を変えて、図形がどう変わるか試してみてください。

C言語の次にUnityやUnreal Engineを学ぶ選択肢

用途ごとに選ぶ C言語のおすすめ本

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

のいのアバター のい UNIX Cafe マスター

Macintosh Color Classicから始まった旅は、長いWindows時代を経て、Windows10のサポート終了をきっかけにUNIXの世界へ戻ってきました。UNIX Cafeでは、UNIX・Linux・そしてMacな世界を、むずかしい言葉を使わず、物語のように書いています。プログラミングは、アイデアをコンピューターに伝えるための言葉です。簡単な単語と文法を覚えれば、誰でもコマンドを使えます。ぜひ一度、やさしいプログラミングの世界をのぞいてみてください。

目次