前回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;
}
