C#で2048ゲームのクローンを作る[その2]

前回は、上キーを押したときのみの処理を書いていましたが、上下左右のキーに対応しました。

これに伴って、上下左右のキーを押したとき、各セルの移動先を求める関数を切り出しました。
移動先を求めるのはgetDestPos()メソッドで、これを利用してセル移動を行うmoveCell()メソッドを実装しています。

他には、方向指定がC#のKeys列挙体の定義を流用していたため、専用のDIR列挙体を作っているなど、細かいところを修正しました。

using System;
using System.Collections.Generic;
using System.Data;
using System.Windows.Forms;
using System.Diagnostics;
 
namespace Mock2048 {
    enum DIR {
        UP,
        RIGHT,
        DOWN,
        LEFT,
    };
 
    public partial class Form1 : Form {
        const int BOARD_SIZE = 4;
 
        // 盤面の情報
        private int[,] board = new int[BOARD_SIZE,BOARD_SIZE];      
 
        public Form1() {
            InitializeComponent();
        }
 
        //*********************************************************************
        /// <summary> 画面表示時のハンドラ
        /// </summary>
        //*********************************************************************
        private void Form1_Load( object sender, EventArgs e ) {
            // Formコントロール自身がキー入力を取得可能とする
            this.KeyPreview = true;
 
            //盤面の初期化
            initBoard();
 
            // 盤面を表示する
            displayBoard(); 
        }
 
 
        //*********************************************************************
        /// <summary> キー入力時のハンドラ
        /// </summary>
        //*********************************************************************
        private void Form1_KeyDown( object sender, KeyEventArgs e ) {
            switch (e.KeyCode) {
                case Keys.Up:    moveCell( DIR.UP );    break;
                case Keys.Right: moveCell( DIR.RIGHT ); break;
                case Keys.Down:  moveCell( DIR.DOWN );  break;
                case Keys.Left:  moveCell( DIR.LEFT );  break;
            }
 
            // 盤面を表示する
            displayBoard();
        }
 
        //*********************************************************************
        /// <summary> 盤面の初期化(テストデータのセット)
        /// </summary>
        //*********************************************************************
        private void initBoard() {
            board[0,0] = 2;
            board[0,3] = 2;
            board[1,1] = 8;
 
            board[2,0] = 4;
            board[2,2] = 4;
            board[3,0] = 2;
            board[3,1] = 2;
            board[3,3] = 8;
 
        }
 
        //*********************************************************************
        /// <summary> 盤面を画面に表示させる
        /// </summary>
        //*********************************************************************
        private void displayBoard() {
            string boardData = "";
 
            // すべての行のデータを出すまで繰り返し
            for (int y = 0; y < 4; y++) {
                // 1行分のデータを出力
                for (int x = 0; x < 4; x++) {
                    boardData += "[" + String.Format("{0, 2}", board[x,y]) + "] ";
                }
                boardData += Environment.NewLine;
                boardData += Environment.NewLine;
            }
 
            txtLog.Text = boardData;
 
            txtLog.Select(0,0);
            txtLog.ReadOnly = true;
        }
 
        //*********************************************************************
        /// <summary> 指定された方向に移動させる
        /// </summary>
        /// <param name="keyCode"></param>
        //*********************************************************************
        private void moveCell( DIR dir ) {
 
            // 入力されたキーを判定する
            switch ( dir ) {
                case DIR.UP:
                    // 2~4列目に対し上方向のスライドを試みる
                    for (int y = 1; y < BOARD_SIZE; y++) {
                        for (int x = 0; x < BOARD_SIZE; x++) {  
                            moveCell( x, y, DIR.UP );
                        }
                    }
                    break;
                case DIR.DOWN:
                    // 1~3列目に対し下方向のスライドを試みる
                    for (int y = BOARD_SIZE-2; y >= 0; y--) {
                        for (int x = 0; x < BOARD_SIZE; x++) {  
                            moveCell( x, y, DIR.DOWN );
                        }
                    }
                    break;
                case DIR.LEFT:
                    // 2~4行目に対し左方向のスライドを試みる
                    for (int y = 0; y < BOARD_SIZE; y++) {
                        for (int x = 1; x < 4; x++) {
                            moveCell( x, y, DIR.LEFT );
                        }
                    }
                    break;
                case DIR.RIGHT:
                    // 1~3行目に対し右方向のスライドを試みる
                    for (int y = 0; y < BOARD_SIZE; y++) {
                        for (int x = BOARD_SIZE-2; x >= 0; x--) {
                            moveCell( x, y, DIR.RIGHT );
                        }
                    }
                    break;
                default:
                    //txtLog.Text = "";
                    break;                        
            }
        }
 
        //*********************************************************************
        /// <summary> 指定された方向にセルをスライドさせる
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="dir"></param>
        //*********************************************************************
        private void moveCell( int x, int y, DIR dir ) {
            int endX;
            int endY;
            bool isDuplicate;
 
            // 移動先のセル位置を求める
            getDestPos( x, y, dir, out endX, out endY, out isDuplicate );
 
            // セルを移動
            int targetVal = board[x,y];
            board[x,y] = 0;
            board[endX,endY] = targetVal * (isDuplicate ? 2 : 1 );
        }
 
        //*********************************************************************
        /// <summary> 指定された方向にセルをスライドさせた時の移動先を求める
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="dir"></param>
        /// <param name="endX"></param>
        /// <param name="endY"></param>
        /// <param name="isDuplicate"></param>
        //*********************************************************************
        private void getDestPos( int x, int y, DIR dir, out int endX, out int endY, out bool isDuplicate ) {
 
            // initialize
            endX = x;
            endY = y;
            isDuplicate = false;
 
            // 指定されたセルの値を取得
            int targetCellVal = board[x,y];
            if (targetCellVal == 0) {
                return;
            }
 
            // 移動方向を元に探索方向のオフセットを決める
            int dx = 1;
            int dy = 1;
            switch ( dir ) {
                case DIR.UP:    dx =  0; dy = -1; break;
                case DIR.RIGHT: dx =  1; dy =  0; break;
                case DIR.DOWN:  dx =  0; dy =  1; break;
                case DIR.LEFT:  dx = -1; dy =  0; break;
            }
 
 
            // 何かにぶつかるまで探索する
            while (true) {
                if (endX + dx < 0 || endY + dy < 0 || endX + dx >= BOARD_SIZE || endY + dy >= BOARD_SIZE) {
                    // 壁にぶつかった -> 1つ手前の場所でストップ
                    isDuplicate = false;
                    break;
                } else if (board[endX + dx, endY + dy] != 0) {
                    // 他のセルにぶつかった
                    if (targetCellVal == board[endX + dx, endY + dy]) {
                        // 衝突先が同じ値だった -> 重ねる
                        endX += dx;
                        endY += dy;
                        isDuplicate = true;
                        break;
                    } else {
                        // 衝突先が異なる値だった -> 1つ手前の場所でストップ
                        isDuplicate = false;
                        break;
                    }
                } else {
                    // 何にもぶつからなかった -> さらに次の位置をチェック
                    endX += dx;
                    endY += dy;
                    continue;
                }               
            }
 
            //Debug.WriteLine( "セルの移動を行います [" + x + ", " + y + "] -> [" + endX + ", " + endY + "] dup=" + isDuplicate );
        }
    }
}



今回作ったプログラムのexeも置いときます。
キーボードのカーソルキーを押して操作してみてください。うまく操作すると、32の数字を作ることができます。
Mock2048_20160127.zip

次回は、Boardの概念をクラスに分けるか、もしくは全体のゲームの進行制御を作る予定です。

関連記事

コメントを残す

メールアドレスが公開されることはありません。