// Osero AI Version-1
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;
using System.Collections; //ArrayList
using System.IO; // for File, StreamReader
using System.Text; // for Encoding
class Cell
{ public ArrayList AL = null;
public int n; // N 手目
public int p; // 座標
public int v=0; // 点数
}
public class MyForm : Form
{ // 黒番の局面に設定すること
int[,] m_tt = new int[8, 8]
{{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, -1, 0, 0, 0 },
{ 0, 0, 0, -1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 }};
int[,] m_vt = new int[8, 8] // 1-1, 2-2 は別途
{{ 0, -6, 4, 2, 2, 4, -6, 0 },
{ -6, 0, -2, -1, -1, -2, 0, -6 },
{ 4, -2, 0, 0, 0, 0, -2, 4 },
{ 2, -1, 0, 0, 0, 0, -1, 2 },
{ 2, -1, 0, 0, 0, 0, -1, 2 },
{ 4, -2, 0, 0, 0, 0, -2, 4 },
{ -6, 0, -2, -1, -1, -2, 0, -6 },
{ 0, -6, 4, 2, 2, 4, -6, 0 }};
int[,] m_t = new int[8, 8]; // オセロ盤
int[,] t00 = new int[4, 2] //0-0 座標テーブル(Y,X)
{{ 0,0 }, { 0,7 }, { 7,7 }, { 7,0 }};
int[,] t11 = new int[4, 2] //1-1 座標テーブル(Y,X)
{{ 1,1 }, { 1,6 }, { 6,6 }, { 6,1 }};
int[,] dt = new int[4, 2] //上辺→,右辺↓,下辺←,左辺↑
{{ 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 }};
int[,] dt5 = new int[4, 2] //左上,右上,右下,左下
{{ 1, 1 }, { 1, -1 }, { -1, -1 }, { -1, 1 }};
int[] m_dat = new int[70]; // 棋譜の記録(Y*10+X)
int m_num; // m_dat の Index
int m_my, m_you; // マシンの駒, プレイヤーの駒
int m_teban= 0; // 手番(9:終局)
int m_pos; // Final 関数の座標値
int x,y;
Random rand = new Random();
Cell m_top = new Cell();
int m_black, m_white, m_v;
string file_name = "C:\\tmp\\osero.txt";
StreamReader reader;
// Constructor
public MyForm()
{
BackColor = SystemColors.AppWorkspace;
Width = 560;
Height = 600;
Paint += new PaintEventHandler(MyHandler);
MouseDown += new MouseEventHandler(OnMyMouseDown);
Set_Cell();
m_t = (int[,])m_tt.Clone();
}
// オセロ盤の描画
private void MyHandler(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawImage(new Bitmap("c:\\data\\test\\ban.gif"), new PointF(10F, 10F));
for (y = 0; y < 8; y++)
{ for (x = 0; x < 8; x++)
{ if (m_t[y, x] == 1) g.DrawImage(new Bitmap("c:\\data\\test\\koma_b.gif"), new PointF(x * 61 + 28, y * 61 + 24));
if (m_t[y, x] == -1) g.DrawImage(new Bitmap("c:\\data\\test\\koma_w.gif"), new PointF(x * 61 + 28, y * 61 + 24));
}
}
m_teban= Pass(m_teban);
}
// マウスのクリック
private void OnMyMouseDown(object sender, MouseEventArgs e)
{
int x,y;
if (e.Button == MouseButtons.Right)
{
m_t = (int[,])m_tt.Clone();
if (m_num > 0) m_num--; // 一手戻す
if (m_dat[m_num] == 99) m_num--;
for (int i = 0; i < m_num; i++)
{
if (i % 2 == 0) Reverse(1, m_dat[i], m_t);
else Reverse(-1, m_dat[i], m_t);
}
m_teban = Set_Teban(m_num);
Invalidate();
return;
}
if (m_teban==9)
{ Pass(9);
return;
}
x = (e.X - 28) / 61;
y = (e.Y - 24) / 61;
if (m_teban==-1)
{ //Think(0, m_teban, m_t);
Cell_Srh(m_teban, m_t);
y= m_pos/10;
x= m_pos%10;
}
if (Reverse(m_teban, x, y, m_t)==false)
{ string str = "Play Error C:" + m_teban + "[" + y + "," + x + "]";
MessageBox.Show(str);
return;
}
m_dat[m_num] = y*10+x;
m_num++;
m_teban = Set_Teban(m_num);
Invalidate();
}
// m_num を参照して手番を設定
int Set_Teban(int num)
{
if (m_num % 2 == 0)
{
m_my = 1; // コンピュータ
m_you = -1; // プレイヤー
return 1;
}
m_my = -1;
m_you = 1;
return -1;
}
// パス, 終局を調べる
int Pass(int c)
{
int nc;
ArrayList array;
if (c == 0)
{
m_num = 0;
m_my = 1;
m_you = -1;
return 1; // 黒番で Start
}
if (c!=9) // パスを調べる
{ array = Search(c, m_t);
if (array.Count>0) return c;
}
nc= 0-c; // 反手番で調べる
array = Search(nc, m_t);
if (array.Count>0)
{ if (c==1)
{ MessageBox.Show("黒はパスです"); }
else
{ MessageBox.Show("白はパスです"); }
m_dat[m_num] = 99;
m_num++;
m_my = 0 - m_my; // 手番を反転
m_you = 0 - m_you;
return nc;
}
if (c!=9) Put();
string str= "★Game Over 黒:" + Count(1, m_t) + " 白:" + Count(-1, m_t);
MessageBox.Show(str);
return 9; // 終局
}
// t[yp,xp] に打てるか調べる
bool Check(int c, int xp, int yp, int[,] t)
{
int i,j,x,y;
int nc = 0-c;
if (yp>7 || xp>7 || yp<0 || xp<0 || c==0 || t[yp,xp]!=0) return false;
for(i=-1; i<2; i++)
for(j=-1; j<2; j++)
{ y = yp + i; // 上下左右を調べる
x = xp + j;
if (y<8 && x<8 && y>=0 && x>=0 && t[y,x]==nc)
{ for(; y<8 && x<8 && y>=0 && x>=0 && t[y,x]==nc; y+=i,x+=j);
if (y<8 && x<8 && y>=0 && x>=0 && t[y,x]==c) return true;
}
}
return false; //置くことができない
}
bool Check(int c, int pos, int[,] t)
{
int x,y;
y= pos/10;
x= pos%10;
return Check(c,x,y,t);
}
// t[y,x] に駒を置いて、挟んだ駒を裏返す
bool Reverse(int c, int xp, int yp, int[,] t)
{
int i, j, x, y;
bool sw = false;
int nc = 0-c;
if (yp > 7 || xp > 7 || yp < 0 || xp < 0 || c == 0 || t[yp, xp] != 0) return false;
for (i = -1; i < 2; i++)
for (j = -1; j < 2; j++)
{
y = yp + i;
x = xp + j;
if (y < 8 && x < 8 && y >= 0 && x >= 0 && t[y, x] == nc)
{
for (; y < 8 && x < 8 && y >= 0 && x >= 0 && t[y, x] == nc; y += i, x += j) ;
if (y < 8 && x < 8 && y >= 0 && x >= 0 && t[y, x] == c)
{
for (; y != yp || x != xp; y -= i, x -= j) t[y, x] = c;
sw = true;
}
}
}
if (sw == false) return false;
t[yp, xp] = c;
return true;
}
bool Reverse(int c, int pos, int[,] t)
{
int x,y;
y= pos/10;
x= pos%10;
return Reverse(c,x,y,t);
}
// 打てる手をサーチ
ArrayList Search(int c, int[,] t)
{
int x,y,pos;
ArrayList array= new ArrayList();
for(y=0; y<8; y++)
for(x=0; x<8; x++)
{ if (Check(c,x,y,t))
{ pos= y*10 + x;
array.Add(pos);
}
}
return array;
}
// 駒のカウント
int Count(int c, int[,] t)
{
int cnt;
cnt = 0;
for (int y = 0; y < 8; y++)
{
for (int x = 0; x < 8; x++)
if (t[y, x] == c) cnt++;
}
return cnt;
}
// t[8][8] から辺を一列取り出す
ArrayList GetLine(int no, int[,] t)
{
ArrayList array= new ArrayList();
int xp,yp,k;
yp= t00[no,0];
xp= t00[no,1];
for(k=0; k<8; k++)
{ array.Add(t[yp,xp]);
yp+= dt[no,0];
xp+= dt[no,1];
}
return array;
}
// コーナ(黒or白)から展開する駒をカウント
int Corner(int no, int[,] t)
{ int xp, yp, cnt;
int koma;
yp = t00[no, 0];
xp = t00[no, 1];
if (t[yp, xp] == 0) return 0;
koma = t[yp, xp];
//コーナ(no)のt[8,8]をt55にコピー
int[,] t55 = new int[5, 5];
for(y=0, yp=t00[no, 0]; y<5; y++, yp+=dt5[no, 0])
{ for(x=0, xp=t00[no, 1]; x<5; x++, xp+=dt5[no, 1])
{ t55[y,x] = t[yp,xp]; }
}
//コーナの確定した石数を求める
cnt = 0;
xp = 4;
for(y=0; y<4 && t55[y,0]==koma; y++)
{ for(x=0; x<xp; x++)
{ if ((t55[y,x]!=koma) ||
(y>0 && x>0 && t55[y-1, x+1]!=koma && t55[y+1, x-1]!=koma))
{ xp=x; break; }
cnt++;
}
}
return cnt;
}
// 端の駒(黒or白)の立場で続く駒を評価
int EvEdge(ArrayList s, int bcnt, int wcnt, int scnt)
{
if (scnt<3) return 0;
if ((int)s[0]==1 && (int)s[1]==-1 && (int)s[2]==0 ) return wcnt*20; // [●○△・・・]
if ((int)s[0]==-1 && (int)s[1]==1 && (int)s[2]==0 ) return bcnt*20; // [○●△・・・]
return 0;
}
// 1-2 に駒(黒or白)があるとき、C の手番(立場)でパターンを調べる
int EvPattern(ArrayList s, int c, int sc)
{
int you = 0-c;
if (sc == 4)
{ // [△○●・・・・] 黒(c)の手番で隅を取れる
if ((int)s[1]==you && (int)s[2]==c) return 50;
return 20; // 辺が全て黒になる[△●○・・・]
}
if ((int)s[1]==c) // 1-2 に黒 [△●・・・]
{
if ((int)s[2]==you && (int)s[3]==c) return -60; // [△●○●・・・]
if ((int)s[2]==you && (int)s[3]==0 && (int)s[4]==you) return -40; // [△●○ ○・・・]
return 0;
}
// 1-2 に白[△○・・・]
if ((int)s[2]==c && (int)s[3]==you) return 60; // [△○●○・・・]
if ((int)s[2]==0 && (int)s[3]==you) return 20; // [△○※○・・・]
if ((int)s[2]==0 && (int)s[3]==0 && (int)s[4]==c) return 30; //[△○※・●・・・]
if ((int)s[2]==0 && (int)s[3]==c && (int)s[4]==you) return 30; //[△○※●○・・・]
return 0;
}
// C の手番(立場)で取り出した1列の辺を評価
int EvLine(ArrayList s, int c)
{
ArrayList ws= new ArrayList();
int mcnt, ycnt, scnt, rt, val, wk;
int you= 0-c;
// 黒石, 白石, 列の数をカウント
mcnt = ycnt = 0;
scnt = 1;
wk = (int)s[0];
foreach(int DAT in s)
{
if (DAT==c) mcnt++;
if (DAT==you) ycnt++;
if (DAT != wk)
{
scnt++;
wk = DAT;
}
}
if (mcnt+ycnt==8) return (mcnt-ycnt)*20;
if (scnt<3) return 0;
//連続する(○,●)を一つに詰める
wk = 9;
foreach(int DAT in s)
{
if (DAT==0 || DAT!=wk)
{
ws.Add(DAT);
wk = DAT;
}
}
rt= ws.Count-1;
// 端に黒(白)が置かれている
if ((int)ws[0] != 0)
{
val = EvEdge(ws, mcnt, ycnt, scnt);
if ((int)ws[0]==c) return val;
else return(0-val);
}
if ((int)ws[rt]!=0)
{
ws.Reverse();
val = EvEdge(ws, mcnt, ycnt, scnt);
if ((int)ws[0]==c) return val;
else return(0-val);
}
// [△・・・△]
if (scnt==3) return (mcnt-ycnt)*10;
val= 0;
// 1-2 に駒 [△*・・・△]
if ((int)ws[1]!=0 || (int)ws[rt-1]!=0)
{
if ((int)ws[1]!=0)
{
if ((int)ws[1]==c) val-= EvPattern(ws,c,scnt);
else val+= EvPattern(ws,c,scnt);
}
if ((int)ws[rt-1]!=0)
{
ws.Reverse();
if ((int)ws[1]==c) val-= EvPattern(ws,c,scnt);
else val+= EvPattern(ws,c,scnt);
}
}
// 両端は[△△・・・△△]
if (scnt==3) return (mcnt-ycnt)*10; //[△○△][△●△]
if (scnt==4) return (mcnt+ycnt)*10; //[△○●△]
return val;
}
// 現在の局面を C で評価
int Eval(int c, int[,] wt)
{ ArrayList line;
int x,y,val;
int nc= 0-c;
bool flg;
val= 0;
for(int i=0; i<4; i++)
{ y= t00[i,0];
x= t00[i,1];
if (wt[y,x]!=0) // Corner() 関数の評価
{ if (wt[y,x]==c) val+= Corner(i,wt)*20+30;
else val-= Corner(i,wt)*20+30;
}
else
{
flg = false;
if (Check(c,x,y,wt)) // 隅を取れる
{ val += 60;
flg = true;
}
if (flg==false)
{ y = t11[i, 0];
x = t11[i, 1];
if (wt[y,x]==nc) val += 20;
if (wt[y,x]==c) val -= 20;
}
line = GetLine(i, wt);
int wk= EvLine(line,c);
val += wk;
}
}
for(int i=0; i<8; i++)
{ for(int j=0; j<8; j++)
{ if (wt[i,j]==c) val+= m_vt[i,j];
if (wt[i,j]==nc) val-= m_vt[i,j];
}
}
return val;
}
//★ t[8,8] の局面を c の手番で再帰コールする
// 終盤12手は読み切り(それ以外は3手先の白番で評価)
int Think(int lev, int c, int[,] t)
{
ArrayList array;
int[,] w_t;
int best, pos, ans;
int nc;
if (lev > 30)
{
MessageBox.Show("Think Call Over Level");
return (Count(m_my, t) - Count(m_you, t));
}
nc = 0 - c;
array = Search(c, t);
if (array.Count == 0)
{
array = Search(nc, t);
if (array.Count == 0) //☆終局(両方がパス)
{ return (Count(m_my, t) - Count(m_you, t)); }
return (Think(lev+1, nc, t)); //☆反転して再起コール
}
// 白番・3手先で評価
if (m_num<48 && lev>2 && c==m_my) return Eval(c,t);
pos = 99;
best = 60000;
if (c==m_my) best= -60000;
foreach (int DAT in array)
{
w_t = (int[,])t.Clone();
Reverse(c, DAT, w_t);
ans= Think(lev+1,nc,w_t);
if (best<ans && c==m_my)
{
best = ans;
pos = DAT;
}
if (best>ans && c==m_you)
{
best = ans;
pos = DAT;
}
if (best==ans && rand.Next(2)==0)
{
best = ans;
pos = DAT;
}
}
array.Clear();
if (lev == 0) m_pos = pos; // lev=0 のとき座標を設定
return best;
}
// Cell List を検索して手を決める(未登録のときは Think 関数で決定)
int Cell_Srh(int c, int[,] t)
{ Cell p,q;
int i,k,v,best=0;
p=q= m_top;
for(i=0; i<m_num; i++)
{
if (p.AL == null) return Think(0,c,t);
for (k=0; k<p.AL.Count; k++)
{ q= (Cell)p.AL[k];
if (q.p==m_dat[i]) break;
}
if (k>=p.AL.Count) return Think(0,c,t); // 未登録
p= q;
}
v = -100000; // 先手番のときは最大値
if (c==-1) v= 100000; // 後手番のときは最小値
if (p.AL == null) return Think(0,c,t);
for (k = 0; k < p.AL.Count; k++)
{ q = (Cell)p.AL[k];
if (c==-1 && v>q.v) // 後手番
{ v= q.v;
best= k;
}
if (c==1 && v<q.v) // 先手番
{ v= q.v;
best= k;
}
}
if (c==-1 && v>0) return Think(0,c,t); // 登録は不利な局面のみ
m_pos = ((Cell)p.AL[best]).p;
return m_pos;
}
// osero.txt を Cell に登録
void Set_Cell()
{
int num;
if (!File.Exists(file_name)) return; // 存在確認
reader = new StreamReader(file_name,Encoding.GetEncoding("utf-8"));
for(num = 0; num<10000; num++)
{ if (Input()==false) break;
m_v = m_black-m_white;
if (m_v>0) m_v = 1;
if (m_v<0) m_v = -1;
AddData(m_v, m_num, m_dat);
}
Debug.Write("対局データが終わりました 件数:" + num + "\n");
reader.Close();
}
// 対局データを osero.txt に書き出す
void Put()
{
int bc, wc;
string str;
StreamWriter writer = new StreamWriter(file_name,true,Encoding.GetEncoding("utf-8"));
bc = Count(1, m_t);
wc = Count(-1, m_t);
str = bc.ToString() + "," + wc.ToString() + ",";
for(int j=0; j<m_num; j++) str += m_dat[j] + ",";
writer.WriteLine(str);
writer.Close();
}
// 1件の対局データを osero.txt に入力
bool Input()
{
char[] delimiter = { ',' }; // 区切り符号
string str;
string[] swk;
str = reader.ReadLine();
if (str==null || str.Length<8)
{ return false; }
swk= str.Split(delimiter); // カンマで分割
m_num = swk.GetLength(0)-3;
m_black = int.Parse(swk[0]);
m_white = int.Parse(swk[1]);
for(int i=0; i<m_num; i++)
{ m_dat[i] = int.Parse(swk[i+2]); }
return true;
}
// dat[num] の一局を top(Cell) に登録
void AddData(int val, int num, int[] dat)
{ Cell pt = m_top;
int k;
for(int i=0; i<num; i++)
{ k = AddCell(pt, i, dat[i]);
pt = (Cell)pt.AL[k];
pt.v += val;
}
}
// pt の ArrayList に i, p のセルを追加する
int AddCell(Cell pt, int i, int p)
{ Cell wk,wk2;
int j;
wk = new Cell();
wk.n = i;
wk.p = p;
if (pt.AL==null) pt.AL= new ArrayList();
for (j=0; j<pt.AL.Count; j++)
{ wk2 = (Cell)pt.AL[j];
if (wk2.p==p) break;
}
if (j>=pt.AL.Count) pt.AL.Add(wk);
return j;
}
}
class osero
{
public static void Main()
{
MyForm mf = new MyForm();
Application.Run(mf);
}
}
|