プリント基板設計・シミュレーション

TOP > アポロレポート > コラム > 第130回 プログラミングについて『たまにはオセロで遊ぼうよ!その2』
コラム
2026/06/18

第130回 プログラミングについて『たまにはオセロで遊ぼうよ!その2』

アポロレポート

           プログラミングについて 第130回目

           『たまにはオセロで遊ぼうよ! その2』

 今回も前回に続いてオセロゲームを作っていきましょう。とりあえずは、前回までの全体リストです。アイコンを作成したものとしてちょっと直してあります。

[othello.rc]

 #include "afxres.h"
 #include "resource.h"

 OTHELLO DIALOG DISCARDABLE 0, 0, 250, 277
 STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION |
   WS_SYSMENU
 FONT 9, "MS Pゴシック"
 BEGIN
   CONTROL     "",IDFRAME_BOX,"Static",SS_BLACKFRAME | NOT WS_VISIBLE,4,
           31,240,239
   LTEXT      "",IDBLACK_STATUS,4,4,54,8
   LTEXT      "",IDWHITE_STATUS,4,15,54,8
   CTEXT      "",IDMESSAGE,62,9,182,8
 END

 OTHELLO_ICON      ICON  DISCARDABLE   "othello.ico"

[resource.h]

 #define IDBLACK_STATUS         1000
 #define IDWHITE_STATUS         1001
 #define IDMESSAGE            1002
 #define IDFRAME_BOX           1003

[othello.cpp]

 #include <afxwin.h>
 #include "res\resource.h"
 #include "dummy.h"

 class Othello : public CDialog
 {
  private:
   afx_msg void OnClose();

  public:
   Othello(HICON othello_icon);

  DECLARE_MESSAGE_MAP()
 };

 class OthelloApp : public CWinApp
 {
  public:
   virtual BOOL InitInstance();
   virtual ~OthelloApp();
 };

 BOOL OthelloApp::InitInstance()
 {
  m_pMainWnd=new Othello(LoadIcon("OTHELLO_ICON"));

  return TRUE;
 }

 OthelloApp::~OthelloApp()
 {
  delete m_pMainWnd;
 }

 afx_msg void Othello::OnClose()
 {
  PostQuitMessage(0);
 }

 Othello::Othello(HICON othello_icon)
 {
  Create("OTHELLO");
  SetIcon(othello_icon,TRUE);
  SetWindowText("オセロゲーム");
 }

 BEGIN_MESSAGE_MAP(Othello,CDialog)
  ON_WM_CLOSE()
 END_MESSAGE_MAP()

 OthelloApp OthelloApp;

まずリソースID IDFRAME_BOX の四角形のサイズから石のサイズを決定し、マスのデータを作成しましょう。このサイズはプログラムが起動されて、ウインドウが表示されるときに一回だけ行ないますので、Othello クラスのダイアログボックスの初期化の際に行います。Othello クラスの宣言に、afx_msg BOOL OnInitDialog(); を追加しダイアログボックスの初期化時の処理を行なえるようにします。更に石のサイズ、マスの構造体とデータメンバーの宣言を追加します。

 class Othello : public CDialog
 {
  private:
   int stone_size;

   struct box_data_strct{
    RECT rect;
    int stone;
   };
   struct box_data_strct box_data[MAX_BOX][MAX_BOX];

   afx_msg void OnClose();
   afx_msg BOOL OnInitDialog();

  public:
   Othello(HICON othello_icon);

  DECLARE_MESSAGE_MAP()
 };

MAX_BOX は縦または横のマスの数でオセロゲームでは 8 ですので、プログラムの最初に

 #define MAX_BOX 8

を定義しておきます。追加した変数 stone_size は石のサイズ(直径)を保持するものです。別に半径でも問題はありませんがここでは直径とします。更に追加した構造体、

 struct box_data_strct{
  RECT rect;
  int stone;
 };
 struct box_data_strct box_data[MAX_BOX][MAX_BOX];

のメンバー rect は1つのマスの矩形のサイズで Othello のダイアログボックスの初期化時に一度だけ設定をしておきます。stone はこのマスに何色の石が置かれているかを示すデータです。この構造体を MAX_BOX×MAX_BOX の2次元の配列として宣言します。

Othello クラスのダイアログボックスの初期化は、

 afx_msg BOOL Othello::OnInitDialog()
 {
  RECT rect;
  double xsize,ysize,t;
  int x,y;

  GetDlgItem(IDFRAME_BOX)->GetWindowRect(&rect);

  xsize=((double)(rect.right -rect.left))/((double)MAX_BOX);
  ysize=((double)(rect.bottom-rect.top ))/((double)MAX_BOX);

  if(xsize<ysize)t=xsize;
  else      t=ysize;

  stone_size=(int)(t*STONE_SIZE_RATIO);

  ScreenToClient(&rect);

  for(x=0;x<MAX_BOX;x++){
   for(y=0;y<MAX_BOX;y++){
    box_data[x][y].rect.left =(int)(rect.left+xsize* x  +0.5);
    box_data[x][y].rect.top  =(int)(rect.top +ysize* y  +0.5);
    box_data[x][y].rect.right =(int)(rect.left+xsize*(x+1)+0.5);
    box_data[x][y].rect.bottom=(int)(rect.top +ysize*(y+1)+0.5);
   }
  }

  return TRUE;
 }

としたのですが、少し説明が必要です。使用する変数は、

 RECT rect;

リソースに定義してある盤面の最大外形を示すコントロールの矩形のサイズを取得する目的に使用します。

 double xsize,ysize,t;

xsize と ysize は、1つのマスの縦と横のサイズを保持する目的に使用します。 t は一時的に使用する変数で特に明確な目的はありません。

 int x,y;

マス目の横と縦を走査する目的に使用します。

次に手続きの部分です。

  GetDlgItem(IDFRAME_BOX)->GetWindowRect(&rect);

リソースに定義してある盤面の最大外形を示すコントロールからそのサイズを取得します。Othello クラスは CDialog クラスから派生したもので、CDialog クラスは CWndクラスから派生しています。GetDlgItem は CWnd クラスのメンバー関数で、自分のウインドウ内のダイアログアイテムへのポインタを返すものです。CWnd クラスにはウインドウのサイズを取得するメンバー関数に GetWindowRect と GetClientRect の2種類があり、最初のものはウインドウの外形のサイズを、2番目のものはウインドウ内のクライアント領域(通常、ボタンやリスト等が配置されたり、図形を描画したりする領域です)のサイズを取得するものです。ちょっと注意しなければならないのはGetWindowRect はスクリーン座標系の座標値を返すのに対して、GetClientRect は自分のウインドウのクライアント領域の座標系で座標値を返すことです。

  xsize=((double)(rect.right -rect.left))/((double)MAX_BOX);
  ysize=((double)(rect.bottom-rect.top ))/((double)MAX_BOX);

取得した最大外形から1つのマスの横と縦のサイズを設定しています。GetWindowRectから取得できる値の型は整数値なのですが、ここではわざわざ double 型(浮動小数点型)にしているのには訳があります。それは、それぞれのマスの位置を設定するときの誤差を減らすのが目的です。例えば、取得した矩形の横方向の長さが 319 だったとすると、オセロのマス目の数は8個ですので、1つのマスの長さを整数で計算すると、

 319/8=39

になります。小数以下は切り捨てですので、0.875 は捨てられてしまいます。もしこの39 を8個並べると、

 39*8=304

となってしまって全体で 15 のズレが生じてしまいます。このような不都合が起きないように実数値でサイズを計算しておくのです。

 if(xsize<ysize)t=xsize;
 else      t=ysize;

 stone_size=(int)(t*STONE_SIZE_RATIO);

この部分は石のサイズを設定しているもので、リソース内の盤面の縦と横が同じでないときの保険のために1つのマス目のサイズの小さい方の STONE_SIZE_RATIO 倍を石のサイズにします。STONE_SIZE_RATIO は任意の倍率を予め定義しておきます。だいたい 0.8ぐらいが適当ではないでしょうか。

 ScreenToClient(&rect);

これはスクリーン座標系で取得した盤面の位置とサイズを Othello ウインドウのクライアント領域の座標系に変換しています。クライアント領域への描画はすべてクライアント領域の座標系で行ないますので、この変換が必要になります。

  for(x=0;x<MAX_BOX;x++){
   for(y=0;y<MAX_BOX;y++){
    box_data[x][y].rect.left =(int)(rect.left+xsize* x  +0.5);
    box_data[x][y].rect.top  =(int)(rect.top +ysize* y  +0.5);
    box_data[x][y].rect.right =(int)(rect.left+xsize*(x+1)+0.5);
    box_data[x][y].rect.bottom=(int)(rect.top +ysize*(y+1)+0.5);
   }
  }

ここでは、それぞれのマス目の位置を決定しています。0.5 を加えてから整数値に直すのは、小数以下の値を四捨五入する目的で行なっています。

ここまでのプログラムを実行しても前回と何も変わらないので楽しくはありませんが、また次回にしましょう。

そのお悩み、
アポロ技研に話してみませんか?

アポロ技研でワンランク上の物創りへ!
そのお悩み、アポロ技研に話してみませんか?