简单五子棋,没有电脑AI
刚学了C#委托,做了个五子棋练习,把前台绘制和后台逻辑分开,前台绘制方法用委托传给后台逻辑。
界面好简单。。。
先看类图

控制类控制整个游戏的逻辑,包括调用棋盘类的属性初始化棋盘、初始化两个棋手、轮流落子。棋盘里有一个二维数组保存整个棋盘的落子情况,棋手里也有一个二维数组保存自己的落子情况。方向类是为了方便判断输赢的。
下面是代码:注释很详细就不说明了:
主要控制类:
using System;
using System.Collections.Generic;
using System.Drawing;
using 五子棋.Properties; namespace 五子棋.Control
{
class Controler
{
#region 字段
///// <summary>
///// 画家对象
///// </summary>
//Graphics g;
/// <summary>
/// 棋盘对象
/// </summary>
QiPan qipan;
/// <summary>
/// 棋手列表
/// </summary>
List<QiShou> qishou;
/// <summary>
/// 游戏是否结束
/// </summary>
bool Gameover = false;
/// <summary>
/// 绘制直线委托
/// </summary>
Action<Point, Point> actionDrawLine;
/// <summary>
/// 绘制棋子图像委托
/// </summary>
Action<Image, int, int, float> actionDrawimage;
/// <summary>
/// 取得胜利事件,返回胜利选手的名称
/// </summary>
Action<string> actionWin;
#endregion #region 构造函数 /// <summary>
/// 构造函数
/// </summary>
/// <param name="actionDrawLine">绘制直线委托</param>
/// <param name="actionDrawimage">绘制棋子图像委托</param>
public Controler(Action< Point, Point> actionDrawLine, Action<Image, int, int, float> actionDrawimage,Action<string> actionWin)
{
//开始游戏
this.actionDrawLine = actionDrawLine;
this.actionDrawimage = actionDrawimage;
this.actionWin = actionWin;
StartGame();
}
#endregion #region 方法 /// <summary>
/// 棋手轮流下棋
/// </summary>
/// <param name="x">棋子X坐标</param>
/// <param name="y">棋子Y坐标</param>
public void LuoZi(int x, int y)
{
if (Gameover)
{
return;
}
//把不在棋盘交点上的坐标换算到最接近的棋盘交点上 //x = (int)Math.Round((double)(x - qipan.start.X) / qipan.Grid)
// * qipan.Grid + qipan.start.X;
//y = (int)Math.Round((double)(y - qipan.start.Y) / qipan.Grid)
// * qipan.Grid + qipan.start.Y;
//换算到棋盘第几条线
int qipanX = (int)Math.Round((double)(x - qipan.start.X) / qipan.Grid);
int qipanY = (int)Math.Round((double)(y - qipan.start.Y) / qipan.Grid);
if (qipan[qipanX, qipanY] == )
{
for (int i = ; i < qishou.Count; i++)
{
if (qishou[i].IsZouqi)
{
qipan[qipanX, qipanY] = ;
qishou[i].LuoZi(qipanX, qipanY);
//换算到棋盘控件坐标并绘制棋子
qishou[i].Render(qipanX * qipan.Grid + qipan.start.X,
qipanY * qipan.Grid + qipan.start.Y, actionDrawimage); //判断当前玩家是否获胜,获胜则游戏结束
Gameover = IsWin(qishou[i], new Point(qipanX, qipanY));
if (Gameover)
{
//if (actionWin!=null)
//{
// actionWin.Invoke(qishou[i].PlayerName);
//}
actionWin?.Invoke(qishou[i].PlayerName);
} //走棋后设置棋手不可走棋
//qishou[i].LuoZi(x, y, g);
}
qishou[i].IsZouqi = !qishou[i].IsZouqi;
}
}
}
/// <summary>
/// 刷新界面
/// </summary>
public void Render()
{
qipan.Render(actionDrawLine);
for (int i = ; i < qishou.Count; i++)
{
qishou[i].Render(qipan.start, qipan.Grid, actionDrawimage);
}
}
/// <summary>
/// 判断是否获胜
/// </summary>
/// <param name="qishou">棋手</param>
/// <param name="p">当前棋子坐标</param>
/// <returns></returns>
public bool IsWin(QiShou qishou, Point p)
{ //如果点在连续直线上就累加,当sum=5时,表示连续5个棋子在一条直线上
int sum = ;
for (int i = ; i < ; i++)
{
Console.WriteLine(i);
//下一个要检查的点
Point nextP = p;
Direction dr = (Direction)i;
if (i % == )
{
sum = ;
}
for (int j = ; j < ; j++)
{
//根据当前方向判断设置下一个点
#region switch
switch (dr)
{
case Direction.Top:
nextP.X = nextP.X;
nextP.Y = nextP.Y - ;
break;
case Direction.RightTop:
nextP.X = nextP.X + ;
nextP.Y = nextP.Y - ;
break;
case Direction.Rigth:
nextP.X = nextP.X + ;
nextP.Y = nextP.Y;
break;
case Direction.RigthBotton:
nextP.X = nextP.X + ;
nextP.Y = nextP.Y + ;
break;
case Direction.Botton:
nextP.X = nextP.X;
nextP.Y = nextP.Y + ;
break;
case Direction.LeftBotton:
nextP.X = nextP.X - ;
nextP.Y = nextP.Y + ;
break;
case Direction.Left:
nextP.X = nextP.X - ;
nextP.Y = nextP.Y;
break;
case Direction.LeftTop:
nextP.X = nextP.X - ;
nextP.Y = nextP.Y - ;
break;
default:
break;
}
#endregion if (nextP.X >= && nextP.X < qishou.ZouQiQiPan.GetLength()
&& nextP.Y >= && nextP.Y < qishou.ZouQiQiPan.GetLength())
{
if (qishou.ZouQiQiPan[nextP.X, nextP.Y] == )
{
break;
}
else
{
sum += qishou.ZouQiQiPan[nextP.X, nextP.Y];
}
}
else
{
break;
}
}
if (sum == )
{
return true;
} } return false;
}
/// <summary>
/// 开始游戏
/// </summary>
public void StartGame()
{
Gameover = false;
//初始化棋盘
qipan = new QiPan();
//初始化两种棋手:白棋和黑棋
qishou = new List<QiShou>()
{
new QiShou(Resources.白棋,0.08f,new byte[qipan.Heigth/qipan.Grid+,qipan.Width/qipan.Grid+]) {IsZouqi=true,PlayerName="Bai" }, new QiShou(Resources.黑棋,0.08f,new byte[qipan.Heigth/qipan.Grid+,qipan.Width/qipan.Grid+]) { IsZouqi=false,PlayerName="Hei" }
};
Render();
}
#endregion }
} 棋手类:
using System;
using System.Drawing; namespace 五子棋.Control
{
class QiShou
{
#region 字段
/// <summary>
/// 棋子图像
/// </summary>
/// /// <summary>
/// 走棋棋盘
/// </summary>
byte[,] zouqiqipan;
#endregion
#region 属性
public string PlayerName { get; set; }
public Image ImagePlayer { get; set; }
/// <summary>
/// 棋子图像缩放比例
/// </summary>
public float ImageScale { get; set; } /// <summary>
/// 走棋棋盘
/// </summary>
public byte[,] ZouQiQiPan
{
get
{
return zouqiqipan;
}
set
{
zouqiqipan = value;
}
}
/// <summary>
/// 是否可以走棋
/// </summary>
public bool IsZouqi { get; set; }
#endregion
/// <summary>
/// 构造函数
/// </summary>
/// <param name="image">棋子图像</param>
/// <param name="imagescale">棋子缩放比例</param>
/// /// <param name="zouqiqipan">走棋棋盘大小</param>
public QiShou(Image image, float imagescale, byte[,] zouqiqipan)
{
this.zouqiqipan = zouqiqipan;
ImagePlayer = image;
ImageScale = imagescale;
Init();
}
/// <summary>
/// 初始化棋手
/// </summary>
private void Init()
{
for (int i = ; i < zouqiqipan.GetLength(); i++)
{
for (int j = ; j < zouqiqipan.GetLength(); j++)
{
zouqiqipan[i, j] = ;
}
}
} /// <summary>
/// 下棋落字
/// </summary>
/// <param name="x">棋盘X网格</param>
/// <param name="y">棋盘Y网格</param>
public void LuoZi(int x, int y)
{
ZouQiQiPan[x, y] = ;
}
#region 绘制棋子
/// <summary>
/// 绘制所有棋子
/// </summary>
/// <param name="start"></param>
/// <param name="grid"></param>
public void Render(Point start, int grid,Action<Image, int,int,float> actionDrawImage)
{
for (int i = ; i < ZouQiQiPan.GetLength(); i++)
{
for (int j = ; j < ZouQiQiPan.GetLength(); j++)
{
if (zouqiqipan[i, j] == )
{
Render(i * grid + start.X, i * grid + start.Y,actionDrawImage); }
}
}
}
public void Render(int x,int y, Action<Image, int, int, float> actionDrawImage)
{
actionDrawImage?.Invoke(ImagePlayer, x, y, ImageScale);
}
///// <summary>
///// 绘制棋子
///// </summary>
///// <param name="x">棋盘X网格坐标</param>
///// <param name="y">棋盘Y网格坐标</param>
///// <param name="g">画家对象</param>
//public void Render(int x, int y)
//{
// //actionDrawimage(x, y, ImagePlayer, ImageScale); // //绘制棋子,绘制的坐标应该换算到中心
// g.DrawImage(ImagePlayer, x - ImagePlayer.Width * ImageScale / 2, y - ImagePlayer.Height * ImageScale / 2,
// ImagePlayer.Width * ImageScale, ImagePlayer.Height * ImageScale);
//}
#endregion } }
棋盘类:
using System;
using System.Drawing; namespace 五子棋.Control
{
class QiPan
{ /// <summary>
/// 棋盘绘制起点坐标
/// </summary>
public readonly Point start;
/// <summary>
/// X缩放
/// </summary>
public int ScaleX { get; set; }
/// <summary>
/// Y缩放
/// </summary>
public int ScaleY { get; set; }
/// <summary>
/// 棋盘宽度
/// </summary>
public int Width { get; set; }
/// <summary>
/// 棋盘高度
/// </summary>
public int Heigth { get; set; }
/// <summary>
/// 网格大小
/// </summary>
public int Grid { get; set; }
/// <summary>
/// 棋盘棋子
/// </summary>
private byte[,] qipanQiZi;
public byte this[int x, int y]
{
get
{
if (x < qipanQiZi.GetLength() && y < qipanQiZi.GetLength())
{
return qipanQiZi[x, y];
}
else
{
return ;
}
}
set
{
if (x < qipanQiZi.GetLength() && y < qipanQiZi.GetLength())
{
qipanQiZi[x, y] = value;
}
}
} /// <summary>
/// 构造函数
/// </summary>
/// <param name="width">棋盘宽度</param>
/// <param name="height">棋盘高度</param>
/// <param name="grid">棋盘网格大小</param>
public QiPan(Point start, int width, int height, int grid)
{
this.start = start;
Width = width;
Heigth = height;
Grid = grid;
Init(); } private void Init()
{
qipanQiZi = new byte[Width / Grid + , Heigth / Grid + ];
for (int i = ; i < qipanQiZi.GetLength(); i++)
{
for (int j = ; j < qipanQiZi.GetLength(); j++)
{
qipanQiZi[i, j] = ;
}
}
} /// <summary>
/// 构造函数
/// 默认棋盘大小为400*400,方格大小为40
/// </summary>
public QiPan() : this(new Point(, ), , , )
{
}
#region Graphics绘制棋盘 /// <summary>
/// 绘制棋盘
/// </summary>
/// <param name="g"></param>
//public void Render(Graphics g)
//{
// for (int i = 0; i <= Width / Grid; i++)
// {
// g.DrawLine(Pens.Black, start.X + (Grid * i), start.Y, start.X + (Grid * i), start.Y + Heigth); // }
// for (int i = 0; i <= Heigth / Grid; i++)
// {
// g.DrawLine(Pens.Black, start.X, start.Y + (Grid * i), start.X + Width, start.Y + (Grid * i));
// }
//}
#endregion
public void Render(Action< Point, Point> actionDrawLine)
{
int row = Heigth / Grid + ;
int cel = Width / Grid + ;
for (int i = ; i < row; i++)
{
actionDrawLine?.Invoke( new Point(start.X, start.Y + Grid * i), new Point(start.X + Width, start.Y + Grid * i));
}
for (int i = ; i < cel; i++)
{
actionDrawLine?.Invoke( new Point(start.X + Grid * i, start.Y), new Point(start.X + Grid * i, start.Y + Heigth));
}
}
}
}
简单五子棋,没有电脑AI的更多相关文章
- 五子棋(无AI winform gdi+)
之前无意间在博客园看到一篇用深度学习玩马里奥的文章,于是就想做这个小东西来测试人工智能算法(准备用PYTHON的库,对神经网络的梦已经做了好多年了,但是太难了,一直懒得动它),本来是想用WPF做UI, ...
- [收藏]C++简单五子棋
#include<iostream> #include<iomanip> using namespace std; ; //棋盘行数 ; //棋盘列数 char p[X][Y] ...
- C++的简单“五子棋”游戏,只是核心代码,资源代码未添加
ChessBoard.h #ifndef __CHESS_BOARD_H__ #define __CHESS_BOARD_H__ #include "DataStruct.h" # ...
- 用Java写的简单五子棋游戏(原创五子连珠算法)
源码jar包(已安装jdk环境可直接运行) 下载地址:http://download.csdn.net/detail/eguid_1/9532912 五子连珠算法为自创算法,对于五子棋该算法性能足以. ...
- 37行代码实现一个简单的打游戏AI
不废话,直接上码,跟神经网络一点关系都没有,这37行代码只能保证电脑的对敌牺牲率是1:10左右,如果想手动操控,注释掉autopilot后边的代码即可. 哪个大神有兴趣可以用tensorflow或者s ...
- js+canvas五子棋人机大战ai算法
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- C++五子棋(五)——实现AI落子
AI思考落子点 在之前我们已经实现计算权值了,现在要想让AI落子,应根据之前的计算结果使棋子落在分值最大点上.当然可能会出现多个分值相同的最大点,这时在其中随机取一个点落下即可. chessData. ...
- Leo-io 的C语言实现简单五子棋游戏观后感
源代码: /************************************************************** ** 文 件 名:wuziqi.cpp ** 功 能:扫 ...
- C++ 之 简单的五子棋AI程序
本人是大一新生,寒假无聊,抱着试试看的心态(没有想到可以完成),写了C++的简单五子棋程序,开心. 下面是效果图: 一.首先讲讲大致思路. 五子棋实现的基础: ...
随机推荐
- [AndroidTips]startService与bindService的区别
Service的生命周期方法比Activity少一些,只有onCreate, onStart, onDestroy我们有两种方式启动一个Service,他们对Service生命周期的影响是不一样的. ...
- TFS发布计划发送到钉钉消息群
由于工作中需要用到钉钉,每天都要和钉钉打交道:上下班打卡.出差请假流程.各种工作讨论组,不一而足,工作已然和钉钉绑在了一起,难怪有广告词: 微信是一个生活方式,钉钉是一个工作方式. 我们是钉钉机器人内 ...
- Linux之例行(任务调度)
一. 例行命令之at 1.1 at 仅执行一次就从Linux任务中取消 1.2 at 指令可以将工作命令写入工作记录文件,工作记录文件默认存放在/var/spool/at目录内 1.3 at 工作 ...
- java做单用户的多重并发会话数限制
判定条件很简单,就是在同一时刻,同一帐号仅在一个终端上可正常操作. 我这里用简单的key,value进行判定,将用户存储在map里面,新登录用户登陆进系统后,判断map里是否存在当前用户,若存在就删除 ...
- css3动画图片波纹效果
这里的图片很有特点,下面有演示图片样式 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &quo ...
- CentOS 7 安装Broadcom无线网卡驱动
重新坑了小伙伴一台电脑,用来装centOS练习和做服务器用,哈哈哈 装了了CentOS 7后发现无线网卡读不出来,没有装驱动,网卡是Broadcom ╮(╯_╰)╭ 1.首先确定网卡是否为坑爹类型Br ...
- C#中string和byte[]相互转换问题解决
本来想讲string转换为byte数组,通过在VS上打 ‘str. “来找,结果半天没发现跳出来的函数中有想要的,哭瞎 /(ㄒoㄒ)/~~ 这回将两种情况都记下来了.... string ---> ...
- apache的配置参数
#ErrorDocument 500 "The server made a boo boo."#ErrorDocument 404 /missing.html 1.Document ...
- Struts2基础学习(六)—文件的上传和下载
一.文件的上传 1.单个文件上传 Struts2使用拦截器完成了文件的上传,而且底层使用的也是FileUpload开源组件. 客户端注意事项: (1)method="post&qu ...
- struts2 之 struts2类型转换
1. 在struts2中,相比servlet来时,获取数据时,程序员没有进行手动的类型转换,类型转换工作都有struts2来完成处理,但愿对于自定义类型数据,struts2不会帮助我们完成类型转换工作 ...