2 五子棋
2.1 需求
基于wxWidgets
实现一个五子棋游戏
2.2 功能
1、显示一个15*15线组成的围棋棋盘。
2、棋子只能落在线的交点上。
3、黑白子交替下棋。
4、当黑子或者白子连续五个子组成一条直线,则为胜利。
实例:双人对战
#include <wx/wx.h>
#include <vector>
using namespace std;
//面板类
class GoPanel:public wxPanel{
vector<wxPoint> vec;
const int cellsize = 30;
const int linelen = cellsize*14;
enum ChessType{
EMPTY,BLACK,WHITE,
};
ChessType broad[15][15] = {EMPTY};
bool black = true;
wxPoint offset;
public:
GoPanel(wxFrame* pframe):wxPanel(pframe){
//将面板与绘图绑定
Bind(wxEVT_PAINT,&GoPanel::OnPaint,this);
Bind(wxEVT_LEFT_DOWN,&GoPanel::OnLeftDown,this);
}
void OnLeftDown(wxMouseEvent& event){
wxPoint point = event.GetPosition();
int x = round(1.0*(point.x-offset.x)/cellsize);
int y = round(1.0*(point.y-offset.y)/cellsize);
if(broad[x][y] != EMPTY || x >= 15 || y >= 15) return;
broad[x][y] = black?BLACK:WHITE;
black = !black;
Refresh();
if(Check(x,y)){
wxMessageBox(broad[x][y] == BLACK?"Black Win":"White Win","Note");
}
}
bool Check(int x,int y)const {
ChessType type = broad[x][y];
int count = 1;
//横向检测
for(int i = x+1;i < 15;++i){
if(broad[i][y] == type){
++count;
}else{
break;
}
}
if(count >= 5) return true;
for(int i = x-1;i >= 0;--i){
if(broad[i][y] == type){
++count;
}else{
break;
}
}
if(count >= 5) return true;
//纵向检测
count = 1;
for(int i = y+1;i < 15;++i){
if(broad[x][i] == type){
++count;
}else{
break;
}
}
if(count >= 5) return true;
for(int i = y-1;i >= 0;--i){
if(broad[x][i] == type){
++count;
}else{
break;
}
}
if(count >= 5) return true;
//斜向检测
count = 1;
for(int i = 1;i <= 5 && x+i < 15 && y+i < 15;++i){
if(broad[x+i][y+i] == type){
++count;
}else{
break;
}
}
if(count >= 5) return true;
for(int i = 1;i<=5 && x-i>=0 && y-i>=0;++i){
if(broad[x-i][y-i] == type){
++count;
}else{
break;
}
}
if(count >= 5) return true;
//反斜检测
count = 1;
for(int i = 1;i <= 5 && x+i < 15 && y-i >= 0;++i){
if(broad[x+i][y-i] == type){
++count;
}else{
break;
}
}
if(count >= 5) return true;
for(int i = 1;i<=5 && x-i>=0 && y+i<15;++i){
if(broad[x-i][y+i] == type){
++count;
}else{
break;
}
}
if(count >= 5) return true;
return false;
}
void OnPaint(wxPaintEvent&){
//定义画笔
wxPaintDC dc(this);
//棋盘居中
wxSize size = GetClientSize();
int left = (size.GetWidth()-linelen)/2;
int top = (size.GetHeight()-linelen)/2;
offset = wxPoint(left,top);
for(int i = 0;i < 15;++i){
//dc.DrawLine(wxPoint(left,top+cellsize*i),wxPoint(left+linelen,top+cellsize*i));
//dc.DrawLine(wxPoint(left+cellsize*i,top),wxPoint(left+cellsize*i,top+linelen));
dc.DrawLine(wxPoint(0,cellsize*i)+offset,wxPoint(linelen,cellsize*i)+offset);
dc.DrawLine(wxPoint(cellsize*i,0)+offset,wxPoint(cellsize*i,linelen)+offset);
}
for(int i = 0;i < 15;++i){
for(int j = 0;j < 15;++j){
if(broad[i][j] != EMPTY) DisplayChess(dc,i,j);
}
}
}
void DisplayChess(wxPaintDC& dc,int x,int y){
dc.SetBrush(broad[x][y] == WHITE? *wxWHITE_BRUSH:*wxBLACK_BRUSH);
//dc.DrawCircle(offset.x+x*cellsize,offset.y+y*cellsize,cellsize/2);
//dc.DrawCircle(wxPoint(x*cellsize,y*cellsize)+offset,cellsize/2);
dc.DrawCircle(wxPoint(x,y)*cellsize+offset,cellsize/2);
}
};
//框架类
//框架类中添加面板类
class GoFrame:public wxFrame{
public:
GoFrame(const char* title):wxFrame(nullptr,wxID_ANY,title){
wxPanel* p = new GoPanel(this);
}
};
//应用程序类
//应用程序类中添加框架类
class GoApp:public wxApp{
public:
bool OnInit()override{
GoFrame* pframe = new GoFrame("Name");
pframe->Show();
return true;
};
};
wxIMPLEMENT_APP(GoApp);
2.3 加入AI算法实现人机对战
最常见的基本棋型大体有以下几种:连五,活四,冲四,活三,眠三,活二,眠二。
- 必胜
- 将胜
- 待胜
- 不胜
棋型 | 进攻方 | 防守方 |
---|---|---|
连五 | 胜 | 负 |
活四 | 必胜 | 必负 |
冲四 | 必须进攻 | 必须防守 |
活三 | 必须进攻 | 必须防守 |
眠三 | 可以进攻 | 可以进攻 |
活二 | 可以进攻 | 可以进攻 |
眠二 | 可以进攻 | 可以进攻 |
- 规则:
1、连五直接判定胜负。
2、活四、冲四、活三抢占关键点。
3、眠三、活二、眠二优先进攻。
判断整个棋盘黑白所有的棋型,选取优势棋型进攻防守。例如,执白,如果黑棋的棋型优于白棋防守;反之进攻。
- 实例:加入AI算法的人机五子棋对战
注:机器方仅有防守功能,需进一步优化
#include <wx/wx.h>
#include <vector>
using namespace std;
//面板类
class GoPanel:public wxPanel {
vector<wxPoint> vec;
const int cellsize = 30;
const int linelen = cellsize*14;
enum ChessType {
EMPTY,BLACK,WHITE,
};
ChessType broad[15][15] = {EMPTY};
bool black = true;
wxPoint offset;
pair<int,int> nextpos;
int maxcount = 0;
public:
GoPanel(wxFrame* pframe):wxPanel(pframe) {
//将面板与绘图绑定
Bind(wxEVT_PAINT,&GoPanel::OnPaint,this);
Bind(wxEVT_LEFT_DOWN,&GoPanel::OnLeftDown,this);
}
void OnLeftDown(wxMouseEvent& event) {
wxPoint point = event.GetPosition();
int x = round(1.0*(point.x-offset.x)/cellsize);
int y = round(1.0*(point.y-offset.y)/cellsize);
if(broad[x][y] != EMPTY || x >= 15 || y >= 15) return;
Chess(x,y);
Refresh();
Chess(nextpos.first,nextpos.second); //机器(白棋)
Refresh();
}
void Chess(int x,int y) {
broad[x][y] = black?BLACK:WHITE;
black = !black;
if(Check(x,y)) {
wxMessageBox(broad[x][y] == BLACK?"Black Win":"White Win","Note");
}
}
bool Check(int x,int y) {
ChessType type = broad[x][y];
int count = 1;
maxcount = 0;
int tryx1 = -1,tryy1 = -1, tryx2 = -1,tryy2 = -1;
int add1= 0, add2 = 0;
//横向检测
for(int i = x+1; i < 15; ++i) {
if(broad[i][y] == type) {
++count;
} else {
if(broad[i][y] == EMPTY) {
tryx1 = i;
tryy1 = y;
int j = i+1;
while(j<15 && broad[j][y] == type) {
++add1;
++j;
}
}
break;
}
}
if(count >= 5) return true;
for(int i = x-1; i >= 0; --i) {
if(broad[i][y] == type) {
++count;
} else {
if(broad[i][y] == EMPTY) {
tryx2 = i;
tryy2 = y;
int j = i-1;
while(j>=0 && broad[j][y] == type) {
++add2;
--j;
}
}
break;
}
}
if(count >= 5) return true;
if(maxcount < count+max(add1,add2) && (tryx1 != -1 || tryx2!=-1)) {
maxcount = count+max(add1,add2);
if(add1 == add2) {
if(tryx1 == -1) {
nextpos = make_pair(tryx2,tryy2);
} else {
nextpos = make_pair(tryx1,tryy1);
}
} else {
nextpos = add1 > add2?make_pair(tryx1,tryy1):make_pair(tryx2,tryy2);
}
}
cout << "横向检测:" << maxcount << " Point:" << nextpos.first << "," << nextpos.second << endl;
//纵向检测
tryx1 = tryy1 = tryx2 = tryy2 = -1;
add1 = add2 = 0;
count = 1;
for(int i = y+1; i < 15; ++i) {
if(broad[x][i] == type) {
++count;
} else {
if(broad[x][i] == EMPTY) {
tryx1 = x;
tryy1 = i;
int j = i+1;
while(j<15 && broad[x][j] == type) {
++add1;
++j;
}
}
break;
}
}
if(count >= 5) return true;
for(int i = y-1; i >= 0; --i) {
if(broad[x][i] == type) {
++count;
} else {
if(broad[x][i] == EMPTY) {
tryx2 = x;
tryy2 = i;
int j = i-1;
while(j>=0 && broad[x][j] == type) {
++add2;
--j;
}
}
break;
}
}
if(count >= 5) return true;
if(maxcount < count+max(add1,add2)&&(tryx1!=-1 || tryx2!=-1)) {
maxcount = count+max(add1,add2);
if(add1 == add2) {
if(tryx1 == -1) {
nextpos = make_pair(tryx2,tryy2);
} else {
nextpos = make_pair(tryx1,tryy1);
}
} else {
nextpos = add1 > add2?make_pair(tryx1,tryy1):make_pair(tryx2,tryy2);
}
}
cout << "纵向检测:" << maxcount << " Point:" << nextpos.first << "," << nextpos.second << endl;
//斜向检测
count = 1;
tryx1 = tryy1 = tryx2 = tryy2 = -1;
add1 = add2 = 0;
for(int i = 1; i <= 5 && x+i < 15 && y+i < 15; ++i) {
if(broad[x+i][y+i] == type) {
++count;
} else {
if(broad[x+i][y+i] == EMPTY) {
tryx1 = x+i;
tryy1 = y+i;
int j = i+i+1,k = y+i+1;
while(j < 15 && broad[j][k] == type) {
++add1;
++j;
++k;
}
}
break;
}
}
if(count >= 5) return true;
for(int i = 1; i<=5 && x-i>=0 && y-i>=0; ++i) {
if(broad[x-i][y-i] == type) {
++count;
} else {
if(broad[x-i][y-i] == EMPTY) {
tryx2 = x-i;
tryy2 = y-i;
int j = x-i-1,k = y-i-1;
while(j >=0 && broad[j][k] == type) {
++add2;
--j;
--k;
}
}
break;
}
}
if(count >= 5) return true;
if(maxcount < count+max(add1,add2)&&(tryx1!=-1||tryx2!=-1)) {
maxcount = count+max(add1,add2);
if(add1 == add2) {
if(tryx1 == -1) {
nextpos = make_pair(tryx2,tryy2);
} else {
nextpos = make_pair(tryx1,tryy1);
}
} else {
nextpos = add1 > add2?make_pair(tryx1,tryy1):make_pair(tryx2,tryy2);
}
}
cout << "斜向检测:" << maxcount << " Point:" << nextpos.first << "," << nextpos.second << endl;
//反斜检测
count = 1;
tryx1 = tryy1 = tryx2 = tryy2 = -1;
add1 = add2 = 0;
for(int i = 1; i <= 5 && x+i < 15 && y-i >= 0; ++i) {
if(broad[x+i][y-i] == type) {
++count;
} else {
if(broad[x+i][y-i] == EMPTY) {
tryx1 = x+i;
tryy1 = y-i;
int j = x+i+1,k = y-i-1;
while(j < 15 && k>=0 && broad[j][k] == type) {
++add1;
++j;
--k;
}
}
break;
}
}
if(count >= 5) return true;
for(int i = 1; i<=5 && x-i>=0 && y+i<15; ++i) {
if(broad[x-i][y+i] == type) {
++count;
} else {
if(broad[x-i][y+i] == EMPTY) {
tryx2 = x-i;
tryy2 = y+i;
int j = x-i-1,k = y+i+1;
while(j >=0 && k<15 && broad[j][k] == type) {
++add2;
--j;
++k;
}
}
break;
}
}
if(count >= 5) return true;
if(maxcount < count+max(add1,add2)&&(tryx1!=-1||tryx2!=-1)) {
maxcount = count+max(add1,add2);
if(add1 == add2) {
if(tryx1 == -1) {
nextpos = make_pair(tryx2,tryy2);
} else {
nextpos = make_pair(tryx1,tryy1);
}
} else {
nextpos = add1 > add2?make_pair(tryx1,tryy1):make_pair(tryx2,tryy2);
}
}
cout << "反斜检测:" << maxcount << " Point:" << nextpos.first << "," << nextpos.second << endl;
return false;
}
void OnPaint(wxPaintEvent&) {
//定义画笔
wxPaintDC dc(this);
//棋盘居中
wxSize size = GetClientSize();
int left = (size.GetWidth()-linelen)/2;
int top = (size.GetHeight()-linelen)/2;
offset = wxPoint(left,top);
for(int i = 0; i < 15; ++i) {
//dc.DrawLine(wxPoint(left,top+cellsize*i),wxPoint(left+linelen,top+cellsize*i));
//dc.DrawLine(wxPoint(left+cellsize*i,top),wxPoint(left+cellsize*i,top+linelen));
dc.DrawLine(wxPoint(0,cellsize*i)+offset,wxPoint(linelen,cellsize*i)+offset);
dc.DrawLine(wxPoint(cellsize*i,0)+offset,wxPoint(cellsize*i,linelen)+offset);
}
for(int i = 0; i < 15; ++i) {
for(int j = 0; j < 15; ++j) {
if(broad[i][j] != EMPTY) DisplayChess(dc,i,j);
}
}
}
void DisplayChess(wxPaintDC& dc,int x,int y) {
dc.SetBrush(broad[x][y] == WHITE? *wxWHITE_BRUSH:*wxBLACK_BRUSH);
//dc.DrawCircle(offset.x+x*cellsize,offset.y+y*cellsize,cellsize/2);
//dc.DrawCircle(wxPoint(x*cellsize,y*cellsize)+offset,cellsize/2);
dc.DrawCircle(wxPoint(x,y)*cellsize+offset,cellsize/2);
}
};
//框架类
//框架类中添加面板类
class GoFrame:public wxFrame {
public:
GoFrame(const char* title):wxFrame(nullptr,wxID_ANY,title) {
wxPanel* p = new GoPanel(this);
SetSize(wxSize(500,500));
}
};
//应用程序类
//应用程序类中添加框架类
class GoApp:public wxApp {
public:
bool OnInit()override {
GoFrame* pframe = new GoFrame("Name");
pframe->Show();
return true;
};
};
wxIMPLEMENT_APP(GoApp);