前回C#でコンソールプログラムを書いた後、まだ次のお題が見つかっていなかったので、暇に任せて(C#のプログラムをベースとして)C++でコンソールプログラムを書いてみました。

 

結果論でいうと「文字列操作とコンソールへの文字列表示」の部分を除くとほとんどそのままC#から移植できました。一方、C#ではUnicodeベースのstring型文字列変数が機能が豊富で使いやすく簡単でしたが、C++では2バイト文字のANSI文字列を操作するのは結構面倒だったのと、(コンソールプログラムでユニコードを扱うのは今回が初めてでしたが)C++ライブラリーでユニコード文字列を扱うのは想像以上に面倒であり、書かれている通りにロケールを設定(注1)してWCHARとwcout、wcin(注2)を利用しても正常に動作しなかった為、ユニコードプログラムは断念しました。

注1:#include <locale.h>とsetlocale関数で、setlocale(LC_CTYPE, <引数>);の引数に"Japanese"や"JP-jp"(これは"jp-JP"が正しかったようですが)などを入れても反応せず、やっと"Japanese_Japan.932"でやっと文字が表示されました。

注2:最初どのヘッダーファイルを使うのか、cinとcoutはそのまま使えるのかわからなかったのですが、ウェブで調べて#include <iostream>でwcinとwcoutを使えることが分かりました。(データはWCHARとL"..."です。)しかし、注1の対応でwcoutは動いたのですが、wcinが正常に動きませんでした。

 

この為、あえてC++ではstringを使わずにANSIのcharとchar*を使い、Shift-JISの全角文字も使わずにASCII文字で塔を(半角文字の'O'、'|'と'-'で)描画しています。(何故かプログラミングをしていて郷愁を感じましたよ。)

いかがでしょうか?

C#は中間言語を使ったWindows依拠の実行環境ですが、速度的にはC++と差はないように感じます。しかし、一番印象的であったのはプログラムサイズで、Embarcadero C++コンパイラーはランタイムを使うのでDLL込みで194KBもありますが、MicroSoftのC#コンパイラーはDLLを使っていてもそのサイズは入らないのでたったの10KBだけでした。

 

ご参考になれば幸いです。

 

【ANSI C++によるハノイの塔】

//////////////////////////////////////////////////////////////////
// TowerOfHanoi.cpp
// Tower of Hanoi, a mathimatic game
// https://www.programmingalgorithms.com/algorithm/tower-of-hanoi/
//////////////////////////////////////////////////////////////////

#define        MAXTIRE        10        //最大段数(これより多くする場合、DISK定数も変更する)
#define        DISK        "OOOOOOOOOOOOOOOOOOOO"
#define        HORIZEN        "------------------------------------------------------------"

#include    <windows.h>            //Sleep関数使用の為
#include    <stdio.h>
#include    <conio.h>            //getch()使用の為
#include    <stdlib>
#include    <iostream>

using namespace std;

/////////////////////////////
//Poleクラス-石盤を嵌めたり、
//抜いたりする柱を表現する
/////////////////////////////
class Pole
{
    //メンバー変数
    int* m_Disks;
    int m_Tire = 0;

public:
    //コンストラクター
    Pole() {
        m_Disks = new int [MAXTIRE];
    }

    //コンストラクター(引数付)
    Pole(int num)
    {
        if(num > MAXTIRE) {
            m_Disks = new int [MAXTIRE];
        }
        else {
            m_Disks = new int [num];
        }
    }

    //デストラクター
    ~Pole() {
        delete [] m_Disks;
    }

    //柱に盤を嵌める
    void push(int num) {

        m_Disks[m_Tire] = num;
        m_Tire++;
    }

    //柱から盤を抜き、盤数を返す(なければ0)
    int pop() {

        int val = 0;
        if(m_Tire > 0)
        {
            val = m_Disks[m_Tire - 1];
            m_Disks[m_Tire - 1] = 0;
            --m_Tire;
        }
        return val;
    }

    //柱のnum番目の盤数を返す(なければ0)
    int getDisk(int num) {

        return m_Disks[num];
    }
};

/////////////////////
//TowerOfHanoiクラス
/////////////////////
class TowerOfHanoi
{
    //メンバー変数
    Pole m_pole[3];        //3本の柱
    int m_tire = 0;        //柱の段数

public:
    //コンストラクター
    TowerOfHanoi(int num) {
        //最初の柱に石盤を設置する
        m_tire = num;
        for(int i = 0; i < m_tire; i++)
            m_pole[0].push(m_tire - i);
    }
    //「ハノイの塔」メイン関数
    void TowerofHanoi(int diskCount, int fromPole, int toPole, int viaPole) {

        if (diskCount > 0) {
            TowerofHanoi(diskCount - 1, fromPole, viaPole, toPole);
            m_pole[toPole].push(m_pole[fromPole].pop());
            ShowHanoi();
            Sleep(100);
            //getch();    //毎回の移動を確認する場合これを使ってください。
            TowerofHanoi(diskCount - 1, viaPole, toPole, fromPole);
        }
    }

    //「ハノイの塔」表示関数
    void ShowHanoi() {

//        system("cls");                //画面クリア(そのほかにもsystem("pause")→「続行するには何かキーを押してください...」等がある)
        cout << "\x1B[2J\x1B[H";    //エスケープシーケンスによる画面クリアー
        cout << ">>>>> Tower of Hanoi <<<<<\n" <<endl;
        for(int i = m_tire - 1; i >= 0; i--)
            cout << MakeDisk(m_pole[0].getDisk(i)) << MakeDisk(m_pole[1].getDisk(i)) << MakeDisk(m_pole[2].getDisk(i)) << endl;
        //最後に「地面」線を描く
        cout << HORIZEN << endl;
    }

    //「ハノイの塔石盤作成関数」
    char* MakeDisk(int num)
    {
        if(num > MAXTIRE) {    //MAXTIREを超えた場合のエラー処理
            MessageBox(0, "不正な石盤番号です", "エラー", MB_OK | MB_ICONERROR);
            return NULL;
        }
        char static ln[MAXTIRE * 2 + 1] = {0};        //(MAXTIREX x 2)文字とnull終端分の行を作成
        for(int i = 0; i < (MAXTIRE * 2); i++)        //(MAXTIREX x 2)文字分を空白文字で初期化(最終はnullのまま)
            ln[i] = ' ';
        if(!num)
            ln[10] = ln[9] = '|';                    //柱
        else {                                        //石盤
            memcpy(ln + (MAXTIRE - num), DISK, num * 2);
        }
        return ln;
    }
};

/////////////
// Main関数
/////////////
int main() {

    //段数のユーザー設定
    int tire = 0;
    while(tire <= 0) {
        cout << "石盤柱の段数(整数)を入力してください(小数は切り捨てられます):";
        cin >> tire;
    }
    //ハノイの塔のインスタンスを作成
    TowerOfHanoi TOH(tire);
    //「ハノイの塔」の初期状態を表示
    TOH.ShowHanoi();
    cout << "開始します。何かキーを入押してください:" << endl;
    getch();
    //「ハノイの塔」の開始
    TOH.TowerofHanoi(tire, 0, 1, 2);
    //コンソールが閉じるのを停止させる
    cout << "終了しました。何かキーを入押してください:" << endl;
    getch();
    return 0L;
}