コラム
2026/10/30
プログラミングについて 第137回目
『たまにはオセロで遊ぼうよ! その9』
今回も前回の続きです。リソースは何も変更がないので、プログラム本体のリストです。
#include <afxwin.h>
#include "res\resource.h"
#include "dummy.h"
#define MAX_BOX 8
#define STONE_SIZE_RATIO 0.8
#define RED_COLOR RGB(255, 0, 0)
#define BLACK_COLOR RGB( 0, 0, 0)
#define WHITE_COLOR RGB(255,255,255)
#define DGREEN_COLOR RGB( 0,128, 0)
#define NO_STONE 0
#define BLACK_STONE 1
#define WHITE_STONE 2
#define NOTIFY_STONE 3
#define MAX_DRC 8
int xdrc_table[MAX_DRC]={ 1,-1, 0, 0, 1,-1, 1,-1 };
int ydrc_table[MAX_DRC]={ 0, 0, 1,-1, 1,-1,-1, 1 };
#define PERIOD12 36
#define PERIOD23 48
#define PERIOD1 1
#define PERIOD2 2
#define PERIOD3 3
class Othello : public CDialog
{
private:
int stone_size;
struct box_data_strct{
RECT rect;
int stone;
int point;
};
struct box_data_strct box_data[MAX_BOX][MAX_BOX];
CPen pen ,*old_pen ;
CBrush brush,*old_brush;
int pen_brush_using ;
int on_playing;
int player_stone;
int cpu_stone;
int current_stone;
int total_black_stone;
int total_white_stone;
struct able_data_strct{
int x,y;
int point;
};
struct able_data_strct able_data[MAX_BOX*MAX_BOX];
int lable_data;
afx_msg void OnClose();
afx_msg BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg void OnLButtonDown(UINT nflags,CPoint point);
afx_msg void OnPutStone();
afx_msg void OnAskOrder();
afx_msg void OnThinkPutStone();
void Redraw(CDC *dc);
void SetColor (CDC *dc,COLORREF outline_color,COLORREF fill_color);
void FreeColor(CDC *dc);
void InitializeBoxData();
void WriteStone(CDC *dc,int x,int y);
int IsEnablePutStone(
int stone,int xput,int yput,
int check_this=1,int drc= -1,
struct box_data_strct (*box_data_arg)[MAX_BOX]=NULL);
void WriteStoneTotal();
void PutStone(int stone,int xput,int yput);
void Delay(double delta_time);
void WriteOrder();
int EnablePutStoneAnywhere();
int WriteEndMessage();
void PurgeLButtonDownMessage();
int Random(int total);
void SortAbleData();
int CompareAbleData(struct able_data_strct *data,int i,int j);
void ThinkAbleData();
int ThinkAbleDataPass1();
void ThinkAbleDataPass2(int period);
void GetReverseBranchCount(
int stone,int xput,int yput,int *reverse,int *branch);
void TempPutStone(
struct box_data_strct (*tmp_box_data)[MAX_BOX],
int stone,int xput,int yput);
int GetStoneCount(
struct box_data_strct (*box_data)[MAX_BOX],int stone);
int IsExistPattern(char *string,char *pattern);
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("オセロゲーム");
}
afx_msg BOOL Othello::OnInitDialog()
{
int point_table[MAX_BOX][MAX_BOX]={
0, 0, 2, 3, 3, 2, 0, 0,
0, 0, 4, 4, 4, 4, 0, 0,
2, 4, 5, 5, 5, 5, 4, 2,
3, 4, 5, 0, 0, 5, 4, 3,
3, 4, 5, 0, 0, 5, 4, 3,
2, 4, 5, 5, 5, 5, 4, 2,
0, 0, 4, 4, 4, 4, 0, 0,
0, 0, 2, 3, 3, 2, 0, 0
};
RECT rect;
double xsize,ysize,t;
int x,y;
on_playing=0;
pen_brush_using=0;
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);
box_data[x][y].point=point_table[x][y];
}
}
InitializeBoxData();
PostMessage(WM_COMMAND,IDASK_ORDER);
return TRUE;
}
afx_msg void Othello::OnPaint()
{
CPaintDC dc(this);
Redraw(&dc);
}
void Othello::SetColor(CDC *dc,COLORREF outline_color,COLORREF fill_color)
{
FreeColor(dc);
pen.CreatePen(PS_SOLID,0,outline_color);
brush.CreateSolidBrush(fill_color);
old_pen =dc->SelectObject(&pen );
old_brush=dc->SelectObject(&brush);
pen_brush_using=1;
}
void Othello::FreeColor(CDC *dc)
{
if(pen_brush_using){
dc->SelectObject(old_pen );
dc->SelectObject(old_brush);
pen.DeleteObject();
brush.DeleteObject();
}
pen_brush_using=0;
}
void Othello::Redraw(CDC *dc)
{
RECT rect;
int x,y;
GetDlgItem(IDFRAME_BOX)->GetWindowRect(&rect);
ScreenToClient(&rect);
SetColor(dc,DGREEN_COLOR,DGREEN_COLOR);
dc->Rectangle(&rect);
SetColor(dc,BLACK_COLOR,BLACK_COLOR);
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
dc->MoveTo(box_data[x][y].rect.left ,box_data[x][y].rect.top );
dc->LineTo(box_data[x][y].rect.right,box_data[x][y].rect.top );
dc->LineTo(box_data[x][y].rect.right,box_data[x][y].rect.bottom);
dc->LineTo(box_data[x][y].rect.left ,box_data[x][y].rect.bottom);
dc->LineTo(box_data[x][y].rect.left ,box_data[x][y].rect.top );
}
}
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++)WriteStone(dc,x,y);
}
FreeColor(dc);
}
void Othello::InitializeBoxData()
{
int x,y;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
box_data[x][y].stone=NO_STONE;
}
}
box_data[MAX_BOX/2-1][MAX_BOX/2-1].stone=BLACK_STONE;
box_data[MAX_BOX/2 ][MAX_BOX/2-1].stone=WHITE_STONE;
box_data[MAX_BOX/2-1][MAX_BOX/2 ].stone=WHITE_STONE;
box_data[MAX_BOX/2 ][MAX_BOX/2 ].stone=BLACK_STONE;
total_black_stone=2;
total_white_stone=2;
WriteStoneTotal();
}
void Othello::WriteStone(CDC *dc,int x,int y)
{
COLORREF color;
int xc,yc;
if(box_data[x][y].stone== NO_STONE)return;
else if(box_data[x][y].stone== BLACK_STONE)color=BLACK_COLOR;
else if(box_data[x][y].stone== WHITE_STONE)color=WHITE_COLOR;
else if(box_data[x][y].stone==NOTIFY_STONE)color= RED_COLOR;
xc=(box_data[x][y].rect.left+box_data[x][y].rect.right )/2;
yc=(box_data[x][y].rect.top +box_data[x][y].rect.bottom)/2;
SetColor(dc,color,color);
dc->Ellipse(
xc-stone_size/2,yc-stone_size/2,
xc+stone_size/2,yc+stone_size/2);
FreeColor(dc);
}
afx_msg void Othello::OnLButtonDown(UINT nflags,CPoint point)
{
int x,y;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(point.x>box_data[x][y].rect.left
&& point.x<box_data[x][y].rect.right
&& point.y>box_data[x][y].rect.top
&& point.y<box_data[x][y].rect.bottom){
PostMessage(WM_COMMAND,IDPUT_STONE,(((player_stone<<8)|x)<<8)|y);
return;
}
}
}
}
afx_msg void Othello::OnAskOrder()
{
if(MessageBox(
"●は先攻、○は後攻です。\n\n"
"先攻しますか?",
"順番の問い合わせ",MB_YESNO)==IDYES){
player_stone=BLACK_STONE;
cpu_stone=WHITE_STONE;
}
else{
player_stone=WHITE_STONE;
cpu_stone=BLACK_STONE;
}
current_stone=BLACK_STONE;
on_playing=1;
WriteOrder();
if(player_stone==current_stone){
PurgeLButtonDownMessage();
}
else{
PostMessage(WM_PAINT);
PostMessage(WM_COMMAND,IDTHINK_PUT_STONE);
}
}
afx_msg void Othello::OnPutStone()
{
LPARAM lparam;
int stone,xput,yput;
if(!on_playing)return;
lparam=GetCurrentMessage()->lParam;
yput =lparam & 0x000000ff; lparam=lparam>>8;
xput =lparam & 0x000000ff; lparam=lparam>>8;
stone=lparam & 0x000000ff;
if(stone!=current_stone)return;
if(!IsEnablePutStone(stone,xput,yput))return;
PutStone(stone,xput,yput);
}
int Othello::IsEnablePutStone(
int stone,int xput,int yput,int check_this,int drc,
struct box_data_strct (*box_data_arg)[MAX_BOX])
{
int drc_from,drc_to,x,y,flag,xstone;
if(!box_data_arg)box_data_arg=box_data;
if(check_this && box_data_arg[xput][yput].stone!=NO_STONE)return 0;
xstone=stone==BLACK_STONE ? WHITE_STONE : BLACK_STONE;
if(drc== -1){ drc_from=0 ; drc_to=MAX_DRC-1; }
else { drc_from=drc; drc_to=drc ; }
for(drc=drc_from;drc<=drc_to;drc++){
flag=0;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] ,y=y +ydrc_table[drc]){
if(box_data_arg[x][y].stone==NO_STONE)break;
if(flag==0){
if(box_data_arg[x][y].stone==xstone)flag=1;
else break;
}
else{
if(box_data_arg[x][y].stone== stone)return 1;
}
}
}
return 0;
}
void Othello::WriteStoneTotal()
{
char tmp[31];
sprintf(tmp,"● %2d 個",total_black_stone);
SetDlgItemText(IDBLACK_STATUS,tmp);
sprintf(tmp,"○ %2d 個",total_white_stone);
SetDlgItemText(IDWHITE_STATUS,tmp);
}
void Othello::PutStone(int stone,int xput,int yput)
{
CClientDC dc(this);
int drc,x,y,loop,xstone,*add,*sub;
if(stone==BLACK_STONE){
xstone=WHITE_STONE;
add= &total_black_stone;
sub= &total_white_stone;
}
else{
xstone=BLACK_STONE;
add= &total_white_stone;
sub= &total_black_stone;
}
box_data[xput][yput].stone=stone;
(*add)++;
WriteStone(&dc,xput,yput);
Delay(0.3);
for(drc=0;drc<MAX_DRC;drc++){
if(!IsEnablePutStone(stone,xput,yput,0,drc))continue;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] , y=y +ydrc_table[drc]){
if(box_data[x][y].stone==stone)break;
box_data[x][y].stone=stone;
(*add)++;
(*sub)--;
WriteStone(&dc,x,y);
Delay(0.3);
}
}
WriteStoneTotal();
for(loop=0;loop<=1;loop++){
if(current_stone==BLACK_STONE)current_stone=WHITE_STONE;
else current_stone=BLACK_STONE;
if(EnablePutStoneAnywhere())break;
}
if(loop>1){
if(!WriteEndMessage()){
PostMessage(WM_CLOSE);
return;
}
InitializeBoxData();
WriteStoneTotal();
Redraw(&dc);
PostMessage(WM_COMMAND,IDASK_ORDER);
return;
}
WriteOrder();
if(player_stone==current_stone)PurgeLButtonDownMessage();
else PostMessage(WM_COMMAND,IDTHINK_PUT_STONE);
}
void Othello::Delay(double delta_time)
{
DWORD to,dt;
dt=(int)(delta_time*1000.0);
to=GetTickCount();
while(1){
if(GetTickCount()-to>dt)break;
}
}
void Othello::WriteOrder()
{
char tmp[81];
if(current_stone==player_stone){
sprintf(
tmp,"あなたの %s 石の番です",
current_stone==BLACK_STONE ? "●" : "○");
}
else{
strcpy(tmp,"お待ち下さい");
}
SetDlgItemText(IDMESSAGE,tmp);
}
int Othello::WriteEndMessage()
{
char tmp[81];
int diff;
diff=player_stone==WHITE_STONE ? total_white_stone-total_black_stone
: total_black_stone-total_white_stone;
if(diff< 0)sprintf(tmp,"あなたの %d 個の負けです。",-diff);
else if(diff==0)strcpy (tmp,"引き分けです。");
else sprintf(tmp,"あなたの %d 個の勝ちです。", diff);
strcat(tmp,"\nもう一度勝負しますか?");
if(MessageBox(tmp,"終了",MB_YESNO)==IDYES)return 1;
else return 0;
}
int Othello::EnablePutStoneAnywhere()
{
int x,y;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(IsEnablePutStone(current_stone,x,y))return 1;
}
}
return 0;
}
void Othello::PurgeLButtonDownMessage()
{
MSG msg;
while(PeekMessage(&msg,m_hWnd,WM_LBUTTONDOWN,WM_LBUTTONDOWN,PM_REMOVE));
}
afx_msg void Othello::OnThinkPutStone()
{
CClientDC dc(this);
int x,y,i;
lable_data= -1;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(box_data[x][y].stone!=NO_STONE )continue;
if(!IsEnablePutStone(cpu_stone,x,y))continue;
lable_data++;
able_data[lable_data].x =x;
able_data[lable_data].y =y;
able_data[lable_data].point=box_data[x][y].point;
}
}
ThinkAbleData();
SortAbleData();
for(i=0;i<lable_data;i++){
if(able_data[i].point>able_data[i+1].point){
lable_data=i;
break;
}
}
i=Random(lable_data+1);
x=able_data[i].x;
y=able_data[i].y;
Delay(1.0);
box_data[x][y].stone=NOTIFY_STONE;
WriteStone(&dc,x,y);
box_data[x][y].stone=NO_STONE;
Delay(0.3);
PostMessage(WM_COMMAND,IDPUT_STONE,(((cpu_stone<<8)|x)<<8)|y);
}
int Othello::Random(int total)
{
return GetTickCount()%total;
}
void Othello::SortAbleData()
{
struct able_data_strct tmp;
int i,j,gap;
gap=(lable_data+1)/2;
while(gap){
i=gap;
while(i<=lable_data){
j=i-gap;
while( j>=0 && CompareAbleData(able_data,j,j+gap)==1){
tmp =able_data[j ];
able_data[j ]=able_data[j+gap];
able_data[j+gap]=tmp;
j-=gap;
}
i++;
}
gap/=2;
}
}
int Othello::CompareAbleData(struct able_data_strct *data,int i,int j)
{
if(data[i].point>data[j].point)return 0;
if(data[i].point<data[j].point)return 1;
return 0;
}
void Othello::GetReverseBranchCount(
int stone,int xput,int yput,int *reverse,int *branch)
{
int drc,x,y,flag,xstone;
int rev;
*reverse=0;
*branch =0;
xstone=stone==BLACK_STONE ? WHITE_STONE : BLACK_STONE;
for(drc=0;drc<MAX_DRC;drc++){
flag=0;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] ,y=y +ydrc_table[drc]){
if(box_data[x][y].stone==NO_STONE)break;
if(flag==0){
if(box_data[x][y].stone==stone)break;
flag=1;
rev =1;
}
else{
if(box_data[x][y].stone==xstone){
rev++;
}
else{
(*reverse)+=rev;
(*branch )+=1 ;
break;
}
}
}
}
}
void Othello::ThinkAbleData()
{
int total;
total=total_black_stone+total_white_stone;
if(total<PERIOD12){
if(ThinkAbleDataPass1())return;
ThinkAbleDataPass2(PERIOD1);
}
else if(total<PERIOD23){
ThinkAbleDataPass1();
}
else{
ThinkAbleDataPass1();
}
}
void Othello::ThinkAbleDataPass2(int period)
{
#define BAD_DECREASE_PITCH 10
#define POINT_DECREASE_PITCH 5
struct rbdata_strct{
int index,reverse,branch;
struct rbdata_strct *next;
};
struct rbdata_strct rbdata[MAX_BOX * MAX_BOX];
struct rbdata_strct *top_rbdata,*rbp,*rbc;
struct box_data_strct tmp_box_data1[MAX_BOX][MAX_BOX];
struct box_data_strct tmp_box_data2[MAX_BOX][MAX_BOX];
int i,x,y;
for(i=0;i<=lable_data;i++){
memcpy(tmp_box_data1,box_data,sizeof(box_data));
TempPutStone(
tmp_box_data1,cpu_stone,able_data[i].x,able_data[i].y);
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(!IsEnablePutStone(player_stone,x,y,1,-1,tmp_box_data1))continue;
if((x==0 || x==MAX_BOX-1)
&& (y==0 || y==MAX_BOX-1)){
able_data[i].point-=BAD_DECREASE_PITCH;
continue;
}
memcpy(tmp_box_data2,tmp_box_data1,sizeof(tmp_box_data1));
TempPutStone(tmp_box_data2,player_stone,x,y);
if(GetStoneCount(tmp_box_data2,cpu_stone)==0){
able_data[i].point-=BAD_DECREASE_PITCH;
}
}
}
}
if(period!=PERIOD1)return;
top_rbdata=NULL;
for(i=0;i<=lable_data;i++){
GetReverseBranchCount(
cpu_stone,able_data[i].x,able_data[i].y,
&rbdata[i].reverse,&rbdata[i].branch);
rbdata[i].index=i;
rbdata[i].next =NULL;
rbp=NULL;
rbc=top_rbdata;
while(rbc){
if(rbdata[i].reverse < rbc->reverse)break;
if(rbdata[i].reverse == rbc->reverse
&& rbdata[i].branch == rbc->branch )break;
rbp=rbc;
rbc=rbc->next;
}
rbdata[i].next=rbc;
if(rbp==NULL)top_rbdata=rbdata+i;
else rbp->next=rbdata+i;
}
i=0;
rbp=NULL;
rbc=top_rbdata;
while(rbc){
if(rbp){
if(rbc->reverse!=rbp->reverse
|| rbc->branch !=rbp->branch )i-=POINT_DECREASE_PITCH;
}
able_data[rbc->index].point+=i;
rbp=rbc;
rbc=rbc->next;
}
#undef BAD_DECREASE_PITCH
#undef POINT_DECREASE_PITCH
}
void Othello::TempPutStone(
struct box_data_strct (*tmp_box_data)[MAX_BOX],
int stone,int xput,int yput)
{
int drc,x,y,xstone;
xstone=stone==BLACK_STONE ? WHITE_STONE : BLACK_STONE;
tmp_box_data[xput][yput].stone=stone;
for(drc=0;drc<MAX_DRC;drc++){
if(!IsEnablePutStone(stone,xput,yput,0,drc,tmp_box_data))continue;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] , y=y +ydrc_table[drc]){
if(tmp_box_data[x][y].stone==stone)break;
tmp_box_data[x][y].stone=stone;
}
}
}
int Othello::GetStoneCount(
struct box_data_strct (*box_data)[MAX_BOX],int stone)
{
int x,y,total;
total=0;
for(y=0;y<MAX_BOX;y++){
for(x=0;x<MAX_BOX;x++){
if(box_data[x][y].stone==stone)total++;
}
}
return total;
}
int Othello::ThinkAbleDataPass1()
{
struct pattern_data_strct{
char *pattern;
int add;
};
struct pattern_data_strct pattern_data[]={
{ "NxN" , 0 },
{ "NxPN" , -10 },
{ "NPxPN" , 5 },
{ "NPxCN" , -10 },
{ "NxCPN" , -20 },
{ "NxPCN" , 15 },
{ "NxPCPN" , -10 },
{ "NxPCP$" , -10 },
{ "NxCN" , 0 },
{ "NPxCPN" , 10 },
{ "PxCP" , 10 },
{ "NPxPCN" , -10 },
{ "NPxPCPN" , 10 },
{ "NPCPxN" , -10 },
{ "$x" , 20 },
{ "$CxN" , 10 },
{ "$CxP" , 10 },
{ "$CxPC" , 12 },
{ "$CPx" , 10 },
{ "$nPxPN" , 18 },
{ "$nPxP$" , 18 },
{ "$nPxCPN" , 18 },
{ "$nPxCP$" , 18 },
{ "$nPxPCN" , -10 },
{ "$nPxPC$" , 18 },
{ "$PxP" , 10 },
{ "$PCPxN$" , -10 },
{ "$PCxN$" , -10 },
{ "$CxCn" , 15 },
{ "$CxC$" , 5 },
{ "$PxCN" , -10 },
{ "$NxPCPC$" , -10 },
{ "$NxCPC$" , -10 },
{ "$CNxCP$" , -10 },
{ "CCxCN" , 10 },
{ "CCxCP$" , 10 },
{ "$CPCnN$" , -5 },
{ "$CPCn$" , -10 },
{ "$CxC$" , 10 },
{ "$PxNP" , -10 },
{ "" , 0 }
};
int able,x,y,dx,dy,add,i,flag;
char sts[MAX_BOX+1];
sts[MAX_BOX]='\0';
flag=0;
for(able=0;able<=lable_data;able++){
if(able_data[able].x!=0 && able_data[able].x!=MAX_BOX-1
&& able_data[able].y!=0 && able_data[able].y!=MAX_BOX-1)continue;
if(able_data[able].x==0 || able_data[able].x==MAX_BOX-1){
y=0; dy=1; x=able_data[able].x; dx=0;
}
else{
x=0; dx=1; y=able_data[able].y; dy=0;
}
for(i=0;i<MAX_BOX;i++,x+=dx,y+=dy){
if(x==able_data[able].x && y==able_data[able].y)sts[i]='x';
else if(box_data[x][y].stone ==player_stone )sts[i]='p';
else if(box_data[x][y].stone == cpu_stone )sts[i]='c';
else sts[i]='n';
}
add=0;
for(i=0;pattern_data[i].pattern[0];i++){
if(IsExistPattern(sts,pattern_data[i].pattern)){
if(abs(pattern_data[i].add)>abs(add))add=pattern_data[i].add;
}
}
able_data[able].point+=add;
if(add<= -10 || add>=10)flag=1;
}
return flag;
}
int Othello::IsExistPattern(char *string,char *pattern)
{
char tmp[81],c,*str,*s,*p;
int cont,loop;
strcpy(tmp,string);
for(loop=0;loop<=1;loop++){
if(loop==1){
int i,j,k;
j=strlen(tmp)-1;
k=j/2;
for(i=0;i<=k;i++,j--){ c=tmp[i]; tmp[i]=tmp[j]; tmp[j]=c; }
}
str=tmp;
while(*str){
s=str;
p=pattern;
c= *p++; if(c=='$')c= *p++;
if(c=='N' || c=='P' || c=='C'){ cont=1; c+='a'-'A'; }
else { cont=0; }
while(1){
if(*s==c || c=='*'){
if(!*p)return 1;
if(!*++s && *p=='$')return 1;
if(cont){
cont=2;
continue;
}
}
else{
if(cont!=2)break;
if(!*p )break;
}
c= *p++;
if(c=='N' || c=='P' || c=='C'){ cont=1; c+='a'-'A'; }
else { cont=0; }
}
if(*pattern=='$')break;
else str++;
}
}
return 0;
}
BEGIN_MESSAGE_MAP(Othello,CDialog)
ON_COMMAND(IDASK_ORDER,OnAskOrder)
ON_COMMAND(IDPUT_STONE,OnPutStone)
ON_COMMAND(IDTHINK_PUT_STONE,OnThinkPutStone)
ON_COMMAND(IDOK ,Default)
ON_COMMAND(IDCANCEL,Default)
ON_WM_LBUTTONDOWN()
ON_WM_PAINT()
ON_WM_CLOSE()
END_MESSAGE_MAP()
OthelloApp OthelloApp;
ここまで作ってくるとだんだん計算機も頭が良くなってきた感じがありますが、まだ中盤で大きな失敗をしてしまいます。計算機が打ったおかげでプレーヤーがコーナーを取れたり、プレーヤーがコーナーを取れるのを阻止しなかったりし、一気に負けになってしまうことがあるのです。そこで、その処理を ThinkAbleDataPass1() に追加しましょう。
ThinkAbleDataPass1() を次のようにします。
int Othello::ThinkAbleDataPass1()
{
struct pattern_data_strct{
char *pattern;
int add;
};
struct pattern_data_strct pattern_data[]={
{ "NxN" , 0 },
{ "NxPN" , -10 },
{ "NPxPN" , 5 },
{ "NPxCN" , -10 },
{ "NxCPN" , -20 },
{ "NxPCN" , 15 },
{ "NxPCPN" , -10 },
{ "NxPCP$" , -10 },
{ "NxCN" , 0 },
{ "NPxCPN" , 10 },
{ "PxCP" , 10 },
{ "NPxPCN" , -10 },
{ "NPxPCPN" , 10 },
{ "NPCPxN" , -10 },
{ "$x" , 20 },
{ "$CxN" , 10 },
{ "$CxP" , 10 },
{ "$CxPC" , 12 },
{ "$CPx" , 10 },
{ "$nPxPN" , 18 },
{ "$nPxP$" , 18 },
{ "$nPxCPN" , 18 },
{ "$nPxCP$" , 18 },
{ "$nPxPCN" , -10 },
{ "$nPxPC$" , 18 },
{ "$PxP" , 10 },
{ "$PCPxN$" , -10 },
{ "$PCxN$" , -10 },
{ "$CxCn" , 15 },
{ "$CxC$" , 5 },
{ "$PxCN" , -10 },
{ "$NxPCPC$" , -10 },
{ "$NxCPC$" , -10 },
{ "$CNxCP$" , -10 },
{ "CCxCN" , 10 },
{ "CCxCP$" , 10 },
{ "$CPCnN$" , -5 },
{ "$CPCn$" , -10 },
{ "$CxC$" , 10 },
{ "$PxNP" , -10 },
{ "" , 0 }
};
int able,x,y,dx,dy,add,i,flag;
char sts[MAX_BOX+1];
int csts[4][2];
sts[MAX_BOX]='\0';
flag=0;
for(able=0;able<=lable_data;able++){
add=0;
if( able_data[able].x==0 || able_data[able].x==MAX_BOX-1
|| (able_data[able].y==0 || able_data[able].y==MAX_BOX-1)){
if(able_data[able].x==0 || able_data[able].x==MAX_BOX-1){
y=0; dy=1; x=able_data[able].x; dx=0;
}
else{
x=0; dx=1; y=able_data[able].y; dy=0;
}
for(i=0;i<MAX_BOX;i++,x+=dx,y+=dy){
if(x==able_data[able].x && y==able_data[able].y)sts[i]='x';
else if(box_data[x][y].stone ==player_stone )sts[i]='p';
else if(box_data[x][y].stone == cpu_stone )sts[i]='c';
else sts[i]='n';
}
for(i=0;pattern_data[i].pattern[0];i++){
if(IsExistPattern(sts,pattern_data[i].pattern)){
if(abs(pattern_data[i].add)>abs(add)){
add=pattern_data[i].add;
}
}
}
}
if(GetNextPlayerPutCorner(
cpu_stone,player_stone,
able_data[able].x,able_data[able].y,csts)){
for(i=0;i<=3;i++){
if( csts[i][0] && !csts[i][1])add+=20;
else if(!csts[i][0] && csts[i][1])add-=20;
}
}
able_data[able].point+=add;
if(add<= -10 || add>=10)flag=1;
}
return flag;
}
変更したのは、周辺の処理はそのままとして、それ以外にもプレーヤーがコーナーを取れるのか、またはプレーヤーがコーナーを取れるのを阻止できるかを検査できるようにした部分です。
上のリストの、
if(GetNextPlayerPutCorner(
cpu_stone,player_stone,
able_data[able].x,able_data[able].y,csts)){
for(i=0;i<=3;i++){
if( csts[i][0] && !csts[i][1])add+=20;
else if(!csts[i][0] && csts[i][1])add-=20;
}
}
の GetNextPlayerPutCorner() は現在の状態でプレーヤーがコーナーを取れるか否かと、計算機が打ったときに相手がコーナーを取れるか否かを調べる関数で、今のところ未定義です。csts[*][0] には現在の状態、csts[*][1] には次の状態がセットされます。0 だったものが 1 になるときには計算機にとっては悪い手で、その逆は良い手になります。
GetNextPlayerPutCorner() を Othello クラスに次のように追加します。
int GetNextPlayerPutCorner(
int stone,int xstone,int xput,int yput,
int sts[4][2]);
そして、
int Othello::GetNextPlayerPutCorner(
int stone,int xstone,int xput,int yput,int sts[4][2])
{
int cpos_tbl[4][2]={
0 ,0 ,
0 ,MAX_BOX-1,
MAX_BOX-1,0 ,
MAX_BOX-1,MAX_BOX-1
};
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int loop,flag;
for(loop=0;loop<=3;loop++){
sts[loop][0]=IsEnablePutStone(xstone,cpos_tbl[loop][0],cpos_tbl[loop][1]);
}
memcpy(tmp_box_data,box_data,sizeof(box_data));
TempPutStone(tmp_box_data,stone,xput,yput);
flag=0;
for(loop=0;loop<=3;loop++){
sts[loop][1]=IsEnablePutStone(
xstone,cpos_tbl[loop][0],cpos_tbl[loop][1],1,-1,
tmp_box_data);
if(sts[loop][1]!=sts[loop][0])flag=1;
}
return flag;
}
やっていることは難しいものではありません。
{
int cpos_tbl[4][2]={
0 ,0 ,
0 ,MAX_BOX-1,
MAX_BOX-1,0 ,
MAX_BOX-1,MAX_BOX-1
};
これは4つのコーナーの位置のテーブルです。
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int loop,flag;
for(loop=0;loop<=3;loop++){
sts[loop][0]=IsEnablePutStone(xstone,cpos_tbl[loop][0],cpos_tbl[loop][1]);
}
ここでは計算機が打つ前の状態をセットしています。
memcpy(tmp_box_data,box_data,sizeof(box_data));
TempPutStone(tmp_box_data,stone,xput,yput);
ここで計算機が仮に石を打ちます。
flag=0;
for(loop=0;loop<=3;loop++){
sts[loop][1]=IsEnablePutStone(
xstone,cpos_tbl[loop][0],cpos_tbl[loop][1],1,-1,
tmp_box_data);
if(sts[loop][1]!=sts[loop][0])flag=1;
}
計算機が打った後のプレーヤーの状態をセットします。
return flag;
打つ前と打つ後の状態が変化したときに真をそれ以外は偽を返します。
}
ここまでで中盤は終了です。かなり人間に近い動作になったと思います。
残るは終盤です。終盤は残りのマス目すべてを計算して次に置くマス目を決めようという魂胆です。まず仮の状態にしていた ThinkAbleData() の終盤の部分を次のように直します。
void Othello::ThinkAbleData()
{
int total;
total=total_black_stone+total_white_stone;
if(total<PERIOD12){
if(ThinkAbleDataPass1())return;
ThinkAbleDataPass2(PERIOD1);
}
else if(total<PERIOD23){
ThinkAbleDataPass1();
}
else{
ThinkAbleDataPass3();
}
}
ThinkAbleDataPass3() は未定義ですので Othello クラスに、
void ThinkAbleDataPass3();
を追加します。そして、
void Othello::ThinkAbleDataPass3()
{
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int remain[MAX_BOX * MAX_BOX][2];
int lremain;
int able,x,y,min,max;
for(able=0;able<=lable_data;able++){
memcpy(tmp_box_data,box_data,sizeof(box_data));
TempPutStone(tmp_box_data,cpu_stone,able_data[able].x,able_data[able].y);
lremain= -1;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(tmp_box_data[x][y].stone==NO_STONE){
lremain++;
remain[lremain][0]=x;
remain[lremain][1]=y;
}
}
}
min= -1;
max= -1;
GetTotal(tmp_box_data,player_stone,cpu_stone,remain,lremain,&min,&max);
able_data[able].point=min;
}
}
としました。この関数は説明が必要です。
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
この変数は、石を打つ計算をするときに使用するマス目のデータです。
int remain[MAX_BOX * MAX_BOX][2];
int lremain;
この変数は、残りのマス目(石の置いていないマス目)の位置を入れるデータで、計算時になるべく無駄な動作をしないように用意します。
int able,x,y,min,max;
for(able=0;able<=lable_data;able++){
memcpy(tmp_box_data,box_data,sizeof(box_data));
現在のマス目の状態を計算用の変数にコピーします。
TempPutStone(tmp_box_data,cpu_stone,able_data[able].x,able_data[able].y);
自分の石を置きます。
lremain= -1;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(tmp_box_data[x][y].stone==NO_STONE){
lremain++;
remain[lremain][0]=x;
remain[lremain][1]=y;
}
}
}
このループでは空いているマス目の位置を取得します。
min= -1;
max= -1;
最後まで計算して最終の計算機の石の数の最小値と最大値を初期化します。
GetTotal(tmp_box_data,player_stone,cpu_stone,remain,lremain,&min,&max);
最終の計算機の最小値と最大値を計算します。この関数は未定義ですが、各マス目にお互いの石を打っていきすべての状態での計算機の取得できる石の最小値の最も小さい数を最小値とし、最大値の最も大きい数を最大値として返します。
able_data[able].point=min;
最小値を得点にします。最小値と最大値をどのように考えるかが考えていくとよく分からなくなってしまったので、とりあえずはどんなに少なくても一番多く取れるマス目を優先させることにしました。
次は未定義の GetTotal() です。この関数も Othello クラスに、
int GetTotal(
struct box_data_strct (*box_data)[MAX_BOX],
int stone,int xstone,
int (*remain)[2],int lremain,
int *min,int *max);
と追加します。そして、
int Othello::GetTotal(
struct box_data_strct (*box_data_arg)[MAX_BOX],
int stone,int xstone,
int (*remain)[2],int lremain,
int *min,int *max)
{
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int loop,t,i,lrem,rem[2];
if(lremain<0)return 0;
for(loop=0;loop<=1;loop++){
for(i=0;i<=lremain;i++){
if(IsEnablePutStone(
stone,remain[i][0],remain[i][1],1,-1,box_data_arg))break;
}
if(i<=lremain)break;
t=stone; stone=xstone; xstone=t;
}
if(loop>1)return 0;
lrem=lremain;
while(lrem>=0){
if(IsEnablePutStone(stone,remain[0][0],remain[0][1],1,-1,box_data_arg)){
memcpy(tmp_box_data,box_data_arg,sizeof(tmp_box_data));
TempPutStone(tmp_box_data,stone,remain[0][0],remain[0][1]);
if(!GetTotal(tmp_box_data,xstone,stone,remain+1,lremain-1,min,max)){
t=GetStoneCount(tmp_box_data,cpu_stone);
if(*min== -1 || t< *min)*min=t;
if(*max== -1 || t> *max)*max=t;
}
}
memcpy(rem ,remain[0],sizeof(rem) );
memcpy(remain ,remain+1 ,sizeof(rem)*lrem);
memcpy(remain+lrem,rem ,sizeof(rem) );
lrem--;
}
return 1;
}
としました。この関数も説明が必要です。
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
石を打つために使用するマス目のデータです。この GetTotal() は再帰呼び出しで計算をさせますので、石を打つ前の状態(引数 box_data_arg で与えられる)はそのままにしておき、新しいマス目のデータにその状態をコピーしてから計算をするようにします。
int loop,t,i,lrem,rem[2];
if(lremain<0)return 0;
残りのマス目がなくなったときには偽を返します。
for(loop=0;loop<=1;loop++){
for(i=0;i<=lremain;i++){
if(IsEnablePutStone(
stone,remain[i][0],remain[i][1],1,-1,box_data_arg))break;
}
if(i<=lremain)break;
t=stone; stone=xstone; xstone=t;
}
if(loop>1)return 0;
このループは次に石を打てるか否かを検査しています。ループは2回繰り返し、最初の検査のときに石を一つも打てないときには、石の色を変えてもう一度検査をします。この2回のループの検査で1つも打てないときは最後まで打ったということになりますので偽を返します。
下のループでは石を順番に打っていって最後まで計算させるのですが、その前に少し説明が必要です。マス目の残りの位置を入れた変数 remain の操作方法についてです。いま空きのマス目が5つ(A、B、C、D、E)あったとします。このとき lremain には remain の最終要素のインデックス番号が入っていますので4となっています。この値を別の変数にコピーしておきます(プログラムでは lrem)。
ABCDE lremain=4、lrem=4
いまAの位置に置けるか否かを判定したとき、Aは置けないマス目だったとします。このときにはAを lrem の位置に循環して移動させ lrem の値を1つ減らします。
BCDEA lremain=4、lrem=3
この状態は、残りのマス目は B、C、D、E の4つについて現在の色の石を置ける可能性があり、石を打った後の相手は C、D、E、A の4つのマス目に石を打てる可能性があるということになります。
次に B の位置に石を置けるとすると、まずその B の位置に現在の色の石を置き、マス目の残り位置を remain+1、残り数を lremain-1 とし、石の色を反対にして再び GetTotal() を呼び出します。この remain+1 は remain[1] あるいは &remain[1][0] と同じ意味ですので、呼び出された GetTotal() は C、D、E、Aの4つの残りのマス目について計算します。このようにして計算をしていき lrem の値が -1 になったら呼び出し側に戻るようにします。
lrem=lremain;
while(lrem>=0){
if(IsEnablePutStone(stone,remain[0][0],remain[0][1],1,-1,box_data_arg)){
memcpy(tmp_box_data,box_data_arg,sizeof(tmp_box_data));
石を置けるときにこの関数に引数で与えられたマス目の状態を計算用の変数にコピーします。
TempPutStone(tmp_box_data,stone,remain[0][0],remain[0][1]);
現在の色の石を置きます。
if(!GetTotal(tmp_box_data,xstone,stone,remain+1,lremain-1,min,max)){
石の色を反対にし、残りのマス目を渡して再び GetTotal() を呼び出します。この関数が偽を返したときには最後まで石を打ったときですので、このときの計算機の石の数を取得し、その最小値と最大値をセットします(下の行)。
t=GetStoneCount(tmp_box_data,cpu_stone);
if(*min== -1 || t< *min)*min=t;
if(*max== -1 || t> *max)*max=t;
}
}
memcpy(rem ,remain[0],sizeof(rem) );
memcpy(remain ,remain+1 ,sizeof(rem)*lrem);
memcpy(remain+lrem,rem ,sizeof(rem) );
lrem--;
現在検査した位置を残りのマス目の最後に循環して移動し、残りのマス目を1つ減らします。
}
return 1;
この行に到達したときは最後までは打っていないので真を返します。
ここまでで完成なのですが、実際に実験してみると ThinkAbleDataPass3() は計算を開始したままうんともすんとも言いませんでした。要するに計算に時間がかかっていたのです。考えてみれば、残りのマス目が16個になったときにこの計算をするので、組み合わせの数は16の階乗になり、200億の組み合わせになってしまうから当然といえば当然です。終盤の設定の値が悪かったのですね。そこで色々と試してみると、
#define PERIOD23 54
にするとプレーヤーにとってはストレスがない程度に計算が終了することが分かりました。但しこれは私のパソコンでのことですので(あまり速くないです)、パソコンにパワーがあるときにはもう少し小さい値に設定することも可能だと思います。
ここまででやっと完成です。長い間だらだらとオセロゲームを作ってきましたが、ゲームそのものの他にどのようにプログラムを作っていくのかということと、プログラミング上の小技も少しは理解して頂けたでしょうか。
退屈なときにはオセロでもやって気分転換をしてください。『オセロゲームがあるよ』と彼女(彼氏)を自分の部屋に呼ぶのも面白い使い方(?)ではないでしょうか(オセロじゃあ遊びにこないかもしれませんが)。これをエサにして呼んだときには、せめて一回ぐらいはオセロをして下さいね。後は知りません。
最後に完成したプログラムの全リストです。リストは私のプログラムソースをそのまま(タブはスペースに変換してあります)にしてあります。
[resource.h]
#define IDBLACK_STATUS 1000
#define IDWHITE_STATUS 1001
#define IDMESSAGE 1002
#define IDFRAME_BOX 1003
#define IDPUT_STONE 1004
#define IDASK_ORDER 1005
#define IDTHINK_PUT_STONE 1006
[othello.rc]
#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,3,
28,244,244
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"
[othello.cpp]
#include <afxwin.h>
#include "res\resource.h"
#include "dummy.h"
#define MAX_BOX 8
#define STONE_SIZE_RATIO 0.8
#define RED_COLOR RGB(255, 0, 0)
#define BLACK_COLOR RGB( 0, 0, 0)
#define WHITE_COLOR RGB(255,255,255)
#define DGREEN_COLOR RGB( 0,128, 0)
#define NO_STONE 0
#define BLACK_STONE 1
#define WHITE_STONE 2
#define NOTIFY_STONE 3
#define MAX_DRC 8
int xdrc_table[MAX_DRC]={ 1,-1, 0, 0, 1,-1, 1,-1 };
int ydrc_table[MAX_DRC]={ 0, 0, 1,-1, 1,-1,-1, 1 };
#define PERIOD12 36
#define PERIOD23 54
#define PERIOD1 1
#define PERIOD2 2
#define PERIOD3 3
/*=========================== Declare Othello class ===========================
* Declare othello class.
*/
class Othello : public CDialog
{
private:
int stone_size;
struct box_data_strct{
RECT rect;
int stone;
int point;
};
struct box_data_strct box_data[MAX_BOX][MAX_BOX];
CPen pen ,*old_pen ;
CBrush brush,*old_brush;
int pen_brush_using ;
int on_playing;
int player_stone;
int cpu_stone;
int current_stone;
int total_black_stone;
int total_white_stone;
struct able_data_strct{
int x,y;
int point;
};
struct able_data_strct able_data[MAX_BOX*MAX_BOX];
int lable_data;
afx_msg void OnClose();
afx_msg void OnPaint();
afx_msg BOOL OnInitDialog();
afx_msg void OnLButtonDown(UINT nflags,CPoint point);
afx_msg void OnPutStone();
afx_msg void OnAskOrder();
afx_msg void OnThinkPutStone();
void Redraw(CDC *dc);
void InitializeBoxData();
void SetColor(
CDC *dc,COLORREF outline_color,COLORREF fill_color);
void FreeColor(CDC *dc);
void WriteStone(CDC *dc,int x,int y);
int IsEnablePutStone(
int stone,int xput,int yput,
int check_this=1,int drc= -1,
struct box_data_strct (*box_data_arg)[MAX_BOX]=NULL);
void PutStone(int stone,int xput,int yput);
void WriteStoneTotal();
void Delay(double delta_time);
void WriteOrder();
int EnablePutStoneAnywhere();
int WriteEndMessage();
void PurgeLButtonDownMessage();
int Random(int total);
void SortAbleData();
int CompareAbleData(
struct able_data_strct *data,int i,int j);
void ThinkAbleData();
int ThinkAbleDataPass1();
void ThinkAbleDataPass2(int period);
void ThinkAbleDataPass3();
void GetReverseBranchCount(
int stone,int xput,int yput,
int *reverse,int *branch);
void TempPutStone(
struct box_data_strct (*tmp_box_data)[MAX_BOX],
int stone,int xput,int yput);
int GetStoneCount(
struct box_data_strct (*box_data)[MAX_BOX],
int stone);
int IsExistPattern(char *string,char *pattern);
int GetNextPlayerPutCorner(
int stone,int xstone,int xput,int yput,
int sts[4][2]);
int GetTotal(
struct box_data_strct (*box_data)[MAX_BOX],
int stone,int xstone,
int (*remain)[2],int lremain,
int *min,int *max);
public:
Othello(HICON othello_icon);
DECLARE_MESSAGE_MAP()
};
/*========================== Declare OthelloApp class =========================
* Declare OthelloApp class.
*/
class OthelloApp : public CWinApp
{
public:
virtual BOOL InitInstance();
virtual ~OthelloApp();
};
/*========================== OthelloApp::InitInstance =========================
* OthelloApp::InitInstance.
*/
BOOL OthelloApp::InitInstance()
{
m_pMainWnd=new Othello(LoadIcon("OTHELLO_ICON"));
return TRUE;
}
/*========================== OthelloApp::~OthelloApp ==========================
* OthelloApp::~OthelloApp.
*/
OthelloApp::~OthelloApp()
{
delete m_pMainWnd;
}
/*============================== Othello::OnClose =============================
* Othello::OnClose.
*/
afx_msg void Othello::OnClose()
{
PostQuitMessage(0);
}
/*============================== Othello::Othello =============================
* Othello::Othello.
*/
Othello::Othello(HICON othello_icon)
{
Create("OTHELLO");
SetIcon(othello_icon,TRUE);
SetWindowText("オセロゲーム");
}
/*============================== Othello::OnPaint =============================
* Othello::OnPaint.
*/
afx_msg void Othello::OnPaint()
{
CPaintDC dc(this);
Redraw(&dc);
}
/*=========================== Othello::OnInitDialog ===========================
* Othello::OnInitDialog.
*/
afx_msg BOOL Othello::OnInitDialog()
{
int point_table[MAX_BOX][MAX_BOX]={
0, 0, 2, 3, 3, 2, 0, 0,
0, 0, 4, 4, 4, 4, 0, 0,
2, 4, 5, 5, 5, 5, 4, 2,
3, 4, 5, 0, 0, 5, 4, 3,
3, 4, 5, 0, 0, 5, 4, 3,
2, 4, 5, 5, 5, 5, 4, 2,
0, 0, 4, 4, 4, 4, 0, 0,
0, 0, 2, 3, 3, 2, 0, 0
};
RECT rect;
double xsize,ysize,t;
int x,y;
on_playing=0;
pen_brush_using=0;
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);
box_data[x][y].point=point_table[x][y];
}
}
InitializeBoxData();
PostMessage(WM_COMMAND,IDASK_ORDER);
return TRUE;
}
/*========================= Othello::InitializeBoxData ========================
* Othello::InitializeBoxData.
*/
void Othello::InitializeBoxData()
{
int x,y;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
box_data[x][y].stone=NO_STONE;
}
}
box_data[MAX_BOX/2-1][MAX_BOX/2-1].stone=BLACK_STONE;
box_data[MAX_BOX/2 ][MAX_BOX/2-1].stone=WHITE_STONE;
box_data[MAX_BOX/2-1][MAX_BOX/2 ].stone=WHITE_STONE;
box_data[MAX_BOX/2 ][MAX_BOX/2 ].stone=BLACK_STONE;
total_black_stone=2;
total_white_stone=2;
WriteStoneTotal();
}
/*============================== Othello::Redraw ==============================
* Othello::Redraw.
*/
void Othello::Redraw(CDC *dc)
{
RECT rect;
int x,y;
//Fill background.
GetDlgItem(IDFRAME_BOX)->GetWindowRect(&rect);
ScreenToClient(&rect);
SetColor(dc,DGREEN_COLOR,DGREEN_COLOR);
dc->Rectangle(&rect);
//Write frame.
SetColor(dc,BLACK_COLOR,BLACK_COLOR);
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
dc->MoveTo(box_data[x][y].rect.left ,box_data[x][y].rect.top );
dc->LineTo(box_data[x][y].rect.right,box_data[x][y].rect.top );
dc->LineTo(box_data[x][y].rect.right,box_data[x][y].rect.bottom);
dc->LineTo(box_data[x][y].rect.left ,box_data[x][y].rect.bottom);
dc->LineTo(box_data[x][y].rect.left ,box_data[x][y].rect.top );
}
}
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++)WriteStone(dc,x,y);
}
FreeColor(dc);
}
/*============================= Othello::SetColor =============================
* Othello::SetColor.
*/
void Othello::SetColor(CDC *dc,COLORREF outline_color,COLORREF fill_color)
{
FreeColor(dc);
pen.CreatePen(PS_SOLID,0,outline_color);
brush.CreateSolidBrush(fill_color);
old_pen =dc->SelectObject(&pen );
old_brush=dc->SelectObject(&brush);
pen_brush_using=1;
}
/*============================= Othello::FreeColor ============================
* Othello::FreeColor.
*/
void Othello::FreeColor(CDC *dc)
{
if(pen_brush_using){
dc->SelectObject(old_pen );
dc->SelectObject(old_brush);
pen.DeleteObject();
brush.DeleteObject();
}
pen_brush_using=0;
}
/*============================ Othello::WriteStone ============================
* Othello::WriteStone.
*/
void Othello::WriteStone(CDC *dc,int x,int y)
{
COLORREF color;
int xc,yc;
if(box_data[x][y].stone== NO_STONE)return;
else if(box_data[x][y].stone== BLACK_STONE)color=BLACK_COLOR;
else if(box_data[x][y].stone== WHITE_STONE)color=WHITE_COLOR;
else if(box_data[x][y].stone==NOTIFY_STONE)color= RED_COLOR;
xc=(box_data[x][y].rect.left+box_data[x][y].rect.right )/2;
yc=(box_data[x][y].rect.top +box_data[x][y].rect.bottom)/2;
SetColor(dc,color,color);
dc->Ellipse(
xc-stone_size/2,yc-stone_size/2,
xc+stone_size/2,yc+stone_size/2);
FreeColor(dc);
}
/*=========================== Othello::OnLButtonDown ==========================
* Othello::OnLButtonDown.
*/
afx_msg void Othello::OnLButtonDown(UINT nflags,CPoint point)
{
int x,y;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(point.x>box_data[x][y].rect.left
&& point.x<box_data[x][y].rect.right
&& point.y>box_data[x][y].rect.top
&& point.y<box_data[x][y].rect.bottom){
PostMessage(
WM_COMMAND,IDPUT_STONE,(((player_stone<<8)|x)<<8)|y);
return;
}
}
}
}
/*============================ Othello::OnAskOrder ============================
* Othello::OnAskOrder.
*/
afx_msg void Othello::OnAskOrder()
{
if(MessageBox(
"●は先攻、○は後攻です。\n\n"
"先攻しますか?",
"順番の問い合わせ",MB_YESNO)==IDYES){
player_stone=BLACK_STONE;
cpu_stone=WHITE_STONE;
}
else{
player_stone=WHITE_STONE;
cpu_stone=BLACK_STONE;
}
current_stone=BLACK_STONE;
on_playing=1;
WriteOrder();
if(player_stone==current_stone){
PurgeLButtonDownMessage();
}
else{
PostMessage(WM_PAINT);
PostMessage(WM_COMMAND,IDTHINK_PUT_STONE);
}
}
/*========================= Othello::IsEnablePutStone =========================
* Othello::IsEnablePutStone.
*/
int Othello::IsEnablePutStone(
int stone,int xput,int yput,int check_this,int drc,
struct box_data_strct (*box_data_arg)[MAX_BOX])
{
int drc_from,drc_to,x,y,flag,xstone;
if(!box_data_arg)box_data_arg=box_data;
if(check_this && box_data_arg[xput][yput].stone!=NO_STONE)return 0;
xstone=stone==BLACK_STONE ? WHITE_STONE : BLACK_STONE;
if(drc== -1){ drc_from=0 ; drc_to=MAX_DRC-1; }
else { drc_from=drc; drc_to=drc ; }
for(drc=drc_from;drc<=drc_to;drc++){
flag=0;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] ,y=y +ydrc_table[drc]){
if(box_data_arg[x][y].stone==NO_STONE)break;
if(flag==0){
if(box_data_arg[x][y].stone==xstone)flag=1;
else break;
}
else{
if(box_data_arg[x][y].stone== stone)return 1;
}
}
}
return 0;
}
/*========================== Othello::WriteStoneTotal =========================
* Othello::WriteStoneTotal.
*/
void Othello::WriteStoneTotal()
{
char tmp[31];
sprintf(tmp,"● %2d 個",total_black_stone);
SetDlgItemText(IDBLACK_STATUS,tmp);
sprintf(tmp,"○ %2d 個",total_white_stone);
SetDlgItemText(IDWHITE_STATUS,tmp);
}
/*============================ Othello::OnPutStone ============================
* Othello::OnPutStone.
*/
afx_msg void Othello::OnPutStone()
{
LPARAM lparam;
int stone,xput,yput;
/*
* " Check playing "
*/
if(!on_playing)return;
/*
* " Get parameters "
*/
lparam=GetCurrentMessage()->lParam;
yput =lparam & 0x000000ff; lparam=lparam>>8;
xput =lparam & 0x000000ff; lparam=lparam>>8;
stone=lparam & 0x000000ff;
if(stone!=current_stone)return;
/*
* " Check enable put stone "
*/
if(!IsEnablePutStone(stone,xput,yput))return;
/*
* " Put stone "
*/
PutStone(stone,xput,yput);
}
/*=============================== Othello::Delay ==============================
* Othello::Delay.
*/
void Othello::Delay(double delta_time)
{
DWORD to,dt;
dt=(int)(delta_time*1000.0);
to=GetTickCount();
while(1){
if(GetTickCount()-to>dt)break;
}
}
/*============================ Othello::WriteOrder ============================
* Othello::WriteOrder.
*/
void Othello::WriteOrder()
{
char tmp[81];
if(current_stone==player_stone){
sprintf(
tmp,"あなたの %s 石の番です",
current_stone==BLACK_STONE ? "●" : "○");
}
else{
strcpy(tmp,"お待ち下さい");
}
SetDlgItemText(IDMESSAGE,tmp);
}
/*============================= Othello::PutStone =============================
* Othello::PutStone.
*/
void Othello::PutStone(int stone,int xput,int yput)
{
CClientDC dc(this);
int drc,x,y,loop,xstone,*add,*sub;
if(stone==BLACK_STONE){
xstone=WHITE_STONE;
add= &total_black_stone;
sub= &total_white_stone;
}
else{
xstone=BLACK_STONE;
add= &total_white_stone;
sub= &total_black_stone;
}
box_data[xput][yput].stone=stone;
(*add)++;
WriteStone(&dc,xput,yput);
Delay(0.25);
for(drc=0;drc<MAX_DRC;drc++){
if(!IsEnablePutStone(stone,xput,yput,0,drc))continue;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] , y=y +ydrc_table[drc]){
if(box_data[x][y].stone==stone)break;
box_data[x][y].stone=stone;
(*add)++;
(*sub)--;
WriteStone(&dc,x,y);
Delay(0.25);
}
}
WriteStoneTotal();
for(loop=0;loop<=1;loop++){
if(current_stone==BLACK_STONE)current_stone=WHITE_STONE;
else current_stone=BLACK_STONE;
if(EnablePutStoneAnywhere())break;
}
if(loop>1){
if(!WriteEndMessage()){
PostMessage(WM_CLOSE);
return;
}
InitializeBoxData();
WriteStoneTotal();
Redraw(&dc);
PostMessage(WM_COMMAND,IDASK_ORDER);
return;
}
WriteOrder();
if(player_stone==current_stone)PurgeLButtonDownMessage();
else PostMessage(WM_COMMAND,IDTHINK_PUT_STONE);
}
/*====================== Othello::EnablePutStoneAnywhere ======================
* Othello::EnablePutStoneAnywhere.
*/
int Othello::EnablePutStoneAnywhere()
{
int x,y;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(IsEnablePutStone(current_stone,x,y))return 1;
}
}
return 0;
}
/*========================== Othello::WriteEndMessage =========================
* Othello::WriteEndMessage.
*/
int Othello::WriteEndMessage()
{
char tmp[81];
int diff;
diff=player_stone==WHITE_STONE ? total_white_stone-total_black_stone
: total_black_stone-total_white_stone;
if(diff< 0)sprintf(tmp,"あなたの %d 個の負けです。",-diff);
else if(diff==0)strcpy (tmp,"引き分けです。");
else sprintf(tmp,"あなたの %d 個の勝ちです。", diff);
strcat(tmp,"\nもう一度勝負しますか?");
if(MessageBox(tmp,"終了",MB_YESNO)==IDYES)return 1;
else return 0;
}
/*====================== Othello::PurgeLButtonDownMessage =====================
* Othello::PurgeLButtonDownMessage.
*/
void Othello::PurgeLButtonDownMessage()
{
MSG msg;
while(PeekMessage(&msg,m_hWnd,WM_LBUTTONDOWN,WM_LBUTTONDOWN,PM_REMOVE));
}
/*============================== Othello::Random ==============================
* Othello::Random.
*/
int Othello::Random(int total)
{
return GetTickCount()%total;
}
/*=========================== Othello::SortAbleData ===========================
* Othello::SortAbleData.
*/
void Othello::SortAbleData()
{
struct able_data_strct tmp;
int i,j,gap;
gap=(lable_data+1)/2;
while(gap){
i=gap;
while(i<=lable_data){
j=i-gap;
while( j>=0 && CompareAbleData(able_data,j,j+gap)==1){
tmp =able_data[j ];
able_data[j ]=able_data[j+gap];
able_data[j+gap]=tmp;
j-=gap;
}
i++;
}
gap/=2;
}
}
/*========================== Othello::CompareAbleData =========================
* Othello::CompareAbleData.
*/
int Othello::CompareAbleData(struct able_data_strct *data,int i,int j)
{
if(data[i].point>data[j].point)return 0;
if(data[i].point<data[j].point)return 1;
return 0;
}
/*========================== Othello::OnThinkPutStone =========================
* Othello::OnThinkPutStone.
*/
afx_msg void Othello::OnThinkPutStone()
{
CClientDC dc(this);
int x,y,i;
lable_data= -1;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(box_data[x][y].stone!=NO_STONE )continue;
if(!IsEnablePutStone(cpu_stone,x,y))continue;
lable_data++;
able_data[lable_data].x =x;
able_data[lable_data].y =y;
able_data[lable_data].point=box_data[x][y].point;
}
}
ThinkAbleData();
SortAbleData();
for(i=0;i<lable_data;i++){
if(able_data[i].point>able_data[i+1].point){
lable_data=i;
break;
}
}
i=Random(lable_data+1);
x=able_data[i].x;
y=able_data[i].y;
Delay(0.8);
box_data[x][y].stone=NOTIFY_STONE;
WriteStone(&dc,x,y);
box_data[x][y].stone=NO_STONE;
Delay(0.25);
PostMessage(WM_COMMAND,IDPUT_STONE,(((cpu_stone<<8)|x)<<8)|y);
}
/*=========================== Othello::ThinkAbleData ==========================
* Othello::ThinkAbleData.
*/
void Othello::ThinkAbleData()
{
int total;
total=total_black_stone+total_white_stone;
if(total<PERIOD12){
if(ThinkAbleDataPass1())return;
ThinkAbleDataPass2(PERIOD1);
}
else if(total<PERIOD23){
ThinkAbleDataPass1();
}
else{
ThinkAbleDataPass3();
}
}
/*======================= Othello::GetReverseBranchCount ======================
* Othello::GetReverseBranchCount.
*/
void Othello::GetReverseBranchCount(
int stone,int xput,int yput,int *reverse,int *branch)
{
int drc,x,y,flag,xstone;
int rev;
*reverse=0;
*branch =0;
xstone=stone==BLACK_STONE ? WHITE_STONE : BLACK_STONE;
for(drc=0;drc<MAX_DRC;drc++){
flag=0;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] ,y=y +ydrc_table[drc]){
if(box_data[x][y].stone==NO_STONE)break;
if(flag==0){
if(box_data[x][y].stone==stone)break;
flag=1;
rev =1;
}
else{
if(box_data[x][y].stone==xstone){
rev++;
}
else{
(*reverse)+=rev;
(*branch )+=1 ;
break;
}
}
}
}
}
/*=========================== Othello::TempPutStone ===========================
* Othello::TempPutStone.
*/
void Othello::TempPutStone(
struct box_data_strct (*tmp_box_data)[MAX_BOX],
int stone,int xput,int yput)
{
int drc,x,y,xstone;
xstone=stone==BLACK_STONE ? WHITE_STONE : BLACK_STONE;
tmp_box_data[xput][yput].stone=stone;
for(drc=0;drc<MAX_DRC;drc++){
if(!IsEnablePutStone(stone,xput,yput,0,drc,tmp_box_data))continue;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] , y=y +ydrc_table[drc]){
if(tmp_box_data[x][y].stone==stone)break;
tmp_box_data[x][y].stone=stone;
}
}
}
/*=========================== Othello::GetStoneCount ==========================
* Othello::GetStoneCount.
*/
int Othello::GetStoneCount(
struct box_data_strct (*box_data)[MAX_BOX],int stone)
{
int x,y,total;
total=0;
for(y=0;y<MAX_BOX;y++){
for(x=0;x<MAX_BOX;x++){
if(box_data[x][y].stone==stone)total++;
}
}
return total;
}
/*======================== Othello::ThinkAbleDataPass2 ========================
* Othello::ThinkAbleDataPass2.
*/
void Othello::ThinkAbleDataPass2(int period)
{
#define BAD_DECREASE_PITCH 10
#define POINT_DECREASE_PITCH 2
struct rbdata_strct{
int index,reverse,branch;
struct rbdata_strct *next;
};
struct rbdata_strct rbdata[MAX_BOX * MAX_BOX];
struct rbdata_strct *top_rbdata,*rbp,*rbc;
struct box_data_strct tmp_box_data1[MAX_BOX][MAX_BOX];
struct box_data_strct tmp_box_data2[MAX_BOX][MAX_BOX];
int i,x,y;
/*
* " Check bad box "
*/
for(i=0;i<=lable_data;i++){
memcpy(tmp_box_data1,box_data,sizeof(box_data));
TempPutStone(
tmp_box_data1,cpu_stone,
able_data[i].x,able_data[i].y);
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(!IsEnablePutStone(
player_stone,x,y,1,-1,tmp_box_data1))continue;
if((x==0 || x==MAX_BOX-1)
&& (y==0 || y==MAX_BOX-1)){
able_data[i].point-=BAD_DECREASE_PITCH;
continue;
}
memcpy(tmp_box_data2,tmp_box_data1,sizeof(tmp_box_data1));
TempPutStone(tmp_box_data2,player_stone,x,y);
if(GetStoneCount(tmp_box_data2,cpu_stone)==0){
able_data[i].point-=BAD_DECREASE_PITCH;
}
}
}
}
if(period!=PERIOD1)return;
/*
* " Make reverse and branch list "
*/
top_rbdata=NULL;
for(i=0;i<=lable_data;i++){
GetReverseBranchCount(
cpu_stone,able_data[i].x,able_data[i].y,
&rbdata[i].reverse,&rbdata[i].branch);
rbdata[i].index=i;
rbdata[i].next =NULL;
rbp=NULL;
rbc=top_rbdata;
while(rbc){
if(rbdata[i].reverse < rbc->reverse)break;
if(rbdata[i].reverse == rbc->reverse
&& rbdata[i].branch == rbc->branch )break;
rbp=rbc;
rbc=rbc->next;
}
rbdata[i].next=rbc;
if(rbp==NULL)top_rbdata=rbdata+i;
else rbp->next=rbdata+i;
}
/*
* " Decrease point "
*/
i=0;
rbp=NULL;
rbc=top_rbdata;
while(rbc){
if(rbp){
if(rbc->reverse!=rbp->reverse
|| rbc->branch !=rbp->branch )i-=POINT_DECREASE_PITCH;
}
able_data[rbc->index].point+=i;
rbp=rbc;
rbc=rbc->next;
}
#undef BAD_DECREASE_PITCH
#undef POINT_DECREASE_PITCH
}
/*========================== Othello::IsExistPattern ==========================
* Othello::IsExistPattern.
*/
int Othello::IsExistPattern(char *string,char *pattern)
{
char tmp[81],c,*str,*s,*p;
int cont,loop;
strcpy(tmp,string);
for(loop=0;loop<=1;loop++){
if(loop==1){
int i,j,k;
j=strlen(tmp)-1;
k=j/2;
for(i=0;i<=k;i++,j--){ c=tmp[i]; tmp[i]=tmp[j]; tmp[j]=c; }
}
str=tmp;
while(*str){
s=str;
p=pattern;
c= *p++; if(c=='$')c= *p++;
if(c=='N' || c=='P' || c=='C'){ cont=1; c+='a'-'A'; }
else { cont=0; }
while(1){
if(*s==c || c=='*'){
if(!*p)return 1;
if(!*++s && *p=='$')return 1;
if(cont){
cont=2;
continue;
}
}
else{
if(cont!=2)break;
if(!*p )break;
}
c= *p++;
if(c=='N' || c=='P' || c=='C'){ cont=1; c+='a'-'A'; }
else { cont=0; }
}
if(*pattern=='$')break;
else str++;
}
}
return 0;
}
/*====================== Othello::GetNextPlayerPutCorner ======================
* Othello::GetNextPlayerPutCorner.
*/
int Othello::GetNextPlayerPutCorner(
int stone,int xstone,int xput,int yput,
int sts[4][2])
{
int cpos_tbl[4][2]={
0 ,0 ,
0 ,MAX_BOX-1,
MAX_BOX-1,0 ,
MAX_BOX-1,MAX_BOX-1};
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int loop,flag;
/*
* " Get enable put xstone to corner "
*/
for(loop=0;loop<=3;loop++){
sts[loop][0]=IsEnablePutStone(
xstone,cpos_tbl[loop][0],cpos_tbl[loop][1]);
}
/*
* " Check prevent "
*/
memcpy(tmp_box_data,box_data,sizeof(box_data));
TempPutStone(tmp_box_data,stone,xput,yput);
flag=0;
for(loop=0;loop<=3;loop++){
sts[loop][1]=IsEnablePutStone(
xstone,cpos_tbl[loop][0],cpos_tbl[loop][1],1,-1,
tmp_box_data);
if(sts[loop][1]!=sts[loop][0])flag=1;
}
return flag;
}
/*======================== Othello::ThinkAbleDataPass1 ========================
* Othello::ThinkAbleDataPass1.
*/
int Othello::ThinkAbleDataPass1()
{
struct pattern_data_strct{
char *pattern;
int add;
};
struct pattern_data_strct pattern_data[]={
{ "NxN" , 0 },
{ "NxNC" , -10 },
{ "NxPN" , -10 },
{ "NPxPN" , 5 },
{ "NPxCN" , -10 },
{ "NxCPN" , -20 },
{ "NxPCN" , 15 },
{ "NxPCPN" , -10 },
{ "NxPCP$" , -10 },
{ "NxCN" , 0 },
{ "NPxCPN" , 10 },
{ "PxCP" , 10 },
{ "NPxPCN" , -10 },
{ "NPxPCPN" , 10 },
{ "NPCPxN" , -10 },
{ "$x" , 20 },
{ "$CxN" , 10 },
{ "$CxP" , 10 },
{ "$CxPC" , 12 },
{ "$CPx" , 10 },
{ "$nPxPN" , 18 },
{ "$nPxP$" , 18 },
{ "$nPxCPN" , 18 },
{ "$nPxCP$" , 18 },
{ "$nPxPCN" , -10 },
{ "$nPxPC$" , 18 },
{ "$PxP" , 10 },
{ "$PCPxN$" , -10 },
{ "$PCxN$" , -10 },
{ "$CxCn" , 15 },
{ "$CxC$" , 5 },
{ "$PxCN" , -10 },
{ "$NxPCPC$" , -10 },
{ "$NxCPC$" , -10 },
{ "$CNxCP$" , -10 },
{ "CCxCN" , 10 },
{ "CCxCP$" , 10 },
{ "$CPCnN$" , -5 },
{ "$CPCn$" , -10 },
{ "$CxC$" , 10 },
{ "$PxNP" , -10 },
{ "",0 }
};
int able,x,y,dx,dy,add,i,flag;
char sts[MAX_BOX+1];
int csts[4][2];
sts[MAX_BOX]='\0';
flag=0;
for(able=0;able<=lable_data;able++){
add=0;
if( able_data[able].x==0 || able_data[able].x==MAX_BOX-1
|| (able_data[able].y==0 || able_data[able].y==MAX_BOX-1)){
if(able_data[able].x==0 || able_data[able].x==MAX_BOX-1){
y=0; dy=1; x=able_data[able].x; dx=0;
}
else{
x=0; dx=1; y=able_data[able].y; dy=0;
}
for(i=0;i<MAX_BOX;i++,x+=dx,y+=dy){
if(x==able_data[able].x && y==able_data[able].y)sts[i]='x';
else if(box_data[x][y].stone ==player_stone )sts[i]='p';
else if(box_data[x][y].stone == cpu_stone )sts[i]='c';
else sts[i]='n';
}
for(i=0;pattern_data[i].pattern[0];i++){
if(IsExistPattern(sts,pattern_data[i].pattern)){
if(abs(pattern_data[i].add)>abs(add)){
add=pattern_data[i].add;
}
}
}
}
if(GetNextPlayerPutCorner(
cpu_stone,player_stone,
able_data[able].x,able_data[able].y,csts)){
for(i=0;i<=3;i++){
if( csts[i][0] && !csts[i][1])add+=20;
else if(!csts[i][0] && csts[i][1])add-=20;
}
}
able_data[able].point+=add;
if(add<= -10 || add>=10)flag=1;
}
return flag;
}
/*======================== Othello::ThinkAbleDataPass3 ========================
* Othello::ThinkAbleDataPass3.
*/
void Othello::ThinkAbleDataPass3()
{
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int remain[MAX_BOX * MAX_BOX][2];
int lremain;
int able,x,y,min,max;
for(able=0;able<=lable_data;able++){
memcpy(tmp_box_data,box_data,sizeof(box_data));
TempPutStone(
tmp_box_data,cpu_stone,able_data[able].x,able_data[able].y);
lremain= -1;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(tmp_box_data[x][y].stone==NO_STONE){
lremain++;
remain[lremain][0]=x;
remain[lremain][1]=y;
}
}
}
min= -1;
max= -1;
GetTotal(
tmp_box_data,player_stone,cpu_stone,remain,lremain,&min,&max);
able_data[able].point=min;
}
}
/*============================= Othello::GetTotal =============================
* Othello::GetTotal.
*/
int Othello::GetTotal(
struct box_data_strct (*box_data_arg)[MAX_BOX],
int stone,int xstone,
int (*remain)[2],int lremain,
int *min,int *max)
{
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int loop,t,i,lrem,rem[2];
/*
* " Check enable put "
*/
if(lremain<0)return 0;
for(loop=0;loop<=1;loop++){
for(i=0;i<=lremain;i++){
if(IsEnablePutStone(
stone,remain[i][0],remain[i][1],1,-1,box_data_arg))break;
}
if(i<=lremain)break;
t=stone; stone=xstone; xstone=t;
}
if(loop>1)return 0;
/*
* " Put "
*/
lrem=lremain;
while(lrem>=0){
if(IsEnablePutStone(stone,remain[0][0],remain[0][1],1,-1,box_data_arg)){
memcpy(tmp_box_data,box_data_arg,sizeof(tmp_box_data));
TempPutStone(tmp_box_data,stone,remain[0][0],remain[0][1]);
if(!GetTotal(
tmp_box_data,xstone,stone,remain+1,lremain-1,
min,max)){
t=GetStoneCount(tmp_box_data,cpu_stone);
if(*min== -1 || t< *min)*min=t;
if(*max== -1 || t> *max)*max=t;
}
}
memcpy(rem ,remain[0],sizeof(rem) );
memcpy(remain ,remain+1 ,sizeof(rem)*lrem);
memcpy(remain+lrem,rem ,sizeof(rem) );
lrem--;
}
return 1;
}
BEGIN_MESSAGE_MAP(Othello,CDialog)
ON_COMMAND(IDASK_ORDER,OnAskOrder)
ON_COMMAND(IDPUT_STONE,OnPutStone)
ON_COMMAND(IDTHINK_PUT_STONE,OnThinkPutStone)
ON_COMMAND(IDOK ,Default)
ON_COMMAND(IDCANCEL,Default)
ON_WM_LBUTTONDOWN()
ON_WM_PAINT()
ON_WM_CLOSE()
END_MESSAGE_MAP()
OthelloApp OthelloApp;
アイコンを設定するようになっていますので、othello.ico を適当に作って下さい。
それではまた次回。
第137回 プログラミングについて『たまにはオセロで遊ぼうよ!その9』
プログラミングについて 第137回目
『たまにはオセロで遊ぼうよ! その9』
今回も前回の続きです。リソースは何も変更がないので、プログラム本体のリストです。
#include <afxwin.h>
#include "res\resource.h"
#include "dummy.h"
#define MAX_BOX 8
#define STONE_SIZE_RATIO 0.8
#define RED_COLOR RGB(255, 0, 0)
#define BLACK_COLOR RGB( 0, 0, 0)
#define WHITE_COLOR RGB(255,255,255)
#define DGREEN_COLOR RGB( 0,128, 0)
#define NO_STONE 0
#define BLACK_STONE 1
#define WHITE_STONE 2
#define NOTIFY_STONE 3
#define MAX_DRC 8
int xdrc_table[MAX_DRC]={ 1,-1, 0, 0, 1,-1, 1,-1 };
int ydrc_table[MAX_DRC]={ 0, 0, 1,-1, 1,-1,-1, 1 };
#define PERIOD12 36
#define PERIOD23 48
#define PERIOD1 1
#define PERIOD2 2
#define PERIOD3 3
class Othello : public CDialog
{
private:
int stone_size;
struct box_data_strct{
RECT rect;
int stone;
int point;
};
struct box_data_strct box_data[MAX_BOX][MAX_BOX];
CPen pen ,*old_pen ;
CBrush brush,*old_brush;
int pen_brush_using ;
int on_playing;
int player_stone;
int cpu_stone;
int current_stone;
int total_black_stone;
int total_white_stone;
struct able_data_strct{
int x,y;
int point;
};
struct able_data_strct able_data[MAX_BOX*MAX_BOX];
int lable_data;
afx_msg void OnClose();
afx_msg BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg void OnLButtonDown(UINT nflags,CPoint point);
afx_msg void OnPutStone();
afx_msg void OnAskOrder();
afx_msg void OnThinkPutStone();
void Redraw(CDC *dc);
void SetColor (CDC *dc,COLORREF outline_color,COLORREF fill_color);
void FreeColor(CDC *dc);
void InitializeBoxData();
void WriteStone(CDC *dc,int x,int y);
int IsEnablePutStone(
int stone,int xput,int yput,
int check_this=1,int drc= -1,
struct box_data_strct (*box_data_arg)[MAX_BOX]=NULL);
void WriteStoneTotal();
void PutStone(int stone,int xput,int yput);
void Delay(double delta_time);
void WriteOrder();
int EnablePutStoneAnywhere();
int WriteEndMessage();
void PurgeLButtonDownMessage();
int Random(int total);
void SortAbleData();
int CompareAbleData(struct able_data_strct *data,int i,int j);
void ThinkAbleData();
int ThinkAbleDataPass1();
void ThinkAbleDataPass2(int period);
void GetReverseBranchCount(
int stone,int xput,int yput,int *reverse,int *branch);
void TempPutStone(
struct box_data_strct (*tmp_box_data)[MAX_BOX],
int stone,int xput,int yput);
int GetStoneCount(
struct box_data_strct (*box_data)[MAX_BOX],int stone);
int IsExistPattern(char *string,char *pattern);
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("オセロゲーム");
}
afx_msg BOOL Othello::OnInitDialog()
{
int point_table[MAX_BOX][MAX_BOX]={
0, 0, 2, 3, 3, 2, 0, 0,
0, 0, 4, 4, 4, 4, 0, 0,
2, 4, 5, 5, 5, 5, 4, 2,
3, 4, 5, 0, 0, 5, 4, 3,
3, 4, 5, 0, 0, 5, 4, 3,
2, 4, 5, 5, 5, 5, 4, 2,
0, 0, 4, 4, 4, 4, 0, 0,
0, 0, 2, 3, 3, 2, 0, 0
};
RECT rect;
double xsize,ysize,t;
int x,y;
on_playing=0;
pen_brush_using=0;
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);
box_data[x][y].point=point_table[x][y];
}
}
InitializeBoxData();
PostMessage(WM_COMMAND,IDASK_ORDER);
return TRUE;
}
afx_msg void Othello::OnPaint()
{
CPaintDC dc(this);
Redraw(&dc);
}
void Othello::SetColor(CDC *dc,COLORREF outline_color,COLORREF fill_color)
{
FreeColor(dc);
pen.CreatePen(PS_SOLID,0,outline_color);
brush.CreateSolidBrush(fill_color);
old_pen =dc->SelectObject(&pen );
old_brush=dc->SelectObject(&brush);
pen_brush_using=1;
}
void Othello::FreeColor(CDC *dc)
{
if(pen_brush_using){
dc->SelectObject(old_pen );
dc->SelectObject(old_brush);
pen.DeleteObject();
brush.DeleteObject();
}
pen_brush_using=0;
}
void Othello::Redraw(CDC *dc)
{
RECT rect;
int x,y;
GetDlgItem(IDFRAME_BOX)->GetWindowRect(&rect);
ScreenToClient(&rect);
SetColor(dc,DGREEN_COLOR,DGREEN_COLOR);
dc->Rectangle(&rect);
SetColor(dc,BLACK_COLOR,BLACK_COLOR);
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
dc->MoveTo(box_data[x][y].rect.left ,box_data[x][y].rect.top );
dc->LineTo(box_data[x][y].rect.right,box_data[x][y].rect.top );
dc->LineTo(box_data[x][y].rect.right,box_data[x][y].rect.bottom);
dc->LineTo(box_data[x][y].rect.left ,box_data[x][y].rect.bottom);
dc->LineTo(box_data[x][y].rect.left ,box_data[x][y].rect.top );
}
}
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++)WriteStone(dc,x,y);
}
FreeColor(dc);
}
void Othello::InitializeBoxData()
{
int x,y;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
box_data[x][y].stone=NO_STONE;
}
}
box_data[MAX_BOX/2-1][MAX_BOX/2-1].stone=BLACK_STONE;
box_data[MAX_BOX/2 ][MAX_BOX/2-1].stone=WHITE_STONE;
box_data[MAX_BOX/2-1][MAX_BOX/2 ].stone=WHITE_STONE;
box_data[MAX_BOX/2 ][MAX_BOX/2 ].stone=BLACK_STONE;
total_black_stone=2;
total_white_stone=2;
WriteStoneTotal();
}
void Othello::WriteStone(CDC *dc,int x,int y)
{
COLORREF color;
int xc,yc;
if(box_data[x][y].stone== NO_STONE)return;
else if(box_data[x][y].stone== BLACK_STONE)color=BLACK_COLOR;
else if(box_data[x][y].stone== WHITE_STONE)color=WHITE_COLOR;
else if(box_data[x][y].stone==NOTIFY_STONE)color= RED_COLOR;
xc=(box_data[x][y].rect.left+box_data[x][y].rect.right )/2;
yc=(box_data[x][y].rect.top +box_data[x][y].rect.bottom)/2;
SetColor(dc,color,color);
dc->Ellipse(
xc-stone_size/2,yc-stone_size/2,
xc+stone_size/2,yc+stone_size/2);
FreeColor(dc);
}
afx_msg void Othello::OnLButtonDown(UINT nflags,CPoint point)
{
int x,y;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(point.x>box_data[x][y].rect.left
&& point.x<box_data[x][y].rect.right
&& point.y>box_data[x][y].rect.top
&& point.y<box_data[x][y].rect.bottom){
PostMessage(WM_COMMAND,IDPUT_STONE,(((player_stone<<8)|x)<<8)|y);
return;
}
}
}
}
afx_msg void Othello::OnAskOrder()
{
if(MessageBox(
"●は先攻、○は後攻です。\n\n"
"先攻しますか?",
"順番の問い合わせ",MB_YESNO)==IDYES){
player_stone=BLACK_STONE;
cpu_stone=WHITE_STONE;
}
else{
player_stone=WHITE_STONE;
cpu_stone=BLACK_STONE;
}
current_stone=BLACK_STONE;
on_playing=1;
WriteOrder();
if(player_stone==current_stone){
PurgeLButtonDownMessage();
}
else{
PostMessage(WM_PAINT);
PostMessage(WM_COMMAND,IDTHINK_PUT_STONE);
}
}
afx_msg void Othello::OnPutStone()
{
LPARAM lparam;
int stone,xput,yput;
if(!on_playing)return;
lparam=GetCurrentMessage()->lParam;
yput =lparam & 0x000000ff; lparam=lparam>>8;
xput =lparam & 0x000000ff; lparam=lparam>>8;
stone=lparam & 0x000000ff;
if(stone!=current_stone)return;
if(!IsEnablePutStone(stone,xput,yput))return;
PutStone(stone,xput,yput);
}
int Othello::IsEnablePutStone(
int stone,int xput,int yput,int check_this,int drc,
struct box_data_strct (*box_data_arg)[MAX_BOX])
{
int drc_from,drc_to,x,y,flag,xstone;
if(!box_data_arg)box_data_arg=box_data;
if(check_this && box_data_arg[xput][yput].stone!=NO_STONE)return 0;
xstone=stone==BLACK_STONE ? WHITE_STONE : BLACK_STONE;
if(drc== -1){ drc_from=0 ; drc_to=MAX_DRC-1; }
else { drc_from=drc; drc_to=drc ; }
for(drc=drc_from;drc<=drc_to;drc++){
flag=0;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] ,y=y +ydrc_table[drc]){
if(box_data_arg[x][y].stone==NO_STONE)break;
if(flag==0){
if(box_data_arg[x][y].stone==xstone)flag=1;
else break;
}
else{
if(box_data_arg[x][y].stone== stone)return 1;
}
}
}
return 0;
}
void Othello::WriteStoneTotal()
{
char tmp[31];
sprintf(tmp,"● %2d 個",total_black_stone);
SetDlgItemText(IDBLACK_STATUS,tmp);
sprintf(tmp,"○ %2d 個",total_white_stone);
SetDlgItemText(IDWHITE_STATUS,tmp);
}
void Othello::PutStone(int stone,int xput,int yput)
{
CClientDC dc(this);
int drc,x,y,loop,xstone,*add,*sub;
if(stone==BLACK_STONE){
xstone=WHITE_STONE;
add= &total_black_stone;
sub= &total_white_stone;
}
else{
xstone=BLACK_STONE;
add= &total_white_stone;
sub= &total_black_stone;
}
box_data[xput][yput].stone=stone;
(*add)++;
WriteStone(&dc,xput,yput);
Delay(0.3);
for(drc=0;drc<MAX_DRC;drc++){
if(!IsEnablePutStone(stone,xput,yput,0,drc))continue;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] , y=y +ydrc_table[drc]){
if(box_data[x][y].stone==stone)break;
box_data[x][y].stone=stone;
(*add)++;
(*sub)--;
WriteStone(&dc,x,y);
Delay(0.3);
}
}
WriteStoneTotal();
for(loop=0;loop<=1;loop++){
if(current_stone==BLACK_STONE)current_stone=WHITE_STONE;
else current_stone=BLACK_STONE;
if(EnablePutStoneAnywhere())break;
}
if(loop>1){
if(!WriteEndMessage()){
PostMessage(WM_CLOSE);
return;
}
InitializeBoxData();
WriteStoneTotal();
Redraw(&dc);
PostMessage(WM_COMMAND,IDASK_ORDER);
return;
}
WriteOrder();
if(player_stone==current_stone)PurgeLButtonDownMessage();
else PostMessage(WM_COMMAND,IDTHINK_PUT_STONE);
}
void Othello::Delay(double delta_time)
{
DWORD to,dt;
dt=(int)(delta_time*1000.0);
to=GetTickCount();
while(1){
if(GetTickCount()-to>dt)break;
}
}
void Othello::WriteOrder()
{
char tmp[81];
if(current_stone==player_stone){
sprintf(
tmp,"あなたの %s 石の番です",
current_stone==BLACK_STONE ? "●" : "○");
}
else{
strcpy(tmp,"お待ち下さい");
}
SetDlgItemText(IDMESSAGE,tmp);
}
int Othello::WriteEndMessage()
{
char tmp[81];
int diff;
diff=player_stone==WHITE_STONE ? total_white_stone-total_black_stone
: total_black_stone-total_white_stone;
if(diff< 0)sprintf(tmp,"あなたの %d 個の負けです。",-diff);
else if(diff==0)strcpy (tmp,"引き分けです。");
else sprintf(tmp,"あなたの %d 個の勝ちです。", diff);
strcat(tmp,"\nもう一度勝負しますか?");
if(MessageBox(tmp,"終了",MB_YESNO)==IDYES)return 1;
else return 0;
}
int Othello::EnablePutStoneAnywhere()
{
int x,y;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(IsEnablePutStone(current_stone,x,y))return 1;
}
}
return 0;
}
void Othello::PurgeLButtonDownMessage()
{
MSG msg;
while(PeekMessage(&msg,m_hWnd,WM_LBUTTONDOWN,WM_LBUTTONDOWN,PM_REMOVE));
}
afx_msg void Othello::OnThinkPutStone()
{
CClientDC dc(this);
int x,y,i;
lable_data= -1;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(box_data[x][y].stone!=NO_STONE )continue;
if(!IsEnablePutStone(cpu_stone,x,y))continue;
lable_data++;
able_data[lable_data].x =x;
able_data[lable_data].y =y;
able_data[lable_data].point=box_data[x][y].point;
}
}
ThinkAbleData();
SortAbleData();
for(i=0;i<lable_data;i++){
if(able_data[i].point>able_data[i+1].point){
lable_data=i;
break;
}
}
i=Random(lable_data+1);
x=able_data[i].x;
y=able_data[i].y;
Delay(1.0);
box_data[x][y].stone=NOTIFY_STONE;
WriteStone(&dc,x,y);
box_data[x][y].stone=NO_STONE;
Delay(0.3);
PostMessage(WM_COMMAND,IDPUT_STONE,(((cpu_stone<<8)|x)<<8)|y);
}
int Othello::Random(int total)
{
return GetTickCount()%total;
}
void Othello::SortAbleData()
{
struct able_data_strct tmp;
int i,j,gap;
gap=(lable_data+1)/2;
while(gap){
i=gap;
while(i<=lable_data){
j=i-gap;
while( j>=0 && CompareAbleData(able_data,j,j+gap)==1){
tmp =able_data[j ];
able_data[j ]=able_data[j+gap];
able_data[j+gap]=tmp;
j-=gap;
}
i++;
}
gap/=2;
}
}
int Othello::CompareAbleData(struct able_data_strct *data,int i,int j)
{
if(data[i].point>data[j].point)return 0;
if(data[i].point<data[j].point)return 1;
return 0;
}
void Othello::GetReverseBranchCount(
int stone,int xput,int yput,int *reverse,int *branch)
{
int drc,x,y,flag,xstone;
int rev;
*reverse=0;
*branch =0;
xstone=stone==BLACK_STONE ? WHITE_STONE : BLACK_STONE;
for(drc=0;drc<MAX_DRC;drc++){
flag=0;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] ,y=y +ydrc_table[drc]){
if(box_data[x][y].stone==NO_STONE)break;
if(flag==0){
if(box_data[x][y].stone==stone)break;
flag=1;
rev =1;
}
else{
if(box_data[x][y].stone==xstone){
rev++;
}
else{
(*reverse)+=rev;
(*branch )+=1 ;
break;
}
}
}
}
}
void Othello::ThinkAbleData()
{
int total;
total=total_black_stone+total_white_stone;
if(total<PERIOD12){
if(ThinkAbleDataPass1())return;
ThinkAbleDataPass2(PERIOD1);
}
else if(total<PERIOD23){
ThinkAbleDataPass1();
}
else{
ThinkAbleDataPass1();
}
}
void Othello::ThinkAbleDataPass2(int period)
{
#define BAD_DECREASE_PITCH 10
#define POINT_DECREASE_PITCH 5
struct rbdata_strct{
int index,reverse,branch;
struct rbdata_strct *next;
};
struct rbdata_strct rbdata[MAX_BOX * MAX_BOX];
struct rbdata_strct *top_rbdata,*rbp,*rbc;
struct box_data_strct tmp_box_data1[MAX_BOX][MAX_BOX];
struct box_data_strct tmp_box_data2[MAX_BOX][MAX_BOX];
int i,x,y;
for(i=0;i<=lable_data;i++){
memcpy(tmp_box_data1,box_data,sizeof(box_data));
TempPutStone(
tmp_box_data1,cpu_stone,able_data[i].x,able_data[i].y);
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(!IsEnablePutStone(player_stone,x,y,1,-1,tmp_box_data1))continue;
if((x==0 || x==MAX_BOX-1)
&& (y==0 || y==MAX_BOX-1)){
able_data[i].point-=BAD_DECREASE_PITCH;
continue;
}
memcpy(tmp_box_data2,tmp_box_data1,sizeof(tmp_box_data1));
TempPutStone(tmp_box_data2,player_stone,x,y);
if(GetStoneCount(tmp_box_data2,cpu_stone)==0){
able_data[i].point-=BAD_DECREASE_PITCH;
}
}
}
}
if(period!=PERIOD1)return;
top_rbdata=NULL;
for(i=0;i<=lable_data;i++){
GetReverseBranchCount(
cpu_stone,able_data[i].x,able_data[i].y,
&rbdata[i].reverse,&rbdata[i].branch);
rbdata[i].index=i;
rbdata[i].next =NULL;
rbp=NULL;
rbc=top_rbdata;
while(rbc){
if(rbdata[i].reverse < rbc->reverse)break;
if(rbdata[i].reverse == rbc->reverse
&& rbdata[i].branch == rbc->branch )break;
rbp=rbc;
rbc=rbc->next;
}
rbdata[i].next=rbc;
if(rbp==NULL)top_rbdata=rbdata+i;
else rbp->next=rbdata+i;
}
i=0;
rbp=NULL;
rbc=top_rbdata;
while(rbc){
if(rbp){
if(rbc->reverse!=rbp->reverse
|| rbc->branch !=rbp->branch )i-=POINT_DECREASE_PITCH;
}
able_data[rbc->index].point+=i;
rbp=rbc;
rbc=rbc->next;
}
#undef BAD_DECREASE_PITCH
#undef POINT_DECREASE_PITCH
}
void Othello::TempPutStone(
struct box_data_strct (*tmp_box_data)[MAX_BOX],
int stone,int xput,int yput)
{
int drc,x,y,xstone;
xstone=stone==BLACK_STONE ? WHITE_STONE : BLACK_STONE;
tmp_box_data[xput][yput].stone=stone;
for(drc=0;drc<MAX_DRC;drc++){
if(!IsEnablePutStone(stone,xput,yput,0,drc,tmp_box_data))continue;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] , y=y +ydrc_table[drc]){
if(tmp_box_data[x][y].stone==stone)break;
tmp_box_data[x][y].stone=stone;
}
}
}
int Othello::GetStoneCount(
struct box_data_strct (*box_data)[MAX_BOX],int stone)
{
int x,y,total;
total=0;
for(y=0;y<MAX_BOX;y++){
for(x=0;x<MAX_BOX;x++){
if(box_data[x][y].stone==stone)total++;
}
}
return total;
}
int Othello::ThinkAbleDataPass1()
{
struct pattern_data_strct{
char *pattern;
int add;
};
struct pattern_data_strct pattern_data[]={
{ "NxN" , 0 },
{ "NxPN" , -10 },
{ "NPxPN" , 5 },
{ "NPxCN" , -10 },
{ "NxCPN" , -20 },
{ "NxPCN" , 15 },
{ "NxPCPN" , -10 },
{ "NxPCP$" , -10 },
{ "NxCN" , 0 },
{ "NPxCPN" , 10 },
{ "PxCP" , 10 },
{ "NPxPCN" , -10 },
{ "NPxPCPN" , 10 },
{ "NPCPxN" , -10 },
{ "$x" , 20 },
{ "$CxN" , 10 },
{ "$CxP" , 10 },
{ "$CxPC" , 12 },
{ "$CPx" , 10 },
{ "$nPxPN" , 18 },
{ "$nPxP$" , 18 },
{ "$nPxCPN" , 18 },
{ "$nPxCP$" , 18 },
{ "$nPxPCN" , -10 },
{ "$nPxPC$" , 18 },
{ "$PxP" , 10 },
{ "$PCPxN$" , -10 },
{ "$PCxN$" , -10 },
{ "$CxCn" , 15 },
{ "$CxC$" , 5 },
{ "$PxCN" , -10 },
{ "$NxPCPC$" , -10 },
{ "$NxCPC$" , -10 },
{ "$CNxCP$" , -10 },
{ "CCxCN" , 10 },
{ "CCxCP$" , 10 },
{ "$CPCnN$" , -5 },
{ "$CPCn$" , -10 },
{ "$CxC$" , 10 },
{ "$PxNP" , -10 },
{ "" , 0 }
};
int able,x,y,dx,dy,add,i,flag;
char sts[MAX_BOX+1];
sts[MAX_BOX]='\0';
flag=0;
for(able=0;able<=lable_data;able++){
if(able_data[able].x!=0 && able_data[able].x!=MAX_BOX-1
&& able_data[able].y!=0 && able_data[able].y!=MAX_BOX-1)continue;
if(able_data[able].x==0 || able_data[able].x==MAX_BOX-1){
y=0; dy=1; x=able_data[able].x; dx=0;
}
else{
x=0; dx=1; y=able_data[able].y; dy=0;
}
for(i=0;i<MAX_BOX;i++,x+=dx,y+=dy){
if(x==able_data[able].x && y==able_data[able].y)sts[i]='x';
else if(box_data[x][y].stone ==player_stone )sts[i]='p';
else if(box_data[x][y].stone == cpu_stone )sts[i]='c';
else sts[i]='n';
}
add=0;
for(i=0;pattern_data[i].pattern[0];i++){
if(IsExistPattern(sts,pattern_data[i].pattern)){
if(abs(pattern_data[i].add)>abs(add))add=pattern_data[i].add;
}
}
able_data[able].point+=add;
if(add<= -10 || add>=10)flag=1;
}
return flag;
}
int Othello::IsExistPattern(char *string,char *pattern)
{
char tmp[81],c,*str,*s,*p;
int cont,loop;
strcpy(tmp,string);
for(loop=0;loop<=1;loop++){
if(loop==1){
int i,j,k;
j=strlen(tmp)-1;
k=j/2;
for(i=0;i<=k;i++,j--){ c=tmp[i]; tmp[i]=tmp[j]; tmp[j]=c; }
}
str=tmp;
while(*str){
s=str;
p=pattern;
c= *p++; if(c=='$')c= *p++;
if(c=='N' || c=='P' || c=='C'){ cont=1; c+='a'-'A'; }
else { cont=0; }
while(1){
if(*s==c || c=='*'){
if(!*p)return 1;
if(!*++s && *p=='$')return 1;
if(cont){
cont=2;
continue;
}
}
else{
if(cont!=2)break;
if(!*p )break;
}
c= *p++;
if(c=='N' || c=='P' || c=='C'){ cont=1; c+='a'-'A'; }
else { cont=0; }
}
if(*pattern=='$')break;
else str++;
}
}
return 0;
}
BEGIN_MESSAGE_MAP(Othello,CDialog)
ON_COMMAND(IDASK_ORDER,OnAskOrder)
ON_COMMAND(IDPUT_STONE,OnPutStone)
ON_COMMAND(IDTHINK_PUT_STONE,OnThinkPutStone)
ON_COMMAND(IDOK ,Default)
ON_COMMAND(IDCANCEL,Default)
ON_WM_LBUTTONDOWN()
ON_WM_PAINT()
ON_WM_CLOSE()
END_MESSAGE_MAP()
OthelloApp OthelloApp;
ここまで作ってくるとだんだん計算機も頭が良くなってきた感じがありますが、まだ中盤で大きな失敗をしてしまいます。計算機が打ったおかげでプレーヤーがコーナーを取れたり、プレーヤーがコーナーを取れるのを阻止しなかったりし、一気に負けになってしまうことがあるのです。そこで、その処理を ThinkAbleDataPass1() に追加しましょう。
ThinkAbleDataPass1() を次のようにします。
int Othello::ThinkAbleDataPass1()
{
struct pattern_data_strct{
char *pattern;
int add;
};
struct pattern_data_strct pattern_data[]={
{ "NxN" , 0 },
{ "NxPN" , -10 },
{ "NPxPN" , 5 },
{ "NPxCN" , -10 },
{ "NxCPN" , -20 },
{ "NxPCN" , 15 },
{ "NxPCPN" , -10 },
{ "NxPCP$" , -10 },
{ "NxCN" , 0 },
{ "NPxCPN" , 10 },
{ "PxCP" , 10 },
{ "NPxPCN" , -10 },
{ "NPxPCPN" , 10 },
{ "NPCPxN" , -10 },
{ "$x" , 20 },
{ "$CxN" , 10 },
{ "$CxP" , 10 },
{ "$CxPC" , 12 },
{ "$CPx" , 10 },
{ "$nPxPN" , 18 },
{ "$nPxP$" , 18 },
{ "$nPxCPN" , 18 },
{ "$nPxCP$" , 18 },
{ "$nPxPCN" , -10 },
{ "$nPxPC$" , 18 },
{ "$PxP" , 10 },
{ "$PCPxN$" , -10 },
{ "$PCxN$" , -10 },
{ "$CxCn" , 15 },
{ "$CxC$" , 5 },
{ "$PxCN" , -10 },
{ "$NxPCPC$" , -10 },
{ "$NxCPC$" , -10 },
{ "$CNxCP$" , -10 },
{ "CCxCN" , 10 },
{ "CCxCP$" , 10 },
{ "$CPCnN$" , -5 },
{ "$CPCn$" , -10 },
{ "$CxC$" , 10 },
{ "$PxNP" , -10 },
{ "" , 0 }
};
int able,x,y,dx,dy,add,i,flag;
char sts[MAX_BOX+1];
int csts[4][2];
sts[MAX_BOX]='\0';
flag=0;
for(able=0;able<=lable_data;able++){
add=0;
if( able_data[able].x==0 || able_data[able].x==MAX_BOX-1
|| (able_data[able].y==0 || able_data[able].y==MAX_BOX-1)){
if(able_data[able].x==0 || able_data[able].x==MAX_BOX-1){
y=0; dy=1; x=able_data[able].x; dx=0;
}
else{
x=0; dx=1; y=able_data[able].y; dy=0;
}
for(i=0;i<MAX_BOX;i++,x+=dx,y+=dy){
if(x==able_data[able].x && y==able_data[able].y)sts[i]='x';
else if(box_data[x][y].stone ==player_stone )sts[i]='p';
else if(box_data[x][y].stone == cpu_stone )sts[i]='c';
else sts[i]='n';
}
for(i=0;pattern_data[i].pattern[0];i++){
if(IsExistPattern(sts,pattern_data[i].pattern)){
if(abs(pattern_data[i].add)>abs(add)){
add=pattern_data[i].add;
}
}
}
}
if(GetNextPlayerPutCorner(
cpu_stone,player_stone,
able_data[able].x,able_data[able].y,csts)){
for(i=0;i<=3;i++){
if( csts[i][0] && !csts[i][1])add+=20;
else if(!csts[i][0] && csts[i][1])add-=20;
}
}
able_data[able].point+=add;
if(add<= -10 || add>=10)flag=1;
}
return flag;
}
変更したのは、周辺の処理はそのままとして、それ以外にもプレーヤーがコーナーを取れるのか、またはプレーヤーがコーナーを取れるのを阻止できるかを検査できるようにした部分です。
上のリストの、
if(GetNextPlayerPutCorner(
cpu_stone,player_stone,
able_data[able].x,able_data[able].y,csts)){
for(i=0;i<=3;i++){
if( csts[i][0] && !csts[i][1])add+=20;
else if(!csts[i][0] && csts[i][1])add-=20;
}
}
の GetNextPlayerPutCorner() は現在の状態でプレーヤーがコーナーを取れるか否かと、計算機が打ったときに相手がコーナーを取れるか否かを調べる関数で、今のところ未定義です。csts[*][0] には現在の状態、csts[*][1] には次の状態がセットされます。0 だったものが 1 になるときには計算機にとっては悪い手で、その逆は良い手になります。
GetNextPlayerPutCorner() を Othello クラスに次のように追加します。
int GetNextPlayerPutCorner(
int stone,int xstone,int xput,int yput,
int sts[4][2]);
そして、
int Othello::GetNextPlayerPutCorner(
int stone,int xstone,int xput,int yput,int sts[4][2])
{
int cpos_tbl[4][2]={
0 ,0 ,
0 ,MAX_BOX-1,
MAX_BOX-1,0 ,
MAX_BOX-1,MAX_BOX-1
};
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int loop,flag;
for(loop=0;loop<=3;loop++){
sts[loop][0]=IsEnablePutStone(xstone,cpos_tbl[loop][0],cpos_tbl[loop][1]);
}
memcpy(tmp_box_data,box_data,sizeof(box_data));
TempPutStone(tmp_box_data,stone,xput,yput);
flag=0;
for(loop=0;loop<=3;loop++){
sts[loop][1]=IsEnablePutStone(
xstone,cpos_tbl[loop][0],cpos_tbl[loop][1],1,-1,
tmp_box_data);
if(sts[loop][1]!=sts[loop][0])flag=1;
}
return flag;
}
やっていることは難しいものではありません。
{
int cpos_tbl[4][2]={
0 ,0 ,
0 ,MAX_BOX-1,
MAX_BOX-1,0 ,
MAX_BOX-1,MAX_BOX-1
};
これは4つのコーナーの位置のテーブルです。
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int loop,flag;
for(loop=0;loop<=3;loop++){
sts[loop][0]=IsEnablePutStone(xstone,cpos_tbl[loop][0],cpos_tbl[loop][1]);
}
ここでは計算機が打つ前の状態をセットしています。
memcpy(tmp_box_data,box_data,sizeof(box_data));
TempPutStone(tmp_box_data,stone,xput,yput);
ここで計算機が仮に石を打ちます。
flag=0;
for(loop=0;loop<=3;loop++){
sts[loop][1]=IsEnablePutStone(
xstone,cpos_tbl[loop][0],cpos_tbl[loop][1],1,-1,
tmp_box_data);
if(sts[loop][1]!=sts[loop][0])flag=1;
}
計算機が打った後のプレーヤーの状態をセットします。
return flag;
打つ前と打つ後の状態が変化したときに真をそれ以外は偽を返します。
}
ここまでで中盤は終了です。かなり人間に近い動作になったと思います。
残るは終盤です。終盤は残りのマス目すべてを計算して次に置くマス目を決めようという魂胆です。まず仮の状態にしていた ThinkAbleData() の終盤の部分を次のように直します。
void Othello::ThinkAbleData()
{
int total;
total=total_black_stone+total_white_stone;
if(total<PERIOD12){
if(ThinkAbleDataPass1())return;
ThinkAbleDataPass2(PERIOD1);
}
else if(total<PERIOD23){
ThinkAbleDataPass1();
}
else{
ThinkAbleDataPass3();
}
}
ThinkAbleDataPass3() は未定義ですので Othello クラスに、
void ThinkAbleDataPass3();
を追加します。そして、
void Othello::ThinkAbleDataPass3()
{
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int remain[MAX_BOX * MAX_BOX][2];
int lremain;
int able,x,y,min,max;
for(able=0;able<=lable_data;able++){
memcpy(tmp_box_data,box_data,sizeof(box_data));
TempPutStone(tmp_box_data,cpu_stone,able_data[able].x,able_data[able].y);
lremain= -1;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(tmp_box_data[x][y].stone==NO_STONE){
lremain++;
remain[lremain][0]=x;
remain[lremain][1]=y;
}
}
}
min= -1;
max= -1;
GetTotal(tmp_box_data,player_stone,cpu_stone,remain,lremain,&min,&max);
able_data[able].point=min;
}
}
としました。この関数は説明が必要です。
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
この変数は、石を打つ計算をするときに使用するマス目のデータです。
int remain[MAX_BOX * MAX_BOX][2];
int lremain;
この変数は、残りのマス目(石の置いていないマス目)の位置を入れるデータで、計算時になるべく無駄な動作をしないように用意します。
int able,x,y,min,max;
for(able=0;able<=lable_data;able++){
memcpy(tmp_box_data,box_data,sizeof(box_data));
現在のマス目の状態を計算用の変数にコピーします。
TempPutStone(tmp_box_data,cpu_stone,able_data[able].x,able_data[able].y);
自分の石を置きます。
lremain= -1;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(tmp_box_data[x][y].stone==NO_STONE){
lremain++;
remain[lremain][0]=x;
remain[lremain][1]=y;
}
}
}
このループでは空いているマス目の位置を取得します。
min= -1;
max= -1;
最後まで計算して最終の計算機の石の数の最小値と最大値を初期化します。
GetTotal(tmp_box_data,player_stone,cpu_stone,remain,lremain,&min,&max);
最終の計算機の最小値と最大値を計算します。この関数は未定義ですが、各マス目にお互いの石を打っていきすべての状態での計算機の取得できる石の最小値の最も小さい数を最小値とし、最大値の最も大きい数を最大値として返します。
able_data[able].point=min;
最小値を得点にします。最小値と最大値をどのように考えるかが考えていくとよく分からなくなってしまったので、とりあえずはどんなに少なくても一番多く取れるマス目を優先させることにしました。
次は未定義の GetTotal() です。この関数も Othello クラスに、
int GetTotal(
struct box_data_strct (*box_data)[MAX_BOX],
int stone,int xstone,
int (*remain)[2],int lremain,
int *min,int *max);
と追加します。そして、
int Othello::GetTotal(
struct box_data_strct (*box_data_arg)[MAX_BOX],
int stone,int xstone,
int (*remain)[2],int lremain,
int *min,int *max)
{
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int loop,t,i,lrem,rem[2];
if(lremain<0)return 0;
for(loop=0;loop<=1;loop++){
for(i=0;i<=lremain;i++){
if(IsEnablePutStone(
stone,remain[i][0],remain[i][1],1,-1,box_data_arg))break;
}
if(i<=lremain)break;
t=stone; stone=xstone; xstone=t;
}
if(loop>1)return 0;
lrem=lremain;
while(lrem>=0){
if(IsEnablePutStone(stone,remain[0][0],remain[0][1],1,-1,box_data_arg)){
memcpy(tmp_box_data,box_data_arg,sizeof(tmp_box_data));
TempPutStone(tmp_box_data,stone,remain[0][0],remain[0][1]);
if(!GetTotal(tmp_box_data,xstone,stone,remain+1,lremain-1,min,max)){
t=GetStoneCount(tmp_box_data,cpu_stone);
if(*min== -1 || t< *min)*min=t;
if(*max== -1 || t> *max)*max=t;
}
}
memcpy(rem ,remain[0],sizeof(rem) );
memcpy(remain ,remain+1 ,sizeof(rem)*lrem);
memcpy(remain+lrem,rem ,sizeof(rem) );
lrem--;
}
return 1;
}
としました。この関数も説明が必要です。
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
石を打つために使用するマス目のデータです。この GetTotal() は再帰呼び出しで計算をさせますので、石を打つ前の状態(引数 box_data_arg で与えられる)はそのままにしておき、新しいマス目のデータにその状態をコピーしてから計算をするようにします。
int loop,t,i,lrem,rem[2];
if(lremain<0)return 0;
残りのマス目がなくなったときには偽を返します。
for(loop=0;loop<=1;loop++){
for(i=0;i<=lremain;i++){
if(IsEnablePutStone(
stone,remain[i][0],remain[i][1],1,-1,box_data_arg))break;
}
if(i<=lremain)break;
t=stone; stone=xstone; xstone=t;
}
if(loop>1)return 0;
このループは次に石を打てるか否かを検査しています。ループは2回繰り返し、最初の検査のときに石を一つも打てないときには、石の色を変えてもう一度検査をします。この2回のループの検査で1つも打てないときは最後まで打ったということになりますので偽を返します。
下のループでは石を順番に打っていって最後まで計算させるのですが、その前に少し説明が必要です。マス目の残りの位置を入れた変数 remain の操作方法についてです。いま空きのマス目が5つ(A、B、C、D、E)あったとします。このとき lremain には remain の最終要素のインデックス番号が入っていますので4となっています。この値を別の変数にコピーしておきます(プログラムでは lrem)。
ABCDE lremain=4、lrem=4
いまAの位置に置けるか否かを判定したとき、Aは置けないマス目だったとします。このときにはAを lrem の位置に循環して移動させ lrem の値を1つ減らします。
BCDEA lremain=4、lrem=3
この状態は、残りのマス目は B、C、D、E の4つについて現在の色の石を置ける可能性があり、石を打った後の相手は C、D、E、A の4つのマス目に石を打てる可能性があるということになります。
次に B の位置に石を置けるとすると、まずその B の位置に現在の色の石を置き、マス目の残り位置を remain+1、残り数を lremain-1 とし、石の色を反対にして再び GetTotal() を呼び出します。この remain+1 は remain[1] あるいは &remain[1][0] と同じ意味ですので、呼び出された GetTotal() は C、D、E、Aの4つの残りのマス目について計算します。このようにして計算をしていき lrem の値が -1 になったら呼び出し側に戻るようにします。
lrem=lremain;
while(lrem>=0){
if(IsEnablePutStone(stone,remain[0][0],remain[0][1],1,-1,box_data_arg)){
memcpy(tmp_box_data,box_data_arg,sizeof(tmp_box_data));
石を置けるときにこの関数に引数で与えられたマス目の状態を計算用の変数にコピーします。
TempPutStone(tmp_box_data,stone,remain[0][0],remain[0][1]);
現在の色の石を置きます。
if(!GetTotal(tmp_box_data,xstone,stone,remain+1,lremain-1,min,max)){
石の色を反対にし、残りのマス目を渡して再び GetTotal() を呼び出します。この関数が偽を返したときには最後まで石を打ったときですので、このときの計算機の石の数を取得し、その最小値と最大値をセットします(下の行)。
t=GetStoneCount(tmp_box_data,cpu_stone);
if(*min== -1 || t< *min)*min=t;
if(*max== -1 || t> *max)*max=t;
}
}
memcpy(rem ,remain[0],sizeof(rem) );
memcpy(remain ,remain+1 ,sizeof(rem)*lrem);
memcpy(remain+lrem,rem ,sizeof(rem) );
lrem--;
現在検査した位置を残りのマス目の最後に循環して移動し、残りのマス目を1つ減らします。
}
return 1;
この行に到達したときは最後までは打っていないので真を返します。
ここまでで完成なのですが、実際に実験してみると ThinkAbleDataPass3() は計算を開始したままうんともすんとも言いませんでした。要するに計算に時間がかかっていたのです。考えてみれば、残りのマス目が16個になったときにこの計算をするので、組み合わせの数は16の階乗になり、200億の組み合わせになってしまうから当然といえば当然です。終盤の設定の値が悪かったのですね。そこで色々と試してみると、
#define PERIOD23 54
にするとプレーヤーにとってはストレスがない程度に計算が終了することが分かりました。但しこれは私のパソコンでのことですので(あまり速くないです)、パソコンにパワーがあるときにはもう少し小さい値に設定することも可能だと思います。
ここまででやっと完成です。長い間だらだらとオセロゲームを作ってきましたが、ゲームそのものの他にどのようにプログラムを作っていくのかということと、プログラミング上の小技も少しは理解して頂けたでしょうか。
退屈なときにはオセロでもやって気分転換をしてください。『オセロゲームがあるよ』と彼女(彼氏)を自分の部屋に呼ぶのも面白い使い方(?)ではないでしょうか(オセロじゃあ遊びにこないかもしれませんが)。これをエサにして呼んだときには、せめて一回ぐらいはオセロをして下さいね。後は知りません。
最後に完成したプログラムの全リストです。リストは私のプログラムソースをそのまま(タブはスペースに変換してあります)にしてあります。
[resource.h]
#define IDBLACK_STATUS 1000
#define IDWHITE_STATUS 1001
#define IDMESSAGE 1002
#define IDFRAME_BOX 1003
#define IDPUT_STONE 1004
#define IDASK_ORDER 1005
#define IDTHINK_PUT_STONE 1006
[othello.rc]
#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,3,
28,244,244
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"
[othello.cpp]
#include <afxwin.h>
#include "res\resource.h"
#include "dummy.h"
#define MAX_BOX 8
#define STONE_SIZE_RATIO 0.8
#define RED_COLOR RGB(255, 0, 0)
#define BLACK_COLOR RGB( 0, 0, 0)
#define WHITE_COLOR RGB(255,255,255)
#define DGREEN_COLOR RGB( 0,128, 0)
#define NO_STONE 0
#define BLACK_STONE 1
#define WHITE_STONE 2
#define NOTIFY_STONE 3
#define MAX_DRC 8
int xdrc_table[MAX_DRC]={ 1,-1, 0, 0, 1,-1, 1,-1 };
int ydrc_table[MAX_DRC]={ 0, 0, 1,-1, 1,-1,-1, 1 };
#define PERIOD12 36
#define PERIOD23 54
#define PERIOD1 1
#define PERIOD2 2
#define PERIOD3 3
/*=========================== Declare Othello class ===========================
* Declare othello class.
*/
class Othello : public CDialog
{
private:
int stone_size;
struct box_data_strct{
RECT rect;
int stone;
int point;
};
struct box_data_strct box_data[MAX_BOX][MAX_BOX];
CPen pen ,*old_pen ;
CBrush brush,*old_brush;
int pen_brush_using ;
int on_playing;
int player_stone;
int cpu_stone;
int current_stone;
int total_black_stone;
int total_white_stone;
struct able_data_strct{
int x,y;
int point;
};
struct able_data_strct able_data[MAX_BOX*MAX_BOX];
int lable_data;
afx_msg void OnClose();
afx_msg void OnPaint();
afx_msg BOOL OnInitDialog();
afx_msg void OnLButtonDown(UINT nflags,CPoint point);
afx_msg void OnPutStone();
afx_msg void OnAskOrder();
afx_msg void OnThinkPutStone();
void Redraw(CDC *dc);
void InitializeBoxData();
void SetColor(
CDC *dc,COLORREF outline_color,COLORREF fill_color);
void FreeColor(CDC *dc);
void WriteStone(CDC *dc,int x,int y);
int IsEnablePutStone(
int stone,int xput,int yput,
int check_this=1,int drc= -1,
struct box_data_strct (*box_data_arg)[MAX_BOX]=NULL);
void PutStone(int stone,int xput,int yput);
void WriteStoneTotal();
void Delay(double delta_time);
void WriteOrder();
int EnablePutStoneAnywhere();
int WriteEndMessage();
void PurgeLButtonDownMessage();
int Random(int total);
void SortAbleData();
int CompareAbleData(
struct able_data_strct *data,int i,int j);
void ThinkAbleData();
int ThinkAbleDataPass1();
void ThinkAbleDataPass2(int period);
void ThinkAbleDataPass3();
void GetReverseBranchCount(
int stone,int xput,int yput,
int *reverse,int *branch);
void TempPutStone(
struct box_data_strct (*tmp_box_data)[MAX_BOX],
int stone,int xput,int yput);
int GetStoneCount(
struct box_data_strct (*box_data)[MAX_BOX],
int stone);
int IsExistPattern(char *string,char *pattern);
int GetNextPlayerPutCorner(
int stone,int xstone,int xput,int yput,
int sts[4][2]);
int GetTotal(
struct box_data_strct (*box_data)[MAX_BOX],
int stone,int xstone,
int (*remain)[2],int lremain,
int *min,int *max);
public:
Othello(HICON othello_icon);
DECLARE_MESSAGE_MAP()
};
/*========================== Declare OthelloApp class =========================
* Declare OthelloApp class.
*/
class OthelloApp : public CWinApp
{
public:
virtual BOOL InitInstance();
virtual ~OthelloApp();
};
/*========================== OthelloApp::InitInstance =========================
* OthelloApp::InitInstance.
*/
BOOL OthelloApp::InitInstance()
{
m_pMainWnd=new Othello(LoadIcon("OTHELLO_ICON"));
return TRUE;
}
/*========================== OthelloApp::~OthelloApp ==========================
* OthelloApp::~OthelloApp.
*/
OthelloApp::~OthelloApp()
{
delete m_pMainWnd;
}
/*============================== Othello::OnClose =============================
* Othello::OnClose.
*/
afx_msg void Othello::OnClose()
{
PostQuitMessage(0);
}
/*============================== Othello::Othello =============================
* Othello::Othello.
*/
Othello::Othello(HICON othello_icon)
{
Create("OTHELLO");
SetIcon(othello_icon,TRUE);
SetWindowText("オセロゲーム");
}
/*============================== Othello::OnPaint =============================
* Othello::OnPaint.
*/
afx_msg void Othello::OnPaint()
{
CPaintDC dc(this);
Redraw(&dc);
}
/*=========================== Othello::OnInitDialog ===========================
* Othello::OnInitDialog.
*/
afx_msg BOOL Othello::OnInitDialog()
{
int point_table[MAX_BOX][MAX_BOX]={
0, 0, 2, 3, 3, 2, 0, 0,
0, 0, 4, 4, 4, 4, 0, 0,
2, 4, 5, 5, 5, 5, 4, 2,
3, 4, 5, 0, 0, 5, 4, 3,
3, 4, 5, 0, 0, 5, 4, 3,
2, 4, 5, 5, 5, 5, 4, 2,
0, 0, 4, 4, 4, 4, 0, 0,
0, 0, 2, 3, 3, 2, 0, 0
};
RECT rect;
double xsize,ysize,t;
int x,y;
on_playing=0;
pen_brush_using=0;
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);
box_data[x][y].point=point_table[x][y];
}
}
InitializeBoxData();
PostMessage(WM_COMMAND,IDASK_ORDER);
return TRUE;
}
/*========================= Othello::InitializeBoxData ========================
* Othello::InitializeBoxData.
*/
void Othello::InitializeBoxData()
{
int x,y;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
box_data[x][y].stone=NO_STONE;
}
}
box_data[MAX_BOX/2-1][MAX_BOX/2-1].stone=BLACK_STONE;
box_data[MAX_BOX/2 ][MAX_BOX/2-1].stone=WHITE_STONE;
box_data[MAX_BOX/2-1][MAX_BOX/2 ].stone=WHITE_STONE;
box_data[MAX_BOX/2 ][MAX_BOX/2 ].stone=BLACK_STONE;
total_black_stone=2;
total_white_stone=2;
WriteStoneTotal();
}
/*============================== Othello::Redraw ==============================
* Othello::Redraw.
*/
void Othello::Redraw(CDC *dc)
{
RECT rect;
int x,y;
//Fill background.
GetDlgItem(IDFRAME_BOX)->GetWindowRect(&rect);
ScreenToClient(&rect);
SetColor(dc,DGREEN_COLOR,DGREEN_COLOR);
dc->Rectangle(&rect);
//Write frame.
SetColor(dc,BLACK_COLOR,BLACK_COLOR);
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
dc->MoveTo(box_data[x][y].rect.left ,box_data[x][y].rect.top );
dc->LineTo(box_data[x][y].rect.right,box_data[x][y].rect.top );
dc->LineTo(box_data[x][y].rect.right,box_data[x][y].rect.bottom);
dc->LineTo(box_data[x][y].rect.left ,box_data[x][y].rect.bottom);
dc->LineTo(box_data[x][y].rect.left ,box_data[x][y].rect.top );
}
}
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++)WriteStone(dc,x,y);
}
FreeColor(dc);
}
/*============================= Othello::SetColor =============================
* Othello::SetColor.
*/
void Othello::SetColor(CDC *dc,COLORREF outline_color,COLORREF fill_color)
{
FreeColor(dc);
pen.CreatePen(PS_SOLID,0,outline_color);
brush.CreateSolidBrush(fill_color);
old_pen =dc->SelectObject(&pen );
old_brush=dc->SelectObject(&brush);
pen_brush_using=1;
}
/*============================= Othello::FreeColor ============================
* Othello::FreeColor.
*/
void Othello::FreeColor(CDC *dc)
{
if(pen_brush_using){
dc->SelectObject(old_pen );
dc->SelectObject(old_brush);
pen.DeleteObject();
brush.DeleteObject();
}
pen_brush_using=0;
}
/*============================ Othello::WriteStone ============================
* Othello::WriteStone.
*/
void Othello::WriteStone(CDC *dc,int x,int y)
{
COLORREF color;
int xc,yc;
if(box_data[x][y].stone== NO_STONE)return;
else if(box_data[x][y].stone== BLACK_STONE)color=BLACK_COLOR;
else if(box_data[x][y].stone== WHITE_STONE)color=WHITE_COLOR;
else if(box_data[x][y].stone==NOTIFY_STONE)color= RED_COLOR;
xc=(box_data[x][y].rect.left+box_data[x][y].rect.right )/2;
yc=(box_data[x][y].rect.top +box_data[x][y].rect.bottom)/2;
SetColor(dc,color,color);
dc->Ellipse(
xc-stone_size/2,yc-stone_size/2,
xc+stone_size/2,yc+stone_size/2);
FreeColor(dc);
}
/*=========================== Othello::OnLButtonDown ==========================
* Othello::OnLButtonDown.
*/
afx_msg void Othello::OnLButtonDown(UINT nflags,CPoint point)
{
int x,y;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(point.x>box_data[x][y].rect.left
&& point.x<box_data[x][y].rect.right
&& point.y>box_data[x][y].rect.top
&& point.y<box_data[x][y].rect.bottom){
PostMessage(
WM_COMMAND,IDPUT_STONE,(((player_stone<<8)|x)<<8)|y);
return;
}
}
}
}
/*============================ Othello::OnAskOrder ============================
* Othello::OnAskOrder.
*/
afx_msg void Othello::OnAskOrder()
{
if(MessageBox(
"●は先攻、○は後攻です。\n\n"
"先攻しますか?",
"順番の問い合わせ",MB_YESNO)==IDYES){
player_stone=BLACK_STONE;
cpu_stone=WHITE_STONE;
}
else{
player_stone=WHITE_STONE;
cpu_stone=BLACK_STONE;
}
current_stone=BLACK_STONE;
on_playing=1;
WriteOrder();
if(player_stone==current_stone){
PurgeLButtonDownMessage();
}
else{
PostMessage(WM_PAINT);
PostMessage(WM_COMMAND,IDTHINK_PUT_STONE);
}
}
/*========================= Othello::IsEnablePutStone =========================
* Othello::IsEnablePutStone.
*/
int Othello::IsEnablePutStone(
int stone,int xput,int yput,int check_this,int drc,
struct box_data_strct (*box_data_arg)[MAX_BOX])
{
int drc_from,drc_to,x,y,flag,xstone;
if(!box_data_arg)box_data_arg=box_data;
if(check_this && box_data_arg[xput][yput].stone!=NO_STONE)return 0;
xstone=stone==BLACK_STONE ? WHITE_STONE : BLACK_STONE;
if(drc== -1){ drc_from=0 ; drc_to=MAX_DRC-1; }
else { drc_from=drc; drc_to=drc ; }
for(drc=drc_from;drc<=drc_to;drc++){
flag=0;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] ,y=y +ydrc_table[drc]){
if(box_data_arg[x][y].stone==NO_STONE)break;
if(flag==0){
if(box_data_arg[x][y].stone==xstone)flag=1;
else break;
}
else{
if(box_data_arg[x][y].stone== stone)return 1;
}
}
}
return 0;
}
/*========================== Othello::WriteStoneTotal =========================
* Othello::WriteStoneTotal.
*/
void Othello::WriteStoneTotal()
{
char tmp[31];
sprintf(tmp,"● %2d 個",total_black_stone);
SetDlgItemText(IDBLACK_STATUS,tmp);
sprintf(tmp,"○ %2d 個",total_white_stone);
SetDlgItemText(IDWHITE_STATUS,tmp);
}
/*============================ Othello::OnPutStone ============================
* Othello::OnPutStone.
*/
afx_msg void Othello::OnPutStone()
{
LPARAM lparam;
int stone,xput,yput;
/*
* " Check playing "
*/
if(!on_playing)return;
/*
* " Get parameters "
*/
lparam=GetCurrentMessage()->lParam;
yput =lparam & 0x000000ff; lparam=lparam>>8;
xput =lparam & 0x000000ff; lparam=lparam>>8;
stone=lparam & 0x000000ff;
if(stone!=current_stone)return;
/*
* " Check enable put stone "
*/
if(!IsEnablePutStone(stone,xput,yput))return;
/*
* " Put stone "
*/
PutStone(stone,xput,yput);
}
/*=============================== Othello::Delay ==============================
* Othello::Delay.
*/
void Othello::Delay(double delta_time)
{
DWORD to,dt;
dt=(int)(delta_time*1000.0);
to=GetTickCount();
while(1){
if(GetTickCount()-to>dt)break;
}
}
/*============================ Othello::WriteOrder ============================
* Othello::WriteOrder.
*/
void Othello::WriteOrder()
{
char tmp[81];
if(current_stone==player_stone){
sprintf(
tmp,"あなたの %s 石の番です",
current_stone==BLACK_STONE ? "●" : "○");
}
else{
strcpy(tmp,"お待ち下さい");
}
SetDlgItemText(IDMESSAGE,tmp);
}
/*============================= Othello::PutStone =============================
* Othello::PutStone.
*/
void Othello::PutStone(int stone,int xput,int yput)
{
CClientDC dc(this);
int drc,x,y,loop,xstone,*add,*sub;
if(stone==BLACK_STONE){
xstone=WHITE_STONE;
add= &total_black_stone;
sub= &total_white_stone;
}
else{
xstone=BLACK_STONE;
add= &total_white_stone;
sub= &total_black_stone;
}
box_data[xput][yput].stone=stone;
(*add)++;
WriteStone(&dc,xput,yput);
Delay(0.25);
for(drc=0;drc<MAX_DRC;drc++){
if(!IsEnablePutStone(stone,xput,yput,0,drc))continue;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] , y=y +ydrc_table[drc]){
if(box_data[x][y].stone==stone)break;
box_data[x][y].stone=stone;
(*add)++;
(*sub)--;
WriteStone(&dc,x,y);
Delay(0.25);
}
}
WriteStoneTotal();
for(loop=0;loop<=1;loop++){
if(current_stone==BLACK_STONE)current_stone=WHITE_STONE;
else current_stone=BLACK_STONE;
if(EnablePutStoneAnywhere())break;
}
if(loop>1){
if(!WriteEndMessage()){
PostMessage(WM_CLOSE);
return;
}
InitializeBoxData();
WriteStoneTotal();
Redraw(&dc);
PostMessage(WM_COMMAND,IDASK_ORDER);
return;
}
WriteOrder();
if(player_stone==current_stone)PurgeLButtonDownMessage();
else PostMessage(WM_COMMAND,IDTHINK_PUT_STONE);
}
/*====================== Othello::EnablePutStoneAnywhere ======================
* Othello::EnablePutStoneAnywhere.
*/
int Othello::EnablePutStoneAnywhere()
{
int x,y;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(IsEnablePutStone(current_stone,x,y))return 1;
}
}
return 0;
}
/*========================== Othello::WriteEndMessage =========================
* Othello::WriteEndMessage.
*/
int Othello::WriteEndMessage()
{
char tmp[81];
int diff;
diff=player_stone==WHITE_STONE ? total_white_stone-total_black_stone
: total_black_stone-total_white_stone;
if(diff< 0)sprintf(tmp,"あなたの %d 個の負けです。",-diff);
else if(diff==0)strcpy (tmp,"引き分けです。");
else sprintf(tmp,"あなたの %d 個の勝ちです。", diff);
strcat(tmp,"\nもう一度勝負しますか?");
if(MessageBox(tmp,"終了",MB_YESNO)==IDYES)return 1;
else return 0;
}
/*====================== Othello::PurgeLButtonDownMessage =====================
* Othello::PurgeLButtonDownMessage.
*/
void Othello::PurgeLButtonDownMessage()
{
MSG msg;
while(PeekMessage(&msg,m_hWnd,WM_LBUTTONDOWN,WM_LBUTTONDOWN,PM_REMOVE));
}
/*============================== Othello::Random ==============================
* Othello::Random.
*/
int Othello::Random(int total)
{
return GetTickCount()%total;
}
/*=========================== Othello::SortAbleData ===========================
* Othello::SortAbleData.
*/
void Othello::SortAbleData()
{
struct able_data_strct tmp;
int i,j,gap;
gap=(lable_data+1)/2;
while(gap){
i=gap;
while(i<=lable_data){
j=i-gap;
while( j>=0 && CompareAbleData(able_data,j,j+gap)==1){
tmp =able_data[j ];
able_data[j ]=able_data[j+gap];
able_data[j+gap]=tmp;
j-=gap;
}
i++;
}
gap/=2;
}
}
/*========================== Othello::CompareAbleData =========================
* Othello::CompareAbleData.
*/
int Othello::CompareAbleData(struct able_data_strct *data,int i,int j)
{
if(data[i].point>data[j].point)return 0;
if(data[i].point<data[j].point)return 1;
return 0;
}
/*========================== Othello::OnThinkPutStone =========================
* Othello::OnThinkPutStone.
*/
afx_msg void Othello::OnThinkPutStone()
{
CClientDC dc(this);
int x,y,i;
lable_data= -1;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(box_data[x][y].stone!=NO_STONE )continue;
if(!IsEnablePutStone(cpu_stone,x,y))continue;
lable_data++;
able_data[lable_data].x =x;
able_data[lable_data].y =y;
able_data[lable_data].point=box_data[x][y].point;
}
}
ThinkAbleData();
SortAbleData();
for(i=0;i<lable_data;i++){
if(able_data[i].point>able_data[i+1].point){
lable_data=i;
break;
}
}
i=Random(lable_data+1);
x=able_data[i].x;
y=able_data[i].y;
Delay(0.8);
box_data[x][y].stone=NOTIFY_STONE;
WriteStone(&dc,x,y);
box_data[x][y].stone=NO_STONE;
Delay(0.25);
PostMessage(WM_COMMAND,IDPUT_STONE,(((cpu_stone<<8)|x)<<8)|y);
}
/*=========================== Othello::ThinkAbleData ==========================
* Othello::ThinkAbleData.
*/
void Othello::ThinkAbleData()
{
int total;
total=total_black_stone+total_white_stone;
if(total<PERIOD12){
if(ThinkAbleDataPass1())return;
ThinkAbleDataPass2(PERIOD1);
}
else if(total<PERIOD23){
ThinkAbleDataPass1();
}
else{
ThinkAbleDataPass3();
}
}
/*======================= Othello::GetReverseBranchCount ======================
* Othello::GetReverseBranchCount.
*/
void Othello::GetReverseBranchCount(
int stone,int xput,int yput,int *reverse,int *branch)
{
int drc,x,y,flag,xstone;
int rev;
*reverse=0;
*branch =0;
xstone=stone==BLACK_STONE ? WHITE_STONE : BLACK_STONE;
for(drc=0;drc<MAX_DRC;drc++){
flag=0;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] ,y=y +ydrc_table[drc]){
if(box_data[x][y].stone==NO_STONE)break;
if(flag==0){
if(box_data[x][y].stone==stone)break;
flag=1;
rev =1;
}
else{
if(box_data[x][y].stone==xstone){
rev++;
}
else{
(*reverse)+=rev;
(*branch )+=1 ;
break;
}
}
}
}
}
/*=========================== Othello::TempPutStone ===========================
* Othello::TempPutStone.
*/
void Othello::TempPutStone(
struct box_data_strct (*tmp_box_data)[MAX_BOX],
int stone,int xput,int yput)
{
int drc,x,y,xstone;
xstone=stone==BLACK_STONE ? WHITE_STONE : BLACK_STONE;
tmp_box_data[xput][yput].stone=stone;
for(drc=0;drc<MAX_DRC;drc++){
if(!IsEnablePutStone(stone,xput,yput,0,drc,tmp_box_data))continue;
for(x=xput+xdrc_table[drc] , y=yput+ydrc_table[drc];
x>=0 && y>=0 && x<MAX_BOX && y<MAX_BOX;
x=x +xdrc_table[drc] , y=y +ydrc_table[drc]){
if(tmp_box_data[x][y].stone==stone)break;
tmp_box_data[x][y].stone=stone;
}
}
}
/*=========================== Othello::GetStoneCount ==========================
* Othello::GetStoneCount.
*/
int Othello::GetStoneCount(
struct box_data_strct (*box_data)[MAX_BOX],int stone)
{
int x,y,total;
total=0;
for(y=0;y<MAX_BOX;y++){
for(x=0;x<MAX_BOX;x++){
if(box_data[x][y].stone==stone)total++;
}
}
return total;
}
/*======================== Othello::ThinkAbleDataPass2 ========================
* Othello::ThinkAbleDataPass2.
*/
void Othello::ThinkAbleDataPass2(int period)
{
#define BAD_DECREASE_PITCH 10
#define POINT_DECREASE_PITCH 2
struct rbdata_strct{
int index,reverse,branch;
struct rbdata_strct *next;
};
struct rbdata_strct rbdata[MAX_BOX * MAX_BOX];
struct rbdata_strct *top_rbdata,*rbp,*rbc;
struct box_data_strct tmp_box_data1[MAX_BOX][MAX_BOX];
struct box_data_strct tmp_box_data2[MAX_BOX][MAX_BOX];
int i,x,y;
/*
* " Check bad box "
*/
for(i=0;i<=lable_data;i++){
memcpy(tmp_box_data1,box_data,sizeof(box_data));
TempPutStone(
tmp_box_data1,cpu_stone,
able_data[i].x,able_data[i].y);
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(!IsEnablePutStone(
player_stone,x,y,1,-1,tmp_box_data1))continue;
if((x==0 || x==MAX_BOX-1)
&& (y==0 || y==MAX_BOX-1)){
able_data[i].point-=BAD_DECREASE_PITCH;
continue;
}
memcpy(tmp_box_data2,tmp_box_data1,sizeof(tmp_box_data1));
TempPutStone(tmp_box_data2,player_stone,x,y);
if(GetStoneCount(tmp_box_data2,cpu_stone)==0){
able_data[i].point-=BAD_DECREASE_PITCH;
}
}
}
}
if(period!=PERIOD1)return;
/*
* " Make reverse and branch list "
*/
top_rbdata=NULL;
for(i=0;i<=lable_data;i++){
GetReverseBranchCount(
cpu_stone,able_data[i].x,able_data[i].y,
&rbdata[i].reverse,&rbdata[i].branch);
rbdata[i].index=i;
rbdata[i].next =NULL;
rbp=NULL;
rbc=top_rbdata;
while(rbc){
if(rbdata[i].reverse < rbc->reverse)break;
if(rbdata[i].reverse == rbc->reverse
&& rbdata[i].branch == rbc->branch )break;
rbp=rbc;
rbc=rbc->next;
}
rbdata[i].next=rbc;
if(rbp==NULL)top_rbdata=rbdata+i;
else rbp->next=rbdata+i;
}
/*
* " Decrease point "
*/
i=0;
rbp=NULL;
rbc=top_rbdata;
while(rbc){
if(rbp){
if(rbc->reverse!=rbp->reverse
|| rbc->branch !=rbp->branch )i-=POINT_DECREASE_PITCH;
}
able_data[rbc->index].point+=i;
rbp=rbc;
rbc=rbc->next;
}
#undef BAD_DECREASE_PITCH
#undef POINT_DECREASE_PITCH
}
/*========================== Othello::IsExistPattern ==========================
* Othello::IsExistPattern.
*/
int Othello::IsExistPattern(char *string,char *pattern)
{
char tmp[81],c,*str,*s,*p;
int cont,loop;
strcpy(tmp,string);
for(loop=0;loop<=1;loop++){
if(loop==1){
int i,j,k;
j=strlen(tmp)-1;
k=j/2;
for(i=0;i<=k;i++,j--){ c=tmp[i]; tmp[i]=tmp[j]; tmp[j]=c; }
}
str=tmp;
while(*str){
s=str;
p=pattern;
c= *p++; if(c=='$')c= *p++;
if(c=='N' || c=='P' || c=='C'){ cont=1; c+='a'-'A'; }
else { cont=0; }
while(1){
if(*s==c || c=='*'){
if(!*p)return 1;
if(!*++s && *p=='$')return 1;
if(cont){
cont=2;
continue;
}
}
else{
if(cont!=2)break;
if(!*p )break;
}
c= *p++;
if(c=='N' || c=='P' || c=='C'){ cont=1; c+='a'-'A'; }
else { cont=0; }
}
if(*pattern=='$')break;
else str++;
}
}
return 0;
}
/*====================== Othello::GetNextPlayerPutCorner ======================
* Othello::GetNextPlayerPutCorner.
*/
int Othello::GetNextPlayerPutCorner(
int stone,int xstone,int xput,int yput,
int sts[4][2])
{
int cpos_tbl[4][2]={
0 ,0 ,
0 ,MAX_BOX-1,
MAX_BOX-1,0 ,
MAX_BOX-1,MAX_BOX-1};
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int loop,flag;
/*
* " Get enable put xstone to corner "
*/
for(loop=0;loop<=3;loop++){
sts[loop][0]=IsEnablePutStone(
xstone,cpos_tbl[loop][0],cpos_tbl[loop][1]);
}
/*
* " Check prevent "
*/
memcpy(tmp_box_data,box_data,sizeof(box_data));
TempPutStone(tmp_box_data,stone,xput,yput);
flag=0;
for(loop=0;loop<=3;loop++){
sts[loop][1]=IsEnablePutStone(
xstone,cpos_tbl[loop][0],cpos_tbl[loop][1],1,-1,
tmp_box_data);
if(sts[loop][1]!=sts[loop][0])flag=1;
}
return flag;
}
/*======================== Othello::ThinkAbleDataPass1 ========================
* Othello::ThinkAbleDataPass1.
*/
int Othello::ThinkAbleDataPass1()
{
struct pattern_data_strct{
char *pattern;
int add;
};
struct pattern_data_strct pattern_data[]={
{ "NxN" , 0 },
{ "NxNC" , -10 },
{ "NxPN" , -10 },
{ "NPxPN" , 5 },
{ "NPxCN" , -10 },
{ "NxCPN" , -20 },
{ "NxPCN" , 15 },
{ "NxPCPN" , -10 },
{ "NxPCP$" , -10 },
{ "NxCN" , 0 },
{ "NPxCPN" , 10 },
{ "PxCP" , 10 },
{ "NPxPCN" , -10 },
{ "NPxPCPN" , 10 },
{ "NPCPxN" , -10 },
{ "$x" , 20 },
{ "$CxN" , 10 },
{ "$CxP" , 10 },
{ "$CxPC" , 12 },
{ "$CPx" , 10 },
{ "$nPxPN" , 18 },
{ "$nPxP$" , 18 },
{ "$nPxCPN" , 18 },
{ "$nPxCP$" , 18 },
{ "$nPxPCN" , -10 },
{ "$nPxPC$" , 18 },
{ "$PxP" , 10 },
{ "$PCPxN$" , -10 },
{ "$PCxN$" , -10 },
{ "$CxCn" , 15 },
{ "$CxC$" , 5 },
{ "$PxCN" , -10 },
{ "$NxPCPC$" , -10 },
{ "$NxCPC$" , -10 },
{ "$CNxCP$" , -10 },
{ "CCxCN" , 10 },
{ "CCxCP$" , 10 },
{ "$CPCnN$" , -5 },
{ "$CPCn$" , -10 },
{ "$CxC$" , 10 },
{ "$PxNP" , -10 },
{ "",0 }
};
int able,x,y,dx,dy,add,i,flag;
char sts[MAX_BOX+1];
int csts[4][2];
sts[MAX_BOX]='\0';
flag=0;
for(able=0;able<=lable_data;able++){
add=0;
if( able_data[able].x==0 || able_data[able].x==MAX_BOX-1
|| (able_data[able].y==0 || able_data[able].y==MAX_BOX-1)){
if(able_data[able].x==0 || able_data[able].x==MAX_BOX-1){
y=0; dy=1; x=able_data[able].x; dx=0;
}
else{
x=0; dx=1; y=able_data[able].y; dy=0;
}
for(i=0;i<MAX_BOX;i++,x+=dx,y+=dy){
if(x==able_data[able].x && y==able_data[able].y)sts[i]='x';
else if(box_data[x][y].stone ==player_stone )sts[i]='p';
else if(box_data[x][y].stone == cpu_stone )sts[i]='c';
else sts[i]='n';
}
for(i=0;pattern_data[i].pattern[0];i++){
if(IsExistPattern(sts,pattern_data[i].pattern)){
if(abs(pattern_data[i].add)>abs(add)){
add=pattern_data[i].add;
}
}
}
}
if(GetNextPlayerPutCorner(
cpu_stone,player_stone,
able_data[able].x,able_data[able].y,csts)){
for(i=0;i<=3;i++){
if( csts[i][0] && !csts[i][1])add+=20;
else if(!csts[i][0] && csts[i][1])add-=20;
}
}
able_data[able].point+=add;
if(add<= -10 || add>=10)flag=1;
}
return flag;
}
/*======================== Othello::ThinkAbleDataPass3 ========================
* Othello::ThinkAbleDataPass3.
*/
void Othello::ThinkAbleDataPass3()
{
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int remain[MAX_BOX * MAX_BOX][2];
int lremain;
int able,x,y,min,max;
for(able=0;able<=lable_data;able++){
memcpy(tmp_box_data,box_data,sizeof(box_data));
TempPutStone(
tmp_box_data,cpu_stone,able_data[able].x,able_data[able].y);
lremain= -1;
for(x=0;x<MAX_BOX;x++){
for(y=0;y<MAX_BOX;y++){
if(tmp_box_data[x][y].stone==NO_STONE){
lremain++;
remain[lremain][0]=x;
remain[lremain][1]=y;
}
}
}
min= -1;
max= -1;
GetTotal(
tmp_box_data,player_stone,cpu_stone,remain,lremain,&min,&max);
able_data[able].point=min;
}
}
/*============================= Othello::GetTotal =============================
* Othello::GetTotal.
*/
int Othello::GetTotal(
struct box_data_strct (*box_data_arg)[MAX_BOX],
int stone,int xstone,
int (*remain)[2],int lremain,
int *min,int *max)
{
struct box_data_strct tmp_box_data[MAX_BOX][MAX_BOX];
int loop,t,i,lrem,rem[2];
/*
* " Check enable put "
*/
if(lremain<0)return 0;
for(loop=0;loop<=1;loop++){
for(i=0;i<=lremain;i++){
if(IsEnablePutStone(
stone,remain[i][0],remain[i][1],1,-1,box_data_arg))break;
}
if(i<=lremain)break;
t=stone; stone=xstone; xstone=t;
}
if(loop>1)return 0;
/*
* " Put "
*/
lrem=lremain;
while(lrem>=0){
if(IsEnablePutStone(stone,remain[0][0],remain[0][1],1,-1,box_data_arg)){
memcpy(tmp_box_data,box_data_arg,sizeof(tmp_box_data));
TempPutStone(tmp_box_data,stone,remain[0][0],remain[0][1]);
if(!GetTotal(
tmp_box_data,xstone,stone,remain+1,lremain-1,
min,max)){
t=GetStoneCount(tmp_box_data,cpu_stone);
if(*min== -1 || t< *min)*min=t;
if(*max== -1 || t> *max)*max=t;
}
}
memcpy(rem ,remain[0],sizeof(rem) );
memcpy(remain ,remain+1 ,sizeof(rem)*lrem);
memcpy(remain+lrem,rem ,sizeof(rem) );
lrem--;
}
return 1;
}
BEGIN_MESSAGE_MAP(Othello,CDialog)
ON_COMMAND(IDASK_ORDER,OnAskOrder)
ON_COMMAND(IDPUT_STONE,OnPutStone)
ON_COMMAND(IDTHINK_PUT_STONE,OnThinkPutStone)
ON_COMMAND(IDOK ,Default)
ON_COMMAND(IDCANCEL,Default)
ON_WM_LBUTTONDOWN()
ON_WM_PAINT()
ON_WM_CLOSE()
END_MESSAGE_MAP()
OthelloApp OthelloApp;
アイコンを設定するようになっていますので、othello.ico を適当に作って下さい。
それではまた次回。






