C# 学習お手本 Console / .NET 8

配列と番兵だけで

@ を歩かせる。

2次元配列でマップを作り、外側を1マス「番兵」で囲む。それだけで、範囲チェックを一切書かずに @ が壁に当たって止まる。コンソールで動く、小さな C# プログラムです。

dotnet run

配列でマップ

0 = 床、1 = 壁 の int[,] を用意し、char のマップへ変換する。

番兵(sentinel)

マップの外側を1マス壁で囲む。境界チェックの書き忘れが構造的に消える。

キー入力

Console.ReadKey で矢印キー(4方向)とテンキー(8方向)を読む。

Program.cs(全 163 行)

// ============================================================
//  Program.cs — C# 学習用お手本
//  「@マークを歩き回らせるだけ」の小さなプログラム
//
//  学べること
//    1. 2次元配列でマップを作る
//    2. 番兵(sentinel)で端の判定を消す
//       → 添字が範囲外になる心配がなくなる
//    3. while ループ + キー入力(矢印キー / テンキー)
//    4. メソッドに分ける(描画 / 入力 / 移動)
//
//  実行方法は同じフォルダの README.md を見てください。
// ============================================================

using System;

class Program
{
    // 番兵=マップの外側を1マス囲む「壁」。
    // 外へ出ようとしたら必ずこの壁に当たるので、
    // 「x が 0 より小さいか?」のような範囲チェックを書かなくて済む。
    const char Wall   = '#'; // 壁(番兵もこれで描く)
    const char Floor  = '.'; // 床
    const char Player = '@'; // プレイヤー

    static void Main()
    {
        // --- (1) マップ本体(内側だけ) ---
        // 0 = 床, 1 = 壁。あとで番兵つきの char マップへ変換する。
        int[,] inner =
        {
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 1, 1, 0, 0, 1, 1, 1, 1, 0 },
            { 0, 1, 0, 0, 0, 0, 0, 0, 1, 0 },
            { 0, 0, 0, 1, 1, 0, 1, 0, 0, 0 },
            { 0, 0, 0, 1, 0, 0, 1, 0, 0, 0 },
            { 0, 1, 0, 0, 0, 0, 0, 0, 1, 0 },
            { 0, 1, 1, 1, 1, 1, 1, 0, 1, 0 },
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        };

        // --- (2) 番兵つきマップに変換 ---
        char[,] map = WithSentinel(inner);

        // プレイヤーの座標(内側だけの座標。描くときに番兵の分 +1 する)
        int px = 2;
        int py = 2;

        Console.CursorVisible = false;

        // --- (3) メインループ:描く → キーを待つ → 動かす ---
        while (true)
        {
            Draw(map, px, py);

            ConsoleKeyInfo key = Console.ReadKey(true); // true = 押したキーを画面に出さない

            if (key.Key == ConsoleKey.Escape || key.Key == ConsoleKey.Q)
                break; // Esc か Q で終了

            int dx, dy;
            if (!TryGetDirection(key.Key, out dx, out dy))
                continue; // 関係ないキーは無視して最初に戻る

            TryMove(map, ref px, ref py, dx, dy);
        }
    }

    // 周囲を1マスぶん壁(番兵)で囲んだ char マップを作る。
    static char[,] WithSentinel(int[,] inner)
    {
        int rows = inner.GetLength(0);
        int cols = inner.GetLength(1);

        char[,] map = new char[rows + 2, cols + 2]; // 上下左右に +1

        for (int y = 0; y < map.GetLength(0); y++)
        {
            for (int x = 0; x < map.GetLength(1); x++)
            {
                bool edge =
                    y == 0 || x == 0 ||
                    y == map.GetLength(0) - 1 ||
                    x == map.GetLength(1) - 1;

                if (edge)
                    map[y, x] = Wall; // 番兵
                else
                    map[y, x] = inner[y - 1, x - 1] == 1 ? Wall : Floor;
            }
        }

        return map;
    }

    // 画面を全部描き直す(簡単のため毎回全描画)。
    static void Draw(char[,] map, int px, int py)
    {
        Console.SetCursorPosition(0, 0); // 毎回左上から書き直す

        for (int y = 0; y < map.GetLength(0); y++)
        {
            for (int x = 0; x < map.GetLength(1); x++)
            {
                // プレイヤーの座標は番兵の分 +1 ずらす
                if (x == px + 1 && y == py + 1)
                    Console.Write(Player);
                else
                    Console.Write(map[y, x]);
            }
            Console.WriteLine();
        }

        Console.WriteLine("矢印キー / テンキー で移動(5 はその場)");
        Console.WriteLine("Esc か Q で終了");
    }

    // 押されたキーを方向 (dx, dy) に変換する。
    // 対応していないキーなら false を返す。
    // テンキーは斜めもあるので 8 方向。
    static bool TryGetDirection(ConsoleKey key, out int dx, out int dy)
    {
        dx = 0;
        dy = 0;

        switch (key)
        {
            // 矢印キー(4方向)
            case ConsoleKey.UpArrow:    dy = -1; return true;
            case ConsoleKey.DownArrow:  dy =  1; return true;
            case ConsoleKey.LeftArrow:  dx = -1; return true;
            case ConsoleKey.RightArrow: dx =  1; return true;

            // テンキー(8方向)
            case ConsoleKey.NumPad8: dy = -1;           return true; // ↑
            case ConsoleKey.NumPad2: dy =  1;           return true; // ↓
            case ConsoleKey.NumPad4: dx = -1;           return true; // ←
            case ConsoleKey.NumPad6: dx =  1;           return true; // →
            case ConsoleKey.NumPad7: dx = -1; dy = -1;  return true; // ↖
            case ConsoleKey.NumPad9: dx =  1; dy = -1;  return true; // ↗
            case ConsoleKey.NumPad1: dx = -1; dy =  1;  return true; // ↙
            case ConsoleKey.NumPad3: dx =  1; dy =  1;  return true; // ↘
        }

        return false;
    }

    // 進もうとした先が壁(番兵を含む)でなければ座標を更新する。
    static void TryMove(char[,] map, ref int px, ref int py, int dx, int dy)
    {
        int nx = px + dx;
        int ny = py + dy;

        // 番兵の分だけずらして地形を見る。
        // 番兵があるので、ここで範囲外になることは絶対にない。
        if (map[ny + 1, nx + 1] != Wall)
        {
            px = nx;
            py = ny;
        }
    }
}

番兵(sentinel)とは

マップの外側をぐるっと壁で囲んでおくテクニック。外へ出ようとすると必ずその壁に当たるので、添字が範囲外にならず、境界チェックの書き忘れによるクラッシュが起こらない。

普通なら、進む前に4方向の範囲を確認する必要がある。

if (0 <= nx && nx < cols && 0 <= ny && ny < rows) // これが要らない

実行のしかた

  1. .NET SDK を入れる

    初回だけ必要。入れ方は下の付録(Windows / Mac)。

  2. 入ったか確認する

    dotnet --version

    8.x.x のように表示されればOK。

  3. 実行する

    このリポジトリにある場合はそのまま。

    cd projects/csharp-walk dotnet run

    手元に新しく作る場合は、雛形を作って Program.cs をこのページのコードに置き換える。

    dotnet new console -o walk cd walk # 生成された Program.cs を置き換える dotnet run

操作

キー動き
↑ ↓ ← →4方向に1マス
テンキー 8 2 4 6上下左右
テンキー 7 9 1 3斜め
Esc / Q終了

テンキーで斜めを動かすには NumLock をオンにする(オフだと矢印キー扱いになる。矢印キーはそのまま使える)。

付録: .NET SDK の入れ方

ビルドと実行に .NET 8 SDK が要る。どちらも公式の配布物を入れる。バージョンは 8 系で揃えている。

Windows

winget install dotnet-sdk-8.0

PowerShell またはコマンドプロンプトで実行する。winget が無い場合は公式インストーラ(.NET 8 SDK / x64)を落として実行する。

dotnet.microsoft.com/download/dotnet/8.0

macOS

brew install --cask dotnet-sdk@8

Homebrew で入れる。Homebrew が無い場合は公式インストーラ(.NET 8 SDK / Arm64 または x64)を落として実行する。

dotnet.microsoft.com/download/dotnet/8.0

入れたら dotnet --version で確認する。dotnet は初回起動時に「Welcome to .NET」の案内を出すことがある。