下五子棋时要把握进攻的时机,以攻势占据主导权。而防守时也不能一位防守,要同时为自己创造更好的局面一箭双雕。最后落子时要对对手的棋路进行预判。
一、进攻
在下五子棋时进攻是非常重要的,如果过度防守就会造成被对手牵着鼻子走的情况。所以在适当的情况下,尤其是对方进攻不积极的时候就要握住主导权展开攻势。
二、防守
防守时不能只以堵住对手的路为先,最好是能够在堵住对手的进攻的同时,也为自己创造有有利的局面。
三、预判
在落子时不能只看当前的棋局,还需要着眼未来,在下每一步棋时都可以预想对手下一步的动作,这样可以增大获胜的几率。
C Qt项目实战教程:五子棋游戏
1,简介
Qt开发的AI人机对战五子棋游戏。
2,效果3,思路棋盘为15*15矩阵
棋子item 存1个坐标点、一个颜色类型(黑棋还是白棋)
绘制顺序依次为 棋盘、棋子、鼠标(也是一个棋子)
核心算法(判断五子连):
对所下的棋子,向 8个方向分别统计相邻的同色棋子个数
8个方向为:左、左上、上、右上、右、右下、下、左下
然后在一条直线的2个方向的棋子个数加起来,即得到该直线上与所下棋子相邻的同色棋子个数
棋子类Item.h:
包含一个QPoint圆心坐标,和一个bool变量,代表是黑方还是白方
#pragma once#include <QPoint> class Item{public: Item(void); Item(QPoint pt,bool bblack); ~Item(void); //重载"=="操作符,判等需要颜色和位置都相同 bool operator==(const Item &t1)const { return ((mPt == t1.mPt) && (mBlack == t1.mBlack)); } QPoint mPt; bool mBlack;private: };
MainWindow.h:
QVector<Item> mItems 保存所有棋子。
#ifndef MAINWINDOW_H#define MAINWINDOW_H #include <QMainWindow>#include "Item.h"#include "qmap.h" namespace Ui {class MainWindow;} #define CHESS_ROWS 15#define CHESS_COLUMES 15#define RECT_WIDTH 50#define RECT_HEIGHT 50 class MainWindow : public QMainWindow{ Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); protected: void paintEvent(QPaintEvent *); void mousePressEvent(QMouseEvent *); private: void DrawChessboard(); void DrawItems(); void DrawItemWithMouse(); void DrawChessAtPoint(QPainter& painter,QPoint& pt); //统计某个方向(共8个方向)上的相连个数,用QPoint表示统计方向,如(1,1)表示右下方,(-1,0)表示向左 int CountNearItem(Item item,QPoint ptDirection); private: Ui::MainWindow *ui; QVector<Item> mItems; bool mIsBlackTurn; //当前该黑棋下}; #endif // MAINWINDOW_H
MainWindow.cpp:
#include "mainwindow.h"#include "ui_mainwindow.h"#include "qpainter.h"#include "qevent.h"#include "qpoint.h"#include "qmessagebox.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow){ ui->setupUi(this); ui->mainToolBar->hide(); ui->menuBar->hide(); resize((CHESS_COLUMES 1)*RECT_WIDTH ,(CHESS_ROWS 1)*RECT_HEIGHT); mIsBlackTurn = true; } MainWindow::~MainWindow(){ delete ui;} void MainWindow::paintEvent(QPaintEvent *e){ DrawChessboard(); //画棋盘 DrawItems(); //画棋子 DrawItemWithMouse(); //画鼠标(当前方的棋子形状) update();} void MainWindow::DrawChessboard(){ QPainter painter(this); painter.setRenderHint(QPainter::HighQualityAntialiasing, true); painter.setBrush(Qt::darkYellow); painter.setPen(QPen(QColor(Qt::black),2)); for(int i = 0;i<CHESS_COLUMES; i ) { for (int j = 0; j<CHESS_ROWS; j ) { painter.drawRect( (i 0.5)*RECT_WIDTH,(j 0.5)*RECT_HEIGHT,RECT_WIDTH,RECT_HEIGHT); } }} void MainWindow::DrawItems(){ QPainter painter(this); painter.setPen(QPen(QColor(Qt::transparent))); for (int i = 0; i<mItems.size(); i ) { Item item = mItems[i]; if (item.mBlack) { painter.setBrush(Qt::black); } else { painter.setBrush(Qt::white); } DrawChessAtPoint(painter,item.mPt); }} void MainWindow::DrawChessAtPoint(QPainter& painter,QPoint& pt){ //painter.drawRect( (pt.x() 0.5)*RECT_WIDTH,(pt.y() 0.5)*RECT_HEIGHT,RECT_WIDTH,RECT_HEIGHT); QPoint ptCenter((pt.x() 0.5)*RECT_WIDTH,(pt.y() 0.5)*RECT_HEIGHT); painter.drawEllipse(ptCenter,RECT_WIDTH / 2,RECT_HEIGHT / 2);} void MainWindow::DrawItemWithMouse(){ QPainter painter(this); painter.setPen(QPen(QColor(Qt::transparent))); if (mIsBlackTurn) { painter.setBrush(Qt::black); } else { painter.setBrush(Qt::white); } //QPoint pt; //pt.setX( (QCursor::pos().x() ) / RECT_WIDTH); //pt.setY( (QCursor::pos().y() ) / RECT_HEIGHT); //DrawChessAtPoint(painter,pt); painter.drawEllipse(mapFromGlobal(QCursor::pos()),RECT_WIDTH / 2,RECT_HEIGHT / 2); } void MainWindow::mousePressEvent(QMouseEvent * e){ //求鼠标点击处的棋子点pt QPoint pt; pt.setX( (e->pos().x() ) / RECT_WIDTH); pt.setY( (e->pos().y() ) / RECT_HEIGHT); //如果已存在棋子,就什么也不做 for (int i = 0; i<mItems.size(); i ) { Item item = mItems[i]; if (item.mPt == pt) { //已有棋子 return; } } //不存在棋子,就下一个 Item item(pt,mIsBlackTurn); mItems.append(item); //统计4个方向是否五子连 int nLeft = CountNearItem(item,QPoint(-1,0)); int nLeftUp = CountNearItem(item,QPoint(-1,-1)); int nUp = CountNearItem(item,QPoint(0,-1)); int nRightUp = CountNearItem(item,QPoint(1,-1)); int nRight = CountNearItem(item,QPoint(1,0)); int nRightDown = CountNearItem(item,QPoint(1,1)); int nDown = CountNearItem(item,QPoint(0,1)); int nLeftDown = CountNearItem(item,QPoint(-1,1)); if ( (nLeft nRight) >= 4 || (nLeftUp nRightDown) >= 4 || (nUp nDown) >= 4 || (nRightUp nLeftDown) >= 4 ) { QString str = mIsBlackTurn?"Black":"White"; QMessageBox::information(NULL, "GAME OVER",str, QMessageBox::Yes , QMessageBox::Yes); mItems.clear(); //NewGame(); return; } //该另一方下棋了 mIsBlackTurn = !mIsBlackTurn;} int MainWindow::CountNearItem(Item item,QPoint ptDirection){ int nCount = 0; item.mPt = ptDirection; while (mItems.contains(item)) { nCount ; item.mPt = ptDirection; } return nCount;}
判断五子连的算法:
(统计某个棋子item在某个方向ptDirection上,相邻的同色棋子数目)
方向用QPoint来表示,是取8个方向上1个单位坐标的点,例如:向上(0,1)、向右上(1,1)、向右(1,0)等
int MainWindow::CountNearItem(Item item,QPoint ptDirection){ int nCount = 0; item.mPt = ptDirection; while (mItems.contains(item)) { nCount ; item.mPt = ptDirection; } return nCount;}
这样在每次下棋后,判断所下棋子横、竖、正斜、反斜4大方向上,每个方向相邻同色棋子数目,达到5个即胜利。
这里的4个方向,每个方向需要调用CountNearItem函数两次,比如横向,需要分别以向右(1,0)和向左(-1,0)来调
CountNearItem函数。