C++第3版 15章 練習問題解答

目次へ戻る

15.1

VC++ で RTTI(すなわち、dynamic_cast など)や例外を 使用するためには、「プロジェクト設定」の「C/C++」タブの 「C++言語」カテゴリの該当項目を有効にしなければいけない。 ちなみに、デフォルトでは「例外処理」が有効に、「RTTI」が無効になっている。

関数版(ptr_castF) とクラス版(ptr_castC) を作った。 関数版のテンプレート引数は2つあるが、使う時は1つしか指定していない。 VC++ では動くが、どうなのだろう?
#include <iostream>
using namespace std;

template <typename T,typename U>
T ptr_castF(U u)
{
    T   t = dynamic_cast<T>(u);
    if (!t) throw bad_cast();
    return t;
}

template <typename T>
class ptr_castC {
    T   t;
public:
    template <typename U>
        ptr_castC(U u) : t(dynamic_cast<T>(u)) {}
    operator T(){if (!t) throw bad_cast(); return t;}
};

struct Base {virtual ~Base(){}};
struct Derived : Base {};
struct Bad {};

int main()
{
    Derived     d;
    Base*       p0 = &d;
    Derived*    p1 = ptr_castF<Derived*>(p0);
    try {
        Bad*    p2 = ptr_castF<Bad*>(p0);
    }
    catch (bad_cast) {
        cout << "err F\n";
    }
    Derived*    p3 = ptr_castC<Derived*>(p0);
    try {
        Bad*    p4 = ptr_castC<Bad*>(p0);
    }
    catch (bad_cast) {
        cout << "err C\n";
    }
}

15.2

VC++ で typeid を使用するには、前問と同じく、 コンパイラの設定が必要だし、対象のクラスに仮想関数が1つ以上ないといけない。 ということで、デストラクタを仮想にしている。
結果を見ればわかるとおり、コンストラクタとデストラクタにおいては、 this はそのクラスそのものを指している。但し、コンストラクタとデストラクタで typeid 等の仮想関数テーブルを参照することは、一般に危ういかもしれない。
#include <iostream>
using namespace std;

struct Base {
    Base(){cout << "In Base cons : " << typeid(*this).name() << endl;}
    virtual ~Base(){cout << "In Base dest : " << typeid(*this).name() << endl;}
    void f(){cout << "In f : " << typeid(*this).name() << endl;}
};
struct Middle : Base {
    Middle(){cout << "In Middle cons : " << typeid(*this).name() << endl;}
    ~Middle(){cout << "In Middle dest : " << typeid(*this).name() << endl;}
    void g(){cout << "In g : " << typeid(*this).name() << endl;}
};
struct Low : Middle {
    Low(){cout << "In Low cons : " << typeid(*this).name() << endl;}
    ~Low(){cout << "In Low dest : " << typeid(*this).name() << endl;}
    void h(){cout << "In h : " << typeid(*this).name() << endl;}
};

int main()
{
    Low l;
    l.f();
    l.g();
    l.h();
}

15.3

結構、長くなった。途中でファイルに保存することもできる。 Undo もしたかったが、面倒なのでやめた。 ランダムな手を打てるようにしてもよいと思う。 コンピュータは、はっきり言って弱い。
#include <cstring>
#include <cstdio>

class COthello {
public:
    enum Stat {Sp = 0, Wh = 1, Bl = 2};
private:
    int     m_no;               // 何手目か
    Stat    m_ban[10][10];      // 盤の状態
    bool    m_bComp[2];         // プレイヤーがコンピュータかどうか
        // py,px から i,j 方向の長さ
    int RevLen(int py,int px,int i,int j)const;
    static bool IsRange(int py,int px)
        {return py >= 1 && py <= 8 && px >= 1 && px <= 8;}
public:
    void Reset();
    COthello(){Reset();}
    int SaveFile(const char* nam)const;
    int LoadFile(const char* nam);
    int SetPlayer();
    void Show()const;
    int CanPut(int py,int px,Stat s)const;
    bool CanPut(Stat s)const;
    int Count(Stat s)const;
    void Think(Stat s,int* py,int* px)const;
    void Put(int py,int px,Stat s);
    void StartGame();
};

void COthello::Reset()
{
    int     i,j;
    for (i=0;i<10;++i) for (j=0;j<10;++j)
        m_ban[i][j] = Sp;
    m_ban[4][4] = m_ban[5][5] = Wh;
    m_ban[5][4] = m_ban[4][5] = Bl;
    m_bComp[0] = m_bComp[1] = false;
    m_no = 0;
}

int COthello::SaveFile(const char* nam)const
{
    FILE*   f = fopen(nam,"w");
    if (!f) return 1;
    fprintf(f,"%d %d %d\n",m_no,m_bComp[0]?1:0,m_bComp[1]?1:0);
    int     i,j;
    for (i=1;i<=8;++i) {
        for (j=1;j<=8;++j)
            fprintf(f," %d",static_cast<int>(m_ban[i][j]));
        fprintf(f,"\n");
    }
    fclose(f);
    return 0;
}

int COthello::LoadFile(const char* nam)
{
    FILE*   f = fopen(nam,"r");
    if (!f) return 1;
    char    buf[80];
    int     i,j,k;
    fgets(buf,sizeof(buf),f);
    sscanf(buf,"%d %d %d",&m_no,&i,&j);
    m_bComp[0] = i != 0;
    m_bComp[1] = j != 0;
    for (i=1;i<=8;++i) {
        for (j=1;j<=8;++j) {
            fscanf(f,"%d",&k);
            m_ban[i][j] = static_cast<Stat>(k);
        }
    }
    fclose(f);
    return 0;
}

int COthello::SetPlayer()
{
    char        buf[80];
    do {
        printf("Is 1st Player comp ? (y/n) ");
        if (!fgets(buf,sizeof(buf),stdin)) return 1;
    } while (*buf != 'y' && *buf != 'n');
    m_bComp[0] = *buf == 'y';
    do {
        printf("Is 2nd Player comp ? (y/n) ");
        if (!fgets(buf,sizeof(buf),stdin)) return 1;
    } while (*buf != 'y' && *buf != 'n');
    m_bComp[1] = *buf == 'y';
    return 0;
}

void COthello::Show()const
{
    int     i,j;
    char*   k[] = {"  ","○","●"};
    printf("%2d  1   2   3   4   5   6   7   8\n",m_no);
    printf(" ┌─┬─┬─┬─┬─┬─┬─┬─┐\n");
    for (i=1;i<=8;++i) {
        printf("%d",i);
        for (j=1;j<=8;++j)
            printf("│%s",k[static_cast<int>(m_ban[i][j])]);
        printf("│\n");
        if (i<8) printf(" ├─┼─┼─┼─┼─┼─┼─┼─┤\n");
        else     printf(" └─┴─┴─┴─┴─┴─┴─┴─┘\n");
    }
}

int COthello::RevLen(int py,int px,int i,int j)const
{
    int     res = 2;
    Stat    s = m_ban[py+i][px+j], t = s == Wh ? Bl : Wh;
    while (m_ban[py+i*res][px+j*res] == s) ++res;
    if (m_ban[py+i*res][px+j*res] != t) return 0;
    return res-1;
}

int COthello::CanPut(int py,int px,Stat s)const
{
    if (!IsRange(px,py)) return 0;
    if (m_ban[py][px] != Sp || s == Sp) return 0;
    int     i,j,k, res = 0;
    Stat    t = s == Wh ? Bl : Wh;
    for (i=-1;i<=1;++i) for (j=-1;j<=1;++j) {
        if (i == 0 && j == 0) continue;
        if (m_ban[py+i][px+j] == t && (k = RevLen(py,px,i,j)) > 0)
            res += k;
    }
    return res;
}

bool COthello::CanPut(Stat s)const
{
    int     i,j;
    for (i=1;i<=8;++i) for (j=1;j<=8;++j)
        if (CanPut(i,j,s)) return true;
    return false;
}

int COthello::Count(Stat s)const
{
    int     i,j, res = 0;
    for (i=1;i<=8;++i) for (j=1;j<=8;++j)
        if (m_ban[i][j] == s) ++res;
    return res;
}

void COthello::Think(Stat s,int* py,int* px)const
{
    static int chk[][8] = {{1,8,2,3,3,2,8,1},{8,9,6,7,7,6,9,8},
        {2,6,4,5,5,4,6,2},{3,7,5,0,0,5,7,3},{3,7,5,0,0,5,7,3},
        {2,6,4,5,5,4,6,2},{8,9,6,7,7,6,9,8},{1,8,2,3,3,2,8,1}};
    int     i,j,k,l,i1,j1,mx;
    for (k=1;k<=9;++k) {
        mx = 0;
        for (i=1;i<=8;++i) for (j=1;j<=8;++j) if (chk[i-1][j-1] == k) {
            l = CanPut(i,j,s);
            if (l > mx) {
                mx = l; i1 = i; j1 = j;
            }
        }
        if (mx > 0) {
            *py = i1; *px = j1;
            break;
        }
    }
}

void COthello::Put(int py,int px,Stat s)
{
    int     i,j,k;
    Stat    t = s == Wh ? Bl : Wh;
    if (!CanPut(py,px,s)) return;
    m_ban[py][px] = s;
    for (i=-1;i<=1;++i) for (j=-1;j<=1;++j) {
        if (i == 0 && j == 0) continue;
        if (m_ban[py+i][px+j] != t || !(k = RevLen(py,px,i,j)))
            continue;
        while (m_ban[py+i*k][px+j*k] = s, --k) ;
    }
}

void COthello::StartGame()
{
    char        buf[80];
    int         i,px,py;
    Stat        st, dt[] = {Bl,Wh};
    if (m_no == 0) {
        Reset();
        if (SetPlayer()) {
            fprintf(stderr,"Can't set player\n");
            return;
        }
    }
    Show();
    while (true) {
        st = dt[m_no%2];
        if (m_bComp[m_no%2]) {
            Think(st,&py,&px);
        } else {
            printf("%s 縦横 or S(ave) or Q(uit) ? ",m_no%2 ? "○" : "●");
            fgets(buf,sizeof(buf),stdin);
            if (*buf == 'Q') return;
            if (*buf == 'S') {
                while (true) {
                    printf("file name ? ");
                    fgets(buf,sizeof(buf),stdin);
                    buf[strlen(buf)-1] = 0;
                    if (!SaveFile(buf)) break;
                }
                return;
            }
            if (sscanf(buf,"%1d%1d",&py,&px) != 2) continue;
            if (!CanPut(py,px,st)) continue;
        }
        ++m_no;
        Put(py,px,st);
        Show();
        if (m_no == 60) {
            i = Count(st);
            if (i == 32) printf("Draw.\n");
            else printf("Player %d win!\n",(i<32?m_no:m_no+1)%2 + 1);
            break;
        } else if (!CanPut(dt[m_no%2])) {
            printf("Player %d win!\n",2-m_no%2);
            break;
        }
    }
}

int main(int argc,char** argv)
{
    COthello    c;
    if (argc > 1) c.LoadFile(argv[1]);
    c.StartGame();
}

15.4

VT100 端末で、候補を矢印キーで動かせる様にした。 コンピュータの思考ルーチンも変えてみた。
#include <cstring>
#include <cstdio>
#include <conio.h>

class COthello {
public:
    enum Stat {Sp = 0, Wh = 1, Bl = 2};
    enum Dir {DirUp,DirLeft,DirRight,DirDown};
private:
    int     m_no;               // 何手目か
    Stat    m_ban[10][10];      // 盤の状態
    bool    m_bComp[2];         // プレイヤーがコンピュータかどうか
    int     m_y,m_x;            // プロンプト
        // py,px から i,j 方向の長さ
    int RevLen(int py,int px,int i,int j)const;
    static bool IsRange(int py,int px)
        {return py >= 1 && py <= 8 && px >= 1 && px <= 8;}
public:
    void Reset();
    COthello(){Reset();}
    int SaveFile(const char* nam)const;
    int LoadFile(const char* nam);
    int SetPlayer();
    void Show()const;
    int CanPut(int py,int px,Stat s)const;
    bool CanPut(Stat s)const;
    int Count(Stat s)const;
    void Think(Stat s,int* py,int* px)const;
    void Put(int py,int px,Stat s);
    void StartGame();
    void PromptMove(Dir d,Stat s);
};

void COthello::Reset()
{
    int     i,j;
    for (i=0;i<10;++i) for (j=0;j<10;++j)
        m_ban[i][j] = Sp;
    m_ban[4][4] = m_ban[5][5] = Wh;
    m_ban[5][4] = m_ban[4][5] = Bl;
    m_bComp[0] = m_bComp[1] = false;
    m_no = 0;
    m_y = m_x = 0;
}

int COthello::SaveFile(const char* nam)const
{
    FILE*   f = fopen(nam,"w");
    if (!f) return 1;
    fprintf(f,"%d %d %d\n",m_no,m_bComp[0]?1:0,m_bComp[1]?1:0);
    int     i,j;
    for (i=1;i<=8;++i) {
        for (j=1;j<=8;++j)
            fprintf(f," %d",static_cast<int>(m_ban[i][j]));
        fprintf(f,"\n");
    }
    fclose(f);
    return 0;
}

int COthello::LoadFile(const char* nam)
{
    FILE*   f = fopen(nam,"r");
    if (!f) return 1;
    char    buf[80];
    int     i,j,k;
    fgets(buf,sizeof(buf),f);
    sscanf(buf,"%d %d %d",&m_no,&i,&j);
    m_bComp[0] = i != 0;
    m_bComp[1] = j != 0;
    for (i=1;i<=8;++i) {
        for (j=1;j<=8;++j) {
            fscanf(f,"%d",&k);
            m_ban[i][j] = static_cast<Stat>(k);
        }
    }
    fclose(f);
    return 0;
}

int COthello::SetPlayer()
{
    char        buf[80];
    do {
        printf("Is 1st Player comp ? (y/n) ");
        if (!fgets(buf,sizeof(buf),stdin)) return 1;
    } while (*buf != 'y' && *buf != 'n');
    m_bComp[0] = *buf == 'y';
    do {
        printf("Is 2nd Player comp ? (y/n) ");
        if (!fgets(buf,sizeof(buf),stdin)) return 1;
    } while (*buf != 'y' && *buf != 'n');
    m_bComp[1] = *buf == 'y';
    return 0;
}

void COthello::Show()const
{
    int     i,j;
    char*   k[] = {"  ","○","●"};
    printf("\033[H\033[J%2d  1   2   3   4   5   6   7   8  \n",m_no);
    printf(" ┌─┬─┬─┬─┬─┬─┬─┬─┐\n");
    for (i=1;i<=8;++i) {
        printf("%d",i);
        for (j=1;j<=8;++j) {
            if (m_y == i && m_x == j && m_ban[i][j] == Sp)
                printf("│◎");
            else printf("│%s",k[static_cast<int>(m_ban[i][j])]);
        }
        printf("│\n");
        if (i<8) printf(" ├─┼─┼─┼─┼─┼─┼─┼─┤\n");
        else     printf(" └─┴─┴─┴─┴─┴─┴─┴─┘\n");
    }
    printf("矢印で移動、スペースで決定\n");
}

int COthello::RevLen(int py,int px,int i,int j)const
{
    int     res = 2;
    Stat    s = m_ban[py+i][px+j], t = s == Wh ? Bl : Wh;
    while (m_ban[py+i*res][px+j*res] == s) ++res;
    if (m_ban[py+i*res][px+j*res] != t) return 0;
    return res-1;
}

int COthello::CanPut(int py,int px,Stat s)const
{
    if (!IsRange(px,py)) return 0;
    if (m_ban[py][px] != Sp || s == Sp) return 0;
    int     i,j,k, res = 0;
    Stat    t = s == Wh ? Bl : Wh;
    for (i=-1;i<=1;++i) for (j=-1;j<=1;++j) {
        if (i == 0 && j == 0) continue;
        if (m_ban[py+i][px+j] == t && (k = RevLen(py,px,i,j)) > 0)
            res += k;
    }
    return res;
}

bool COthello::CanPut(Stat s)const
{
    int     i,j;
    for (i=1;i<=8;++i) for (j=1;j<=8;++j)
        if (CanPut(i,j,s)) return true;
    return false;
}

int COthello::Count(Stat s)const
{
    int     i,j, res = 0;
    for (i=1;i<=8;++i) for (j=1;j<=8;++j)
        if (m_ban[i][j] == s) ++res;
    return res;
}

void COthello::Think(Stat s,int* py,int* px)const
{
    int     i,j,l,i1,j1,mx;
    mx = 0;
    for (i=1;i<=8;++i) for (j=1;j<=8;++j) {
        l = CanPut(i,j,s);
        if (l > mx) {
            mx = l; i1 = i; j1 = j;
        }
    }
    *py = i1; *px = j1;
}

void COthello::Put(int py,int px,Stat s)
{
    int     i,j,k;
    Stat    t = s == Wh ? Bl : Wh;
    if (!CanPut(py,px,s)) return;
    m_ban[py][px] = s;
    for (i=-1;i<=1;++i) for (j=-1;j<=1;++j) {
        if (i == 0 && j == 0) continue;
        if (m_ban[py+i][px+j] != t || !(k = RevLen(py,px,i,j)))
            continue;
        while (m_ban[py+i*k][px+j*k] = s, --k) ;
    }
}

void COthello::StartGame()
{
    int         i,px,py;
    Stat        st, dt[] = {Bl,Wh};
    if (m_no == 0) {
        Reset();
        if (SetPlayer()) {
            fprintf(stderr,"Can't set player\n");
            return;
        }
    }
    while (true) {
        st = dt[m_no%2];
        if (m_bComp[m_no%2]) {
            Think(st,&py,&px);
        } else {
            m_y = m_x = 1; PromptMove(DirDown,st);
            while (true) {
                Show();
                i = getch();
                if (i == 'Q') return;
                if (i == 'S') {
                    SaveFile("a.txt");
                    printf("Save a.txt\n");
                    return;
                }
                if (i == ' ') {
                    py = m_y;
                    px = m_x;
                    break;
                }
                if (i == 0xE0) {
                    i = getch();
                    switch (i) {
                    case 'H': PromptMove(DirUp,st); break;
                    case 'K': PromptMove(DirLeft,st); break;
                    case 'M': PromptMove(DirRight,st); break;
                    case 'P': PromptMove(DirDown,st); break;
                    }
                }
            }
        }
        ++m_no;
        Put(py,px,st);
        m_y = m_x = 0;
        if (m_no == 60) {
            Show();
            i = Count(st);
            if (i == 32) printf("Draw.\n");
            else printf("Player %d win!\n",(i<32?m_no:m_no+1)%2 + 1);
            break;
        } else if (!CanPut(dt[m_no%2])) {
            Show();
            printf("Player %d win!\n",2-m_no%2);
            break;
        }
    }
}

void COthello::PromptMove(Dir d,Stat s)
{
    int dir[][4] = {{0,-1,-1,8},{-1,0,8,-1},{1,0,-8,1},{0,1,1,-8}};
    int i,j, k = static_cast<int>(d), *dr = dir[k];
    i = m_y; j = m_x;
    while (true) {
        i += dr[0];
        j += dr[1];
        if (!IsRange(i,j)) {
            i += dr[2];
            j += dr[3];
        }
        if (!IsRange(i,j)) break;
        if (CanPut(i,j,s)) {
            m_y = i; m_x = j;
            break;
        }
    }
}

int main(int argc,char** argv)
{
    COthello    c;
    if (argc > 1) c.LoadFile(argv[1]);
    c.StartGame();
}

15.5

パス。

15.6

パス。

15.7

パス。

15.8

パス。

15.9

パス。

15.10

パス。

15.11

パス。

15.12

パス。
前章目次次章