C++で簡単な五目並べをプログラムを作成してみました。
C++初心者です。C++で何かプログラムを作りたかったので、五目並べを作ってみました。
至らぬ点ばかりだとは思いますが、何卒ご容赦お願いします。
以下がソースコードです。
あくまで大まかな流れをとりあえず作りたかったので、入力チェックなどはまだ不十分です。今後改善予定です。
main.cpp
#include <iostream>
#include <string>
#include "Board.h"
#include "CommonData.h"
int main(void)
{
Board board;
CommonData::PlayerKind nowPlayer = CommonData::PlayerKind::Black;
while (true)
{
bool isPutSucceed = false;
std::string dispStr;
while (!isPutSucceed)
{
// 盤面の描画
board.Draw();
// 勝敗判定
CommonData::PlayerKind winPlayer = board.Judge();
if (winPlayer != CommonData::PlayerKind::None)
{
if (winPlayer == CommonData::PlayerKind::Black) {
dispStr = "黒";
}
else {
dispStr = "白";
}
// ゲーム終了
std::cout << dispStr << "の勝ち!やったね!!!";
std::rewind(stdin);
std::getchar();
return 0;
}
if (nowPlayer == CommonData::PlayerKind::Black) {
dispStr = "黒";
}
else {
dispStr = "白";
}
// 置く石の位置の入力
std::cout << dispStr << "の手番です。横・縦の順に入力してください。" << std::endl;
int x, y;
std::cout << "横:";
std::cin >> x;
std::cout << "縦:";
std::cin >> y;
isPutSucceed = board.PutStone(nowPlayer, x, y);
if(!isPutSucceed)
{
std::cout << "正しい場所に置いてください!" << std::endl;
std::rewind(stdin);
std::getchar();
std::system("cls");
}
}
// 番の変更
if (nowPlayer == CommonData::PlayerKind::Black) {
nowPlayer = CommonData::PlayerKind::White;
}else {
nowPlayer = CommonData::PlayerKind::Black;
}
std::system("cls");
}
return 0;
}
Board.h
#pragma once
#include <array>
#include "CommonData.h"
class Board
{
enum JudegeKind
{
Tete,
Yoko,
Naname1,
Naname2,
};
public:
Board();
~Board();
void Draw(void);
bool PutStone(CommonData::PlayerKind player, int x, int y);
CommonData::PlayerKind Judge(void);
private:
std::array<std::array<CommonData::PlayerKind, CommonData::BoardSizeY>, CommonData::BoardSizeX> m_boardData;
CommonData::PlayerKind JudgeBaseStone(JudegeKind judgeKind, int x, int y);
bool IsRangeOk(int x, int y);
bool IsPutOk(int x, int y);
};
Board.cpp
#include <string>
#include <iostream>
#include "Board.h"
namespace
{
const char* NumberStr[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9" };
}
Board::Board()
{
// 配列の初期化
for (int y = 0; y < CommonData::BoardSizeY; y++)
{
for (int x = 0; x < CommonData::BoardSizeX; x++)
{
m_boardData[y][x] = CommonData::PlayerKind::None;
}
}
}
Board::~Board()
{
}
// 盤の描画
void Board::Draw()
{
// 一番上
std::string drawStr = " ";
for (int i = 0; i < CommonData::BoardSizeX; i++)
{
drawStr = drawStr + NumberStr[i];
}
std::cout << drawStr << std::endl;
for (int y = 0; y < CommonData::BoardSizeY; y++)
{
drawStr = NumberStr[y];
for (int x = 0; x < CommonData::BoardSizeX; x++)
{
switch (m_boardData[y][x])
{
case CommonData::PlayerKind::None:
drawStr = drawStr + "・";
break;
case CommonData::PlayerKind::White:
drawStr = drawStr + "●";
break;
case CommonData::PlayerKind::Black:
drawStr = drawStr + "○";
break;
}
}
std::cout << drawStr << std::endl;
}
}
// 石を置く
bool Board::PutStone(CommonData::PlayerKind player, int x, int y)
{
if (IsPutOk(x, y))
{
m_boardData[y - 1][x - 1] = player;
return true;
}
else {
return false;
}
}
// 勝敗の判定
CommonData::PlayerKind Board::Judge(void)
{
// すべてのマスごとに、5つ並んでいるか判定を行う
for (int y = 0; y < CommonData::BoardSizeY; y++)
{
for (int x = 0; x < CommonData::BoardSizeX; x++)
{
CommonData::PlayerKind playerResult;
// 縦の判定
playerResult = JudgeBaseStone(JudegeKind::Tete, x, y);
if (playerResult != CommonData::PlayerKind::None) {
return playerResult;
}
// 横の判定
playerResult = JudgeBaseStone(JudegeKind::Yoko, x, y);
if (playerResult != CommonData::PlayerKind::None) {
return playerResult;
}
// 斜めの判定(左上→右下)
playerResult = JudgeBaseStone(JudegeKind::Naname1, x, y);
if (playerResult != CommonData::PlayerKind::None) {
return playerResult;
}
// 斜めの判定(右上→左下)
playerResult = JudgeBaseStone(JudegeKind::Naname2, x, y);
if (playerResult != CommonData::PlayerKind::None) {
return playerResult;
}
}
}
return CommonData::PlayerKind::None;
}
// 基点となる石から5つならんでいるか判定する
CommonData::PlayerKind Board::JudgeBaseStone(JudegeKind judgeKind, int x, int y)
{
CommonData::PlayerKind basePlayerKind;
// 5回ループ
for (int i = 0; i < 5; i++)
{
// 基点の色を保存しておく
if (i == 0) {
basePlayerKind = m_boardData[y][x];
}
int searchX;
int searchY;
switch (judgeKind)
{
case JudegeKind::Tete:
searchX = x;
searchY = y + i;
break;
case JudegeKind::Yoko:
searchX = x + i;
searchY = y;
break;
case JudegeKind::Naname1:
searchX = x + i;
searchY = y + i;
break;
case JudegeKind::Naname2:
searchX = x - i;
searchY = y + i;
break;
}
if (IsRangeOk(searchX, searchY))
{
if (m_boardData[searchY][searchX] != basePlayerKind)
{
// 5つ並んでいない
return CommonData::PlayerKind::None;
}
}
else
{
// 範囲外のときはNG
return CommonData::PlayerKind::None;
}
}
// 5つならんだ駒を返す
return basePlayerKind;
}
// 盤の範囲内か判定を行う
bool Board::IsRangeOk(int x, int y)
{
if (x >= 0 && x < CommonData::BoardSizeX &&
y >= 0 && y < CommonData::BoardSizeX)
{
return true;
}
return false;
}
// 盤に駒が置けるかの判定を行う
bool Board::IsPutOk(int x, int y)
{
if (!IsRangeOk(x-1, y-1)) {
return false;
}
if (m_boardData[y-1][x-1] != CommonData::PlayerKind::None) {
return false;
}
return true;
}
CommonData.h
#pragma once
namespace CommonData
{
const int BoardSizeX = 9;
const int BoardSizeY = 9;
enum PlayerKind
{
None,
White,
Black,
};
}
実際の画面
上記のような画面が表示されます
○ … 黒
● … 白 です(コンソールだと色が反転するので…)
横・縦の順に入力します。
縦・横・斜めのどれかに5つ揃えると、終わりです。
それだけです。
開発環境
- Visual Studio 2017
- Windows 7
意識したポイント
- なるべくマジックナンバーを使わない。
- 描画する"○""●"やJudgeBaseStone関数のfor文の中の5(5個分石が並んでいるか判定の意味)などは、多分変更が無いのと、1箇所だけなので、とりあえず直接書いています。
- なるべくC++っぽく書く。
- 配列などは、 std::array を使うことにした。(std::vectorとどちらを使うか悩んだが、可変長である必要がないのでarrayにしました)
- #defineは使用せず、constやenumを使用する
今後改善したい点
- 5個並べる判定の部分は現在かなり
ゴミ宜しくないので、アルゴリズム改善したい。 - Player vs Player しかできない為、Comとの対戦できるようにしたい。