第329章 対戦を記録する


今回は、対戦の様子を記録できる機能を加えます。



メニューで「ファイル」「ゲーム開始」を選択すると、左のようなダイアログが 出てきます。対戦者の名前を記録しないときは「氏名を記録しない」にチェックを付けます。

勝敗が決まったら、対戦記録を保存するかどうかのメッセージボックスが出現します。 記録すると答えると、ファイル名を尋ねるダイアログボックスが出ます。

1つの対戦に対して1つのファイルに記録されます。拡張子は「gom」としました。



記録したファイルをメモ帳で開いてみると左のような感じになります。



では、プログラムを見てみましょう。

// gomoku04.rcの一部

/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

MYMENU MENU DISCARDABLE 
BEGIN
    POPUP "ファイル(F)"
    BEGIN
        MENUITEM "ゲーム開始(&S)",              IDM_START
        MENUITEM SEPARATOR
        MENUITEM "終了(&X)",                    IDM_END
    END
END


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

MYICON                  ICON    DISCARDABLE     "gomoku.ico"


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

MYNAME DIALOG DISCARDABLE  0, 0, 135, 93
STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "対戦者氏名登録"
FONT 9, "MS Pゴシック"
BEGIN
    EDITTEXT        IDC_SENTE,34,7,92,15,ES_AUTOHSCROLL
    EDITTEXT        IDC_GOTE,34,31,92,15,ES_AUTOHSCROLL
    DEFPUSHBUTTON   "OK",IDOK,7,72,50,14
    PUSHBUTTON      "キャンセル",IDCANCEL,78,72,50,14
    LTEXT           "先手",IDC_STATIC,7,7,24,11
    LTEXT           "後手",IDC_STATIC,7,33,24,11
    CONTROL         "氏名を記録しない",IDC_NONAME,"Button",BS_AUTOCHECKBOX | 
                    WS_TABSTOP,7,51,77,12
END
リソース・スクリプトの一部です。

対戦者氏名入力用のダイアログボックスが増えました。

//        gomoku04.cpp

#ifndef STRICT
    #define STRICT
#endif
#include <windows.h>
#include <windowsx.h>
#include "resource.h"

#define SHUI 30 //碁盤の周囲の幅
#define KANKAKU 20 //碁盤のマス目の間隔
#define STONESIZE 10 //碁石の半径

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK MyNameProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
BOOL MyMakeBan(HDC);
BOOL MyCircle(HDC, int, int, int); //盤(X,Y)に半径Rの円を描画
BOOL SetStone(HWND, int, int, char *);
BOOL MyStoneDraw(HDC);
int Is5(HWND, int, int);
int Is4(HWND); //4があるかどうか調べその数を返す
int Is3(HWND); //3があるかどうか調べその数を返す
BOOL MyRecord(HWND, char *, char *); //対戦を記録する
BOOL MyGetTime(char *);

char szClassName[] = "gomoku04";    //ウィンドウクラス
HINSTANCE hInst;
BOOL bSente = TRUE; //現在の差し手 先手:TRUE 後手:FALSE
BOOL bStart = FALSE; //対戦中かどうか
BOOL bShoHai = FALSE; //勝敗がついているか
int ban[15][15]; //0:石無し 1:先手 2:後手
int nTe; //何手目か
BOOL bName = FALSE; //対戦者氏名を記録するかどうか
char szSenteName[32], szGoteName[32]; //対戦者氏名

struct _tagRec{
    int row;
    int col;
} Rec[15 * 15];
ダイアログボックス関係のマクロを使うのでwindowsx.hをインクルードしています。

対戦者氏名入力用ダイアログボックスのプロシージャが増えました。

SetStone関数の引数が増えました。

その他いろいろなグローバル変数が追加になりました。nTeは今までは メインウィンドウのローカル変数でしたが、都合でグローバル変数にしました。

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
                   LPSTR lpsCmdLine, int nCmdShow)
{
    MSG msg;
    
    hInst = hCurInst;

    if (!InitApp(hCurInst))
        return FALSE;
    if (!InitInstance(hCurInst, nCmdShow)) 
        return FALSE;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

//ウィンドウ・クラスの登録

ATOM InitApp(HINSTANCE hInst)
{
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;    //プロシージャ名
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;//インスタンス
    wc.hIcon = LoadIcon(hInst, "MYICON");
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = "MYMENU";    //メニュー名
    wc.lpszClassName = (LPCSTR)szClassName;
    wc.hIconSm = LoadIcon(hInst, "MYICON");
    return (RegisterClassEx(&wc));
}

//ウィンドウの生成

BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
    HWND hWnd;

    hWnd = CreateWindow(szClassName,
            "猫でもわかる五目並べ", //タイトルバーにこの名前が表示されます
            WS_OVERLAPPEDWINDOW, //ウィンドウの種類
            CW_USEDEFAULT,    //X座標
            CW_USEDEFAULT,    //Y座標
            CW_USEDEFAULT,    //幅
            CW_USEDEFAULT,    //高さ
            NULL, //親ウィンドウのハンドル、親を作るときはNULL
            NULL, //メニューハンドル、クラスメニューを使うときはNULL
            hInst, //インスタンスハンドル
            NULL);
    if (!hWnd)
        return FALSE;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return TRUE;
}
これらの関数に変更はありません。
//ウィンドウプロシージャ

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    int id, x, y;
    PAINTSTRUCT ps;
    HDC hdc;
    static char szBuf[128];
    char szSashite[32];
    static HMENU hMenu;
    static char szStartTime[32], szEndTime[32]; //対戦開始日時、終了日時

    switch (msg) {
        case WM_CREATE:
            hMenu = GetMenu(hWnd);
            break;
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);
            MyMakeBan(hdc);
            MyStoneDraw(hdc);
            if (!bShoHai) {
                if (bSente) {
                    if (!bName) {
                        strcpy(szSashite, "先手●");
                    } else {
                        wsprintf(szSashite, "先手[%s]●", szSenteName);
                    }
                } else {
                    if (!bName) {
                        strcpy(szSashite, "後手○");
                    } else {
                        wsprintf(szSashite, "後手[%s]○", szGoteName);
                    }
                }
                wsprintf(szBuf, "差し手 = %s", szSashite);
                TextOut(hdc, 30, SHUI + KANKAKU * 14 + 30, szBuf, strlen(szBuf));
                wsprintf(szBuf, "第 %02d 手終了 現在 %02d 手目待ち", nTe, nTe + 1);
                TextOut(hdc, 30, SHUI + KANKAKU * 14 + 50, szBuf, strlen(szBuf));
            } else{
                TextOut(hdc, 30, SHUI + KANKAKU * 14 + 30, szBuf, strlen(szBuf));
            }
            EndPaint(hWnd, &ps);
            break;
        case WM_LBUTTONDOWN:
            if (bStart == FALSE)
                break;
            x = LOWORD(lp);
            y = HIWORD(lp);
            if (x >= SHUI && y >= SHUI &&
                x <= KANKAKU * 14 + SHUI &&
                y <= KANKAKU * 14 + SHUI) {
                if (SetStone(hWnd, x, y, szBuf) == FALSE) {
                    MyGetTime(szEndTime);
                    bShoHai = TRUE;
                    InvalidateRect(hWnd, NULL, TRUE);
                    if (MessageBox(hWnd, "対戦を記録しますか", "記録の確認", MB_ICONQUESTION | MB_YESNO) == IDYES)
                        MyRecord(hWnd, szStartTime, szEndTime);
                } else {
                    nTe++;
                }
            }
            break;
        case WM_MENUSELECT:
            if (bStart) {
                EnableMenuItem(hMenu, IDM_START, MF_BYCOMMAND | MF_GRAYED);
                DrawMenuBar(hWnd);
            } else {
                EnableMenuItem(hMenu, IDM_START, MF_BYCOMMAND | MF_ENABLED);
                DrawMenuBar(hWnd);
            }
            break;
        case WM_COMMAND:
            switch (LOWORD(wp)) {
                case IDM_START:
                    if (DialogBox(hInst, "MYNAME", hWnd, (DLGPROC)MyNameProc) == IDCANCEL)
                        break;
                    bStart = TRUE;
                    bShoHai = FALSE;
                    MyGetTime(szStartTime);
                    nTe = 0;
                    memset(ban, 0, sizeof(ban));
                    bSente = TRUE;
                    InvalidateRect(hWnd, NULL, TRUE);
                    break;
                case IDM_END:
                    SendMessage(hWnd, WM_CLOSE, 0, 0);
                    break;
            }
            break;
        case WM_CLOSE:
            id = MessageBox(hWnd,
                "終了してもよいですか",
                "終了確認",
                MB_YESNO | MB_ICONQUESTION);
            if (id == IDYES) {
                DestroyWindow(hWnd);
            }
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return (DefWindowProc(hWnd, msg, wp, lp));
    }
    return 0;
}
メインウィンドウのプロシージャです。

szBufとszSashiteの配列を今までより多く取りました。 szSashiteに実名が入る場合もあるからです。

WM_PAINTメッセージが来たときの処理も変更になりました。 勝敗がついていないとき(bShoHaiがFALSEの時)は、今まで通りです。 ただし、bNameがTRUEの時はszSashiteに実名が入ります。

勝敗がついたときは(bShoHaiがTRUEの時)はその結果を表示します。

WM_LBUTTONDOWNメッセージが来て、SetStone関数を呼んでこれがFALSEを返したとき (勝敗がついたとき)は、szEndTimeに現在の日時を書き込みます。そして、InvalidateRect 関数を呼んで再描画させます。この時szBufには、勝敗の結果が書かれています(SetStone関数が 書き込んでいます)。また、勝敗の記録をするかどうかを尋ねて、する場合はMyRecord関数を呼んでいます。

メニューからIDM_STARTが選択されたら、ダイアログボックスを出して対戦者の氏名を尋ねます。

そして、szStartTimeに現在の日時を書き込みます。

BOOL MyMakeBan(HDC hdc)
{
    int i;
    HBRUSH hBrush, hOldBrush;

    for (i = 0; i < 15; i++) {
        MoveToEx(hdc, SHUI, i *  KANKAKU + SHUI, NULL);
        LineTo(hdc, KANKAKU * (15 - 1) + SHUI, i * KANKAKU + SHUI);
    }
    for (i = 0; i < 15; i++) {
        MoveToEx(hdc, KANKAKU * i + SHUI, SHUI, NULL);
        LineTo(hdc, KANKAKU * i + SHUI, KANKAKU * (15 - 1) + SHUI);
    }

    hBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
    hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);

    MyCircle(hdc, 3, 3, 3);
    MyCircle(hdc, 11, 3, 3);
    MyCircle(hdc, 3, 11, 3);
    MyCircle(hdc, 11, 11, 3);
    MyCircle(hdc, 7, 7, 3);

    SelectObject(hdc, hOldBrush);
    
    return TRUE;
}

BOOL MyCircle(HDC hdc, int x, int y, int r)
{
    int x1, x2, y1, y2;

    x1 = x * KANKAKU + SHUI - r;
    x2 = x * KANKAKU + SHUI + r + 1;
    y1 = y * KANKAKU + SHUI - r;
    y2 = y * KANKAKU + SHUI + r + 1;
    Ellipse(hdc, x1, y1, x2, y2);

    return TRUE;
}
これらの関数に変更はありません。
BOOL SetStone(HWND hWnd, int x, int y, char *szBuf)
{
    int banx, bany, n4, n3, n5;
    char szSashite[64], szStr[128];

    if (bSente) {
        if (!bName) {
            strcpy(szSashite, "先手");
        } else {
            wsprintf(szSashite, "先手(%s)", szSenteName);
        }
    } else {
        if (!bName) {
            strcpy(szSashite, "後手");
        } else {
            wsprintf(szSashite, "後手(%s)", szGoteName);
        }
    }

    banx = (x - SHUI + KANKAKU / 2) / KANKAKU;
    bany = (y - SHUI + KANKAKU / 2) / KANKAKU;
    if (ban[banx][bany] != 0) {
        MessageBox(hWnd, "そこは置けません", "注意", MB_OK);
        return TRUE;
    }
    if (bSente) {
        ban[banx][bany] = 1;
    } else {
        ban[banx][bany] = 2;
    }
    //記録
    Rec[nTe].row = banx;
    Rec[nTe].col = bany;

    InvalidateRect(hWnd, NULL, FALSE);
    
    n5 = Is5(hWnd, banx, bany);
    if (n5 == 1) {
        bStart = FALSE;
        wsprintf(szStr, "%d手目で%sが勝ちました", nTe + 1, szSashite);
        MessageBox(hWnd, szStr, "5連", MB_OK);
        wsprintf(szBuf, "%d手目で%sが勝ちました", nTe + 1, szSashite);
        return FALSE;
    }
    if (n5 == -1) {
        bStart = FALSE;
        wsprintf(szStr, "%d手目で%sが多連で負けました", nTe + 1, szSashite);
        MessageBox(hWnd, szStr, "多連", MB_OK);
        wsprintf(szBuf, "%d手目で%sが多連で負けました", nTe + 1, szSashite);
        return FALSE;
    }
    n4 = Is4(hWnd);
    if (n4 != 0) {
        if (n4 > 1) {
            wsprintf(szStr, "%sに44が出来ました。\nまだ対戦を続けますか", szSashite);
            if (MessageBox(hWnd, szStr, "確認", MB_YESNO | MB_ICONQUESTION) == IDNO) {
                bStart = FALSE;
                wsprintf(szBuf, "%d手目で%sの44勝ちです", nTe + 1, szSashite);
                return FALSE;
            }
        }
        wsprintf(szStr, "%sに4が出来ました", szSashite);
        MessageBox(hWnd, szStr, "警告", MB_OK);

    }
    n3 = Is3(hWnd);
    if (n3 == 1 && n4 == 1) {
        wsprintf(szStr, "%sに43が出来ました\nまだ対戦を続けますか", szSashite);
        if (MessageBox(hWnd, szStr, "確認", MB_YESNO | MB_ICONQUESTION) == IDNO) {
            bStart = FALSE;
            wsprintf(szBuf, "%d手目で%sが43で勝ちました", nTe + 1, szSashite);
            return FALSE;
        }
    }
    if (n3 != 0) {
        if (n3 > 1) {
            wsprintf(szStr, "%sの反則負けです", szSashite);
            MessageBox(hWnd, szStr, "33", MB_OK);
            bStart = FALSE;
            wsprintf(szBuf, "%d手目で%sが33の反則負けです", nTe + 1, szSashite);
            return FALSE;
        }
        wsprintf(szStr, "%sに3が出来ました", szSashite);
        MessageBox(hWnd, szStr, "警告", MB_OK);
    }
    
    bSente = !bSente;
    InvalidateRect(hWnd, NULL, TRUE);
    return TRUE;
}
かなり書きかえました。

まず、この関数の引数が変更となりました。最後のszBufは クライアント領域の情報表示に使われます。

もし、対戦者氏名があれば、szSashiteに書き込みます。

1手ごとに、Rec配列に石を置いた位置を書き込むようにしました。

今までは、Is5関数を呼んでもし5連とか多連を発見したらその関数内で メッセージボックスなどを出すようにしていましたが、これをやめて この関数からメッセージボックスを出すようにしました。Is5関数の戻り値も 変更しました。現在の差し手が勝つときは1, 多連で負けるときは-1が戻るようにしました。 勝敗が決まったらszBufに結果を書き込んでクライアント領域に表示するようにしました。

「43」「44」「33」が出来たときも、szBufに結果を書き込むようにしました。

また、最後のInvalidateRect関数の最後の引数をTRUEにしました。これは、 氏名を表示するようになり、氏名の長短で前の氏名の一部が残ってしまうのを防ぐためです。 もっと別な工夫も出来ます。考えてみてください。

BOOL MyStoneDraw(HDC hdc)
{
    int i, j;
    HBRUSH hBrush, hOldBrush;

    for (i = 0; i < 15; i++) {
        for (j = 0; j < 15; j++) {
            if (ban[i][j] != 0) {
                if (ban[i][j] == 1) {
                    hBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
                    hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
                }
                if (ban[i][j] == 2) {
                    hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
                    hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
                }
                MyCircle(hdc, i, j, STONESIZE);
                SelectObject(hdc, hOldBrush);
            }
        }
    }
    return TRUE;
}
この関数に変更はありません。
//5が出来ていないか調べる
//1:現在の差し手に5が出来た -1:多連 0:5無し
int Is5(HWND hWnd, int x, int y)
{
    int nCount = 1, nJibun, i = 1;
    
    if (bSente)
        nJibun = 1;
    else
        nJibun = 2;
    
    while (x - i >= 0) {
        if (ban[x - i][y] == nJibun) {
            nCount++;
            i++;
        }
        else
            break;
    }
    i = 1;
    while (x + i < 15) {
        if (ban[x + i][y] == nJibun) {
            nCount++;
            i++;
        }
        else
            break;
    }
    if (nCount == 5)
        return 1;
    else if (nCount > 5)
        return -1;

    nCount = 1, i = 1;
    while (y - i >= 0) {
        if (ban[x][y - i] == nJibun) {
            nCount++;
            i++;
        }
        else
            break;
    }
    i = 1;
    while (y + i < 15) {
        if (ban[x][y + i] == nJibun) {
            nCount++;
            i++;
        }
        else
            break;
    }
    if (nCount == 5)
        return 1;
    else if (nCount > 5)
        return -1;

    nCount = 1; i = 1;
    while (x - i >= 0 && y - i >= 0) {
        if (ban[x - i][y - i] == nJibun) {
            nCount++;
            i++;
        } else
            break;
    }
    i = 1;
    while(x + i < 15 && y + i < 15) {
        if (ban[x + i][y + i] == nJibun) {
            nCount++;
            i++;
        } else
            break;
    }
    if (nCount == 5)
        return 1;
    else if (nCount > 5)
        return -1;

    nCount = 1, i = 1;
    while (x - i >= 0 && y + i < 15) {
        if (ban[x - i][y + i] == nJibun) {
            nCount++;
            i++;
        } else
            break;
    }
    i = 1;
    while (x + i < 15 && y - i >= 0) {
        if (ban[x + i][y - i] == nJibun) {
            nCount++;
            i++;
        } else
            break;
    }
    if (nCount == 5)
        return 1;
    else if (nCount > 5)
        return -1;

    return 0;
}
メッセージボックスを出すのをやめて、戻り値も変更しました。 従ってszSashite, szBufなどの変数もなくしました。
int Is4(HWND hWnd) //4になっていないか調べる
{
    int i, j, k, wa = 0, nCount0 = 0, nCount4 = 0, nJibunwa;
    BOOL bLoop = TRUE;

    if (bSente)
        nJibunwa = 4;
    else
        nJibunwa = 8;

    for (i = 0; i < 11; i++) {
        for (j = 0; j < 15; j++) {
            nCount0 = 0;
            wa = 0;
            for (k = 0; k < 5; k++) {
                if (ban[i + k][j] == 0) {
                    nCount0++;
                }
                wa = wa + ban[i + k][j];
                
            }
            if (nCount0 == 1 && wa == nJibunwa) {
                nCount4++;
                bLoop = FALSE;
                break;
            }
        }
        if (bLoop != TRUE)
            break;
    }

    bLoop = TRUE;
    for (i = 0; i < 15; i++) {
        for (j = 0; j < 11; j++) {
            nCount0 = 0;
            wa = 0;
            for (k = 0; k < 5; k++) {
                if (ban[i][j + k] == 0)
                    nCount0++;
                wa += ban[i][j + k];
            }
            if (nCount0 == 1 && wa == nJibunwa) {
                nCount4++;
                bLoop = FALSE;
                break;
            }
        }
        if (bLoop != TRUE)
            break;
    }

    bLoop = TRUE;
    for (i = 0; i < 11; i++) {
        for (j = 0; j < 11; j++) {
            nCount0 = 0;
            wa = 0;
            for (k = 0; k < 5; k++) {
                if (ban[i + k][j + k] == 0)
                    nCount0++;
                wa += ban[i + k][j + k];
            }
            if (nCount0 == 1 && wa == nJibunwa) {
                nCount4++;
                bLoop = FALSE;
                break;
            }
        }
        if (bLoop != TRUE)
            break;
    }

    bLoop = TRUE;
    for (i = 4; i < 15; i++) {
        for (j = 0; j < 11; j++) {
            nCount0 = 0;
            wa = 0;
            for (k = 0; k < 5; k++) {
                if (ban[i - k][j + k] == 0)
                    nCount0++;
                wa += ban[i - k][j + k];
            }
            if (nCount0 == 1 && wa == nJibunwa) {
                nCount4++;
                bLoop = FALSE;
                break;
            }
        }
        if (bLoop != TRUE)
            break;
    }
    return nCount4;
}

int Is3(HWND hWnd) {
    int i, j, k, nCount0 = 0, nCount3 = 0, wa = 0, nJibunwa, nJibun, nAite;
    BOOL bLoop = TRUE, bTome4 = FALSE;

    if (bSente) {
        nJibunwa = 3;
        nJibun = 1;
        nAite = 2;
    } else {
        nJibunwa = 6;
        nJibun = 2;
        nAite = 1;
    }

    for (i = 0; i < 10; i++) {
        for (j = 0; j < 15; j++) {
            nCount0 = 0;
            wa = 0;
            if (ban[i][j] == 0 && ban[i + 5][j] == 0) {
                for (k = 1; k <= 4; k++) {
                    wa += ban[i + k][j];
                    if (ban[i + k][j] == 0)
                        nCount0++;
                }
                
                if (nCount0 == 1 && wa == nJibunwa) {
                    if (i +  6 < 15) {
                        if (ban[i + 6][j] == nJibun) {
                            bTome4 = TRUE;
                        }
                    }
                    if (i - 1 > 0) {
                        if (ban[i - 1][j] == nJibun) {
                            bTome4 = TRUE;
                        }
                    }
                    if (!bTome4) {
                        nCount3++;
                        bLoop = FALSE;
                        break;
                    }
                }
            } 
        }
        if (bLoop != TRUE)
            break;
    }
    bLoop = TRUE;
    bTome4 = FALSE;
    
    for (i = 0; i < 15; i++){
        for (j = 0; j < 10; j++) {
            nCount0 = 0;
            wa = 0;
            if (ban[i][j] == 0 && ban[i][j + 5] == 0) {
                for (k = 1; k <= 4; k++) {
                    wa += ban[i][j + k];
                    if (ban[i][j + k] == 0)
                        nCount0++;
                }
                if (nCount0 == 1 && wa == nJibunwa) {
                    if (j +  6 < 15) {
                        if (ban[i][j + 6] == nJibun) {
                            bTome4 = TRUE;
                        }
                    }
                    if (j - 1 > 0) {
                        if (ban[i][j - 1] == nJibun) {
                            bTome4 = TRUE;
                        }
                    }
                    if (!bTome4) {
                        nCount3++;
                        bLoop = FALSE;
                        break;
                    }
                }
            } 
        }
        if (bLoop != TRUE)
            break;
    }

    bLoop = TRUE;
    
    for (i = 0; i < 10; i++) {
        for (j = 0; j < 10; j++) {
            nCount0 = 0;
            wa = 0;
            if (ban[i][j] == 0 && ban[i + 5][j + 5] == 0) {
                for (k = 1; k <= 4; k++) {
                    wa += ban[i + k][j + k];
                    if (ban[i + k][j + k] == 0)
                        nCount0++;
                }
                if (nCount0 == 1 && wa == nJibunwa) {
                    if (i +  6 < 15 && j + 6 < 15) {
                        if (ban[i + 6][j + 6] == nJibun) {
                            bTome4 = TRUE;
                        }
                    }
                    if (i - 1 > 0 && j - 1 > 0) {
                        if (ban[i - 1][j - 1] == nJibun) {
                            bTome4 = TRUE;
                        }
                    }
                    if (!bTome4) {
                        nCount3++;
                        bLoop = FALSE;
                        break;
                    }
                }
            }
        }
        if (bLoop != TRUE)
             break;
    
    }

    bLoop = TRUE;

    for (i = 5; i < 15; i++) {
        for (j = 0; j < 10; j++) {
            nCount0 = 0;
            wa = 0;
            if (ban[i][j] == 0 && ban[i - 5][j + 5] == 0) {
                for (k = 1; k <= 4; k++) {
                    wa += ban[i - k][j + k];
                    if (ban[i - k][j + k] == 0)
                        nCount0++;
                }
                if (nCount0 == 1 && wa == nJibunwa) {
                    if (i -  6 > 0 && j + 6 < 15) {
                        if (ban[i - 6][j + 6] == nJibun) {
                            bTome4 = TRUE;
                        }
                    }
                    if (j - 1 > 0) {
                        if (ban[i + 1][j - 1] == nJibun) {
                            bTome4 = TRUE;
                        }
                    }
                    if (!bTome4) {
                        nCount3++;
                        bLoop = FALSE;
                        break;
                    }
                }
            }
        }
        if (bLoop != TRUE)
            break;
    }
    return nCount3;
}
これらの関数に変更はありません。
BOOL MyRecord(HWND hWnd, char *szStartTime, char *szEndTime)
{
    OPENFILENAME of;
    char szFile[MAX_PATH], szFileTitle[MAX_PATH], szBuf[128];
    char *szTitle = "五目並べ対戦記録\r\n";
    HANDLE hFile;
    DWORD dwWritten;
    int i;

    strcpy(szFile, "");
    strcpy(szFileTitle, "");

    memset(&of, 0, sizeof(OPENFILENAME));
    of.lStructSize = sizeof(OPENFILENAME);
    of.hwndOwner = hWnd;
    of.lpstrTitle = "対戦記録";
    of.lpstrFilter = "五目ファイル(*.gom)\0*.gom\0All Files\0*.*\0\0";
    of.lpstrFile = szFile;
    of.lpstrFileTitle = szFileTitle;
    of.nMaxFile = MAX_PATH;
    of.nMaxFileTitle = MAX_PATH;
    of.Flags = OFN_OVERWRITEPROMPT;
    of.lpstrDefExt = "gom";

    if (!GetSaveFileName(&of))
        return FALSE;

    hFile = CreateFile(szFile, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL , NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        MessageBox(hWnd, "CreateFile関数が失敗しました", "Error", MB_OK);
        return FALSE;
    }
    WriteFile(hFile, szTitle, strlen(szTitle), &dwWritten, NULL);
    if (bName) {
        wsprintf(szBuf, "先手: %s\r\n後手: %s\r\n", szSenteName, szGoteName);
        WriteFile(hFile, szBuf, strlen(szBuf), &dwWritten, NULL);
    }
    wsprintf(szBuf, "開始:%s\r\n終了:%s\r\n", szStartTime, szEndTime);
    WriteFile(hFile, szBuf, strlen(szBuf), &dwWritten, NULL);
    for (i = 0; i <= nTe ; i++) {
        wsprintf(szBuf, "第%03d手--%02d,%02d\r\n", i + 1, Rec[i].row, Rec[i].col);
        WriteFile(hFile, szBuf, strlen(szBuf), &dwWritten, NULL);
        if (strlen(szBuf) != dwWritten) {
            MessageBox(hWnd, "書き込みエラー", "Error", MB_OK);
            CloseHandle(hFile);
            return FALSE;
        }
    }
        
    CloseHandle(hFile);
    return TRUE;
}
対戦をファイルに記録する関数です。 順番に見ていくと特に難しくはないですね。
BOOL MyGetTime(char *szTime)
{
    SYSTEMTIME st;

    GetLocalTime(&st);
    wsprintf(szTime, "%4d年%02d月%02d日%02d時%02d分%02d秒",
        st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);

    return TRUE;
}
szTimeに現在の日時を書き込む関数です。

SYSTEMTIME構造体、GetLocalTime関数については第34章 を参照してください。

LRESULT CALLBACK MyNameProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
    static HWND hSenteName, hGoteName, hNameCheck;

    switch (msg) {
        case WM_INITDIALOG:
            hSenteName = GetDlgItem(hDlg, IDC_SENTE);
            hGoteName = GetDlgItem(hDlg, IDC_GOTE);
            hNameCheck = GetDlgItem(hDlg, IDC_NONAME);
            Edit_LimitText(hSenteName, 31);
            Edit_LimitText(hGoteName, 31);
            Edit_SetText(hSenteName, szSenteName);
            Edit_SetText(hGoteName, szGoteName);
            return TRUE;
        case WM_COMMAND:
            switch (LOWORD(wp)) {
                case IDOK:
                    if (Button_GetCheck(hNameCheck) == BST_CHECKED) {
                        bName = FALSE;
                        EndDialog(hDlg, IDOK);
                        return TRUE;
                    } else {
                        bName = TRUE;
                        Edit_GetText(hSenteName, szSenteName, sizeof(szSenteName));
                        Edit_GetText(hGoteName, szGoteName, sizeof(szGoteName));
                    }
                    EndDialog(hDlg, IDOK);
                    return TRUE;
                case IDCANCEL:
                    EndDialog(hDlg, IDCANCEL);
                    return TRUE;
                case IDC_NONAME:
                    if (Button_GetCheck(hNameCheck) == BST_CHECKED) {
                        EnableWindow(hSenteName, FALSE);
                        EnableWindow(hGoteName, FALSE);
                    } else {
                        EnableWindow(hSenteName, TRUE);
                        EnableWindow(hGoteName, TRUE);
                    }
                    return TRUE;
            }
            return FALSE;
    }
    return FALSE;
}
対戦者氏名入力用ダイアログボックスのプロシージャです。

特にどうということもありませんがEdit_LimitTextマクロが初めて登場しました。 windowsx.hの中では次のように定義されています。

#define Edit_LimitText(hwndCtl, cchMax)  ((void)SNDMSG((hwndCtl), EM_LIMITTEXT, (WPARAM)(cchMax), 0L))
EM_LIMITTEXTメッセージは
SendMessage( 
  (HWND) hWnd,        //目的とするエディットコントロールのハンドル
  EM_LIMITTEXT,
  (WPARAM) wParam,    // 制限するテキスト
  (LPARAM) lParam     // 0を指定
);
のように使います。これで、エディットコントロールに入力できる文字数(ANSIではバイト数)を制限します。

さて、今回書き込んだ対戦ファイルを元に、対戦を再現するプログラムを作ってみてください。


[SDK第4部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 14/Jan/2002 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。