Aizu Online Judge(AOJ)が提供している「プログラミング入門」(ITP1)の11_A問題をC++とPython で解いてみました。
ITP1 のトピック11では、構造体とクラスについて学びます。「構造体やクラスによって独自の型を定義し、プログラムで利用します。」とあります。この学習コースを通じて、Python に慣れていきたいと考えています。
問題(11_A:Dice I)
問題はリンク先をご覧ください。
トピック11を通じて利用する Dice クラスを設計、プログラムします。
Dice クラスの設計
サイコロを表現する Dice クラスを設計します。
まず、サイコロを表す6個の数字を以下の図の順番で番号を付けます。また、転がす方法を表す図も示します。
問題が求めている E方向、N方向、S方向、W方向の転がし、文字による転がし、その時点での数字の取得をクラスメソッドとして実装します。これらに加えて、上から見た時に時計回りに転がす roll も実装します。
以下は、Dice クラスが持つメソッド一覧です。
メソッド名 | メソッドの説明 |
コンストラクタ | クラスインスタンスを生成する。6個の数字を引数に与える。 |
E | E方向にサイコロを90度転がす |
N | N方向にサイコロを90度転がす |
S | S方向にサイコロを90度転がす |
W | W方向にサイコロを90度転がす |
roll | 上から見て、時計周りに90度転がす |
move | ENSWの文字列を与えると、その方向に90度転がす。 |
get_number | 今のサイコロの数字(向き)を返す。 |
解答案
C++ プログラム例(ITP1 11_A)
設計したメソッドを実装します。図の数字は、1から始めていますが、vector 型のコンテナの添え字は、0 から始めます。転がすメソッドは、もっと上手くプログラムする方法があるかもしれませんが、地道に書きました。
コンストラクタを呼び出し、インスタンスを生成します(109行目)。インスタンスを経由してメソッドを呼び出しています(114、117行目)。
かなり長くなりましたが、全体のプログラムは以下となります。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Dice {
vector<int> number;
public:
Dice(vector<int> num)
{
number = num;
}
void E(void)
{
vector<int> temp(6);
temp[2] = number[0];
temp[1] = number[1];
temp[5] = number[2];
temp[0] = number[3];
temp[4] = number[4];
temp[3] = number[5];
number = temp;
}
void N(void)
{
vector<int> temp(6);
temp[4] = number[0];
temp[0] = number[1];
temp[2] = number[2];
temp[3] = number[3];
temp[5] = number[4];
temp[1] = number[5];
number = temp;
}
void S(void)
{
vector<int> temp(6);
temp[1] = number[0];
temp[5] = number[1];
temp[2] = number[2];
temp[3] = number[3];
temp[0] = number[4];
temp[4] = number[5];
number = temp;
}
void W(void)
{
vector<int> temp(6);
temp[3] = number[0];
temp[1] = number[1];
temp[0] = number[2];
temp[5] = number[3];
temp[4] = number[4];
temp[2] = number[5];
number = temp;
}
void roll(void)
{
vector<int> temp(6);
temp[0] = number[0];
temp[3] = number[1];
temp[1] = number[2];
temp[4] = number[3];
temp[2] = number[4];
temp[5] = number[5];
number = temp;
}
void move(char ch)
{
switch (ch) {
case 'E':
this->E();
break;
case 'N':
this->N();
break;
case 'S':
this->S();
break;
case 'W':
this->W();
break;
default:
break;
}
}
vector<int> get_number(void)
{
return number;
}
};
int main()
{
vector<int> number(6);
for (int i = 0; i < 6; ++i) {
cin >> number[i];
}
Dice d(number);
string s;
cin >> s;
for (int i = 0; i < s.length(); ++i) {
d.move(s[i]);
}
number = d.get_number();
cout << number[0] << endl;
return 0;
}
Python プログラム例(ITP1 11_A)
Python でもクラスを使うことができます。
- クラス名は、慣例で大文字から始めます。
- コンストラクタは、__init__ になります。
- メソッドの第一引数は、クラスのインスタンスを指定します。慣例で self としています。第二引数以降で、メソッドの引数を受け取ります。
Dice クラスを定義した後、39行目からが本文となります。コンストラクタを呼び出し、Dice クラスのインスタンス d を生成しています(40行目)。生成したインスタンスからメソッドを呼び出しています(43、45行目)。
以下が、Python のプログラムとなります。
class Dice:
def __init__(self, number):
self.n = number.copy()
def E(self):
self.n[2], self.n[5], self.n[0], self.n[3] = \
self.n[0], self.n[2], self.n[3], self.n[5]
def N(self):
self.n[4], self.n[0], self.n[5], self.n[1] = \
self.n[0], self.n[1], self.n[4], self.n[5]
def S(self):
self.n[1], self.n[5], self.n[0], self.n[4] = \
self.n[0], self.n[1], self.n[4], self.n[5]
def W(self):
self.n[3], self.n[0], self.n[5], self.n[2] = \
self.n[0], self.n[2], self.n[3], self.n[5]
def roll(self):
self.n[3], self.n[1], self.n[4], self.n[2] = \
self.n[1], self.n[2], self.n[3], self.n[4]
def move(self, ch):
if ch == 'E':
self.E()
elif ch == 'N':
self.N()
elif ch == 'S':
self.S()
elif ch == 'W':
self.W()
def get_number(self):
return self.n
number = list(map(int, input().split()))
d = Dice(number)
s = input()
for i, ch in enumerate(s):
d.move(ch)
number = d.get_number()
print(number[0])
上記プログラムは、すべて AOJ で「AC(Accepted=正解)」と判定されます。
最後に
クラスを取り扱うトピック11は、トピック10までと比較して難易度が上がりました。
クラスは、関数と比較して、より大規模な再利用を可能にします。今回、作成した Dice クラスは、トピック11を通じて、再利用していきます。
引き続き、ITP1 の問題を紹介していきます。