ゲームプログラミング:C++で15パズルを作ってみた


概要

はい、前回に引き続きコンソールゲームプログラミングですね。
今回はもっと簡単です。
 https://qiita.com/Chomolungma/items/52bd8b133b747cb603e7

以下を参考にしながら15パズルを作成しました。
館長、ありがとう!!!
 https://www.youtube.com/watch?v=YoQt9RHy8rA

とりあえずうまく動作していそうです。
まず15パズルで遊んだことが無かったので、このゲームの操作法やら攻略法に苦戦しました。
ゲームプレーヤーとして未熟なのに開発側に回りました、ホントすみません。


16マスにシャッフルされた15個の数字を・・・


揃える!

所感

思ったんですが、トップダウンに(設計→実装)とした方が、やっぱりキレイなコード書ける気がします。
ボトムアップだとちょこちょこ動かしながら楽しめるのですが、main関数にある長ったらしい処理を抽出して、関数化するというのが面倒に感じます(笑)
「もう動くからいいじゃん!」みたいな・・・
自分がウォーターフォールしか経験したこと無かったので、こういうのは新鮮でした。
16マスの範囲外へアクセスすることに対する防御的プログラミングといったことは、すぐに頭に浮かんできますが、そういう石橋を叩いて渡るスタイルだと、動くところまで辿り着きにくいのかもなぁと、ちょっと考え直しました。

コード

せっかくなので、コードを公開します。
ご自由にお試し下さい。
ホントはコメントとかネーミングとかもうちょっと考えて、初学者の方が分かるようにしたいと思っていますが・・・

C++的に記述できていないところも多い(構造体使うならクラス使えよ!)ので、ちょこちょこ修正していこうと思います。
回帰テストせなアカンで!!!

※コードレビューコメント頂けますと幸いです

source.cpp
#include <iostream>
#include <iomanip>
#include <stdlib.h>
#include <conio.h>
#include <time.h>

static const int BOARD_WIDTH = 4;
static const int BOARD_HEIGHT = 4;
static const int BLANK_NUMBER = 16;

int board[BOARD_HEIGHT][BOARD_WIDTH];

struct Vec2
{
    int x;
    int y;
};

Vec2 getBlankPosition();
void moveBlank(Vec2 argMovePosition);

int main()
{
    for (int y = 0; y < BOARD_HEIGHT; ++y)
    {
        for (int x = 0; x < BOARD_WIDTH; ++x)
        {
            board[y][x] = y * BOARD_HEIGHT + x + 1;
        }
    }

    srand(static_cast<int>(time(NULL)));

    for (int i = 0; i < 1000; ++i)
    {
        Vec2 blankPosition = getBlankPosition();
        Vec2 movePosition = blankPosition;

        switch (rand() % BOARD_HEIGHT)
        {
        case 0:
            ++(movePosition.y);
            break;
        case 1:
            --(movePosition.y);
            break;
        case 2:
            ++(movePosition.x);
            break;
        case 3:
            --(movePosition.x);
            break;
        }

        moveBlank(movePosition);
    }

    while (1)
    {
        system("cls");
        std::cout << "+--+--+--+--+" << std::endl;
        for (int y = 0; y < BOARD_HEIGHT; ++y)
        {
            for (int x = 0; x < BOARD_WIDTH; ++x)
            {
                if (BLANK_NUMBER == board[y][x])
                {
                    std::cout << "|  ";
                }
                else
                {
                    std::cout << "|" << std::setw(2) << board[y][x];
                }
            }
            std::cout << "|" << std::endl;
            std::cout << "+--+--+--+--+" << std::endl;
        }

        bool isClear = true;
        for (int y = 0; y < BOARD_HEIGHT; ++y)
        {
            for (int x = 0; x < BOARD_WIDTH; ++x)
            {
                if (board[y][x] != y * BOARD_HEIGHT + x + 1)
                {
                    isClear = false;
                }
            }
        }

        if (true == isClear)
        {
            std::cout << "Clear!" << std::endl;
        }

        Vec2 blankPosition = getBlankPosition();
        Vec2 movePosition = blankPosition;

        switch (_getch())
        {
        case 'w':
            ++(movePosition.y);
            break;
        case 's':
            --(movePosition.y);
            break;
        case 'a':
            ++(movePosition.x);
            break;
        case 'd':
            --(movePosition.x);
            break;
        }

        moveBlank(movePosition);

    }
    _getch();
    return 0;
}

Vec2 getBlankPosition()
{
    Vec2 blankPosition = { -1, -1 };

    for (int y = 0; y < BOARD_HEIGHT; ++y)
    {
        for (int x = 0; x < BOARD_WIDTH; ++x)
        {
            if (BLANK_NUMBER == board[y][x])
            {
                blankPosition = { x ,y };
            }
        }
    }

    return blankPosition;
}

void moveBlank(Vec2 argMovePosition)
{
    if (argMovePosition.x < 0 || argMovePosition.x >= BOARD_WIDTH || argMovePosition.y < 0 || argMovePosition.y >= BOARD_HEIGHT)
    {
        return;
    }

    Vec2 blankPosition = getBlankPosition();

    // swapping
    int tempBlank = board[blankPosition.y][blankPosition.x];
    board[blankPosition.y][blankPosition.x] = board[argMovePosition.y][argMovePosition.x];
    board[argMovePosition.y][argMovePosition.x] = tempBlank;
}