MENU

【C#】ペントミノ2Dパズルを解く(5)ボードクラスを作る

いよいよボードクラスです。マス目の空いている場所に部品が置けるかどうかを判定したり、配置データをこのクラスで管理します。

・クラス宣言

クラスの名前は "Board" です。

internal class Board

・プロパティ:Cells( , )

ボードのマス目(値は Boolean) です。エクセルVBAのCellsと同じ名前ですが縦横は逆です。サイズは動的に確保します。

private bool[,] Cells;

・プロパティ:Width

ボードの幅を返します。

public int Width()
{
    return Cells.GetLength(0);
}

・プロパティ:Height

ボードの高さを返します。

public int Height()
{
    return Cells.GetLength(1);
}

・プロパティ:解判定関係

解の判定に使用するプロパティです。PlacedPieceCountが部品の種類(=12)に等しくなったときが正解です。PlacedFirstPieceは最初に置いた部品でプログレスバーの表示計算に使います。

public int PlacedPieceCount = 0;
public Piece? PlacedFirstPiece = null;

・初期化

引数にボードの横と縦のサイズを指定します。配列のサイズ指定がVBと違うので注意が必要です(筆者はこれにはまりました)。

public Board(int width,int height)
{
    Cells = new bool[width, height];
    PlacedPieceCount = 0;
    PlacedFirstPiece = null;
}

・関数 OnBoard

指定した座標がボード上にあるかどうかを返します(ボード上にあれば True)。

public bool OnBoard(Coord c)
{
    if (c.X >= 0 && c.X <= Width() - 1 &&
       c.Y >= 0 && c.Y <= Height() - 1)
    {
        return true;
    }
    else
    {
        return false; 
    }
}

・関数:NextBlankCell

指定した座標から空(False)セルを検索して、空セルがあればその座標を返します。検索方向は下で、下端まで到達すると右上へ移ります。空セルがなければ エラー(論理的にありえない)です。

public Coord NextBlankCell(Coord c)
{
    int x = c.X;
    int y = c.Y;
    while(Cells[x,y] == true)
    {
        if (y < Height() - 1)
        {
            y++;
        }
        else if(x < Width() - 1)
        {
            x++;
            y = 0;
        }
        else
        {
            throw new Exception("Out of board range error !!");
        }
    }
    return new Coord(x,y);
}

・関数:CanPlace

ボードの指定した位置に部品が置けるかどうかを返します(部品を置くことができれば True)。部品の5個の座標に対応するセルがすべて空であれば True、1つでもセルが埋まっていれば False を返します。

public bool CanPlace(Piece piece, Coord c)
{
    foreach (Coord p in piece.Coords)
    {
        int x = c.X + p.X;
        int y = c.Y + p.Y;
        if(OnBoard(new Coord(x, y)) == false || this.Cells[x,y] == true)
        {
            return false;
        }
    }
    return true;
}

・メソッド:PlacePiece

ボードの指定した位置に部品を置きます

public void PlacePiece(Shelf shelf, Coord c)
{
    if(shelf.SelectedPiece != null)
    {
        foreach (Coord p in shelf.SelectedPiece.Coords)
        {
            Cells[c.X + p.X, c.Y + p.Y] = true;
        }
        PlacedPieceCount++;     //解判定のためのカウンターを加算
        if (PlacedPieceCount == 1) 
        {
            PlacedFirstPiece = shelf.PlacedPiece;   //プログレスバー表示用
        }
    }
}

・メソッド:RemovePiece

ボードの指定した位置の部品を取り除きます

public void RemovePiece(Shelf shelf, Coord c)
{
    if (shelf.SelectedPiece != null)
    {
        foreach (Coord p in shelf.SelectedPiece.Coords)
        {
            Cells[c.X + p.X, c.Y + p.Y] = false;
        }
        PlacedPieceCount--;     //解判定のためのカウンターを減算
    }
}


以上が「ボードクラス」です。クラス全体のソースは以下のとおりです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ペントミノソルバー2D
{
    internal class Board
    {
        //********************************************
        //*        Board  (ボードクラス:盤面) 
        //********************************************

        private bool[,] Cells;               //セル(部品の有無)

        public int Width()
        {
            return Cells.GetLength(0);
        }

        public int Height()
        {
            return Cells.GetLength(1);
        }

        public int PlacedPieceCount = 0;
        public Piece? PlacedFirstPiece = null;

        //初期化
        public Board(int width,int height)
        {
            Cells = new bool[width, height];
            PlacedPieceCount = 0;
            PlacedFirstPiece = null;
        }

        //関数:OnBoard
        public bool OnBoard(Coord c)
        {
            if (c.X >= 0 && c.X <= Width() - 1 &&
               c.Y >= 0 && c.Y <= Height() - 1)
            {
                return true;
            }
            else
            {
                return false; 
            }
        }

        //関数:NextBlankCell
        public Coord NextBlankCell(Coord c)
        {
            int x = c.X;
            int y = c.Y;
            while(Cells[x,y] == true)
            {
                if (y < Height() - 1)
                {
                    y++;
                }
                else if(x < Width() - 1)
                {
                    x++;
                    y = 0;
                }
                else
                {
                    throw new Exception("Out of board range error !!");
                }
            }
            return new Coord(x,y);
        }

        //関数:CanPlace
        public bool CanPlace(Piece piece, Coord c)
        {
            foreach (Coord p in piece.Coords)
            {
                int x = c.X + p.X;
                int y = c.Y + p.Y;
                if(OnBoard(new Coord(x, y)) == false || Cells[x,y] == true)
                {
                    return false;
                }
            }
            return true;
        }

        //メソッド:PlacePiece
        public void PlacePiece(Shelf shelf, Coord c)
        {
            if(shelf.PlacedPiece != null)
            {
                foreach (Coord p in shelf.PlacedPiece.Coords)
                {
                    Cells[c.X + p.X, c.Y + p.Y] = true;
                }
                PlacedPieceCount++;     //解判定のためのカウンターを加算
                if (PlacedPieceCount == 1) 
                {
                    PlacedFirstPiece = shelf.PlacedPiece;   //プログレスバー表示用
                }
            }
        }

        //メソッド:RemovePiece
        public void RemovePiece(Shelf shelf, Coord c)
        {
            if (shelf.PlacedPiece != null)
            {
                foreach (Coord p in shelf.PlacedPiece.Coords)
                {
                    Cells[c.X + p.X, c.Y + p.Y] = false;
                }
                PlacedPieceCount--;     //解判定のためのカウンターを減算
            }
        }

    }
}

以上でプログラムで使用する4つのクラス(XY, Piece, Shelf, Board)が揃いました。次回は準備作業の最後として「DEFクラス」を作成します。このクラスはプログラムの表紙(鑑)です。プログラム諸元や共有で使用する定数や関数などをまとめて記述しておくクラスです。