首先呢,想编写这个程序的同学一定是要下过象棋的(国际的,中国的都可以),就算没下过,也应该看别人下过吧。 好吧..你也没见别人下过,那你至少应该知道象棋长什么样子吧...
首先选择自己的武器,如果你觉得自己够牛的话,那我建议你选择C语言,编译环境为turboC. 然后拥有超强的记忆力记下一大堆的API函数..然后开始动工.. 最后在花上远远多过编写代码所用时间的时间来调试自己的程序(你也就不用再看这篇日志了)
或者说,你觉得自己一般牛B,选择MFC来编写这个程序,虽然说武器是先进了一些,但是依然无法从根本上解决API函数的复杂性。
如果你想用VB6来编写类似于这样的一个Windows程序,可以是可以。但是你永远无法学习到OOP的真正价值所在。VB6不允许程序员在类与类之间建立“is a”的关系. 对于象棋这样的小型程序尚罢..
废话不说了.. 这次的例子我选择了基于.net framew 2.0以上的平台,C#语言来编写这样一个程序。 现在开始动工吧!
首先,你应该明确这是一个Windows Form程序, 而不是一个Console程序..(这一点应该都清楚吧) 所以在这个程序中会使用许多的资源,例如棋盘,棋子图形等等..所以第一步,你需要打开你的Photoshop, 开始绘制这些图片。
棋子的图片如下:
棋盘图片如下:
大家可以根据自己的不同爱好来制作自己的图片,本人一向没有什么审美能力,所以能有这样的图片已经是很尽力了..
准备工作做完后,现在开始编码:
首先打开VS2008 ,新建一个Windows Form Project, 在Projrect中添加第一个类ChessPiece。IDE会自动帮你生成一个ChessPiece.cs文件 和一个Form1.cs文件
现在删除IDE自动在ChessPiece.cs这个文件中的代码,开始自己编写代码:
using System;
using System.Drawing;
// 创建一个棋子类
class ChessPiece
{
public enum Types //这是一个枚举类新,表示所有的棋子种类
{
KING,//国王
QUEEN,//王后
BISHOP,//象
KNIGHT,//马
ROOK,//车
PAWN//卒
}
private int currentType; // 当前类型,用于后台控制当前应该绘制的图像
private Bitmap pieceImage; // 用来储存当前图像当前图像
// 初始化一个矩形区域,这个矩形区域就是以后棋盘的边缘,用于放置整个棋盘和棋盘上的棋子
private Rectangle targetRectangle =
new Rectangle( 0, 0, 75, 75 );
// 构造函数
public ChessPiece( int type, int xLocation,
int yLocation, Bitmap sourceImage )
{
currentType = type; // 设置棋子类型
targetRectangle.X = xLocation; // 设置X坐标
targetRectangle.Y = yLocation; // 设置Y坐标
// 将棋子的子图从大图资源中切割下来
pieceImage = sourceImage.Clone(
new Rectangle( type * 75, 0, 75, 75 ),
System.Drawing.Imaging.PixelFormat.DontCare );
}
// 绘制棋子
public void Draw( Graphics graphicsObject )
{
graphicsObject.DrawImage( pieceImage, targetRectangle );
}
// 获得当前棋子坐标
public Rectangle GetBounds()
{
return targetRectangle;
}
// 设置当前棋子坐标
public void SetLocation( int xLocation, int yLocation )
{
targetRectangle.X = xLocation;
targetRectangle.Y = yLocation;
}
}
好了,这个棋子类已经写完。那应该如何让这些“死”代码活灵活现的呈现在大家面前呢?
我们还需要一个前台呈现类,也就是我们常说的UI类,或者叫Interface。
现在,将刚才在PS中绘制好的图片放置在bin/Release/images目录下
将之前IDE自动生成的Form1.cs改名为ChessGame.cs (不改也可以)
删除IDE自动生成的代码,然后自己编码:
// ChessGame.cs
//该文件实现棋盘和棋子的初始化,部署,和在窗口和面板上的绘制,移动,刷新等工作
using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
public partial class ChessGame : Form
{
private ArrayList chessTile = new ArrayList(); // 创建一个动态数组储存棋盘的图片
private ArrayList chessPieces = new ArrayList(); // 创建一个动态数组储存棋子的图片
private int selectedIndex = -1; // 所选图片的索引
private int[ , ] board = new int[ 8, 8 ]; // 将棋盘用一个 8 * 8 的数组来表示
private const int TILESIZE = 75; // 将每个图片快定义为 75 像素
// 初始化构造函数
public ChessGame()
{
// 对主窗口进行生成
InitializeComponent();
}
// 将棋盘的4种图片快存入动态数组chessTile中并对棋子进行部署
private void ChessGame_Load( object sender, EventArgs e )
{
chessTile.Add( Bitmap.FromFile( @"images\lightTile1.png" ) );
chessTile.Add( Bitmap.FromFile( @"images\lightTile2.png" ) );
chessTile.Add( Bitmap.FromFile( @"images\darkTile1.png" ) );
chessTile.Add( Bitmap.FromFile( @"images\darkTile2.png" ) );
ResetBoard(); //对棋盘上棋子进行部署
Invalidate(); // 刷新主窗口
}
// 具体实现对棋盘的部署
private void ResetBoard()
{
int current = -1;
ChessPiece piece;
Random random = new Random();
bool light = false;
int type;
chessPieces.Clear(); // 确保此时棋子的动态数组为空
// 将白方棋子大图放入临时变量whitePieces中
Bitmap whitePieces =
( Bitmap ) Image.FromFile( @"images\whitePieces.png" );
// 将黑方棋子大图放入临时变量blackPieces中
Bitmap blackPieces =
( Bitmap ) Image.FromFile( @"images\blackPieces.png" );
// 从上到下先部署白色棋子
Bitmap selected = whitePieces;
// 遍历棋盘的行
for ( int row = 0; row <= board.GetUpperBound( 0 ); row++ )
{
// 当到达第 6 行时开始部署黑色棋子
if ( row > 5 )
selected = blackPieces;
// 第二层遍历 遍历棋盘的列
for ( int column = 0;
column <= board.GetUpperBound( 1 ); column++ )
{
// 部署第一行和最后一行上的棋子
if ( row == 0 || row == 7 )
{
switch ( column )
{
case 0:
case 7: // 部署车
current = ( int ) ChessPiece.Types.ROOK;
break;
case 1:
case 6: // 部署马
current = ( int ) ChessPiece.Types.KNIGHT;
break;
case 2:
case 5: // 部署象
current = ( int ) ChessPiece.Types.BISHOP;
break;
case 3: // 部署国王
current = ( int ) ChessPiece.Types.KING;
break;
case 4: // 部署王后
current = ( int ) ChessPiece.Types.QUEEN;
break;
}
// 确定当前棋子的信息
piece = new ChessPiece( current,
column * TILESIZE, row * TILESIZE, selected );
chessPieces.Add( piece ); // 将子图存入动态数组中
} // end if
// 当遍历到第二行和倒数第二行
if ( row == 1 || row == 6 )
{
piece = new ChessPiece(
( int ) ChessPiece.Types.PAWN,
column * TILESIZE, row * TILESIZE, selected );
chessPieces.Add( piece ); // 将卒的子图存入动态数组
}
type = random.Next( 0, 2 ); // 随即决定棋盘的格式
if ( light ) // 设置亮棋盘
{
board[ row, column ] = type;
light = false;
}
else // 设置暗棋盘
{
board[ row, column ] = type + 2;
light = true;
}
}
light = !light; // 让亮色和暗色棋盘交替出现
}
}
// 在主窗口中绘制棋盘
private void ChessGame_Paint( object sender, PaintEventArgs e )
{
Graphics graphicsObject = e.Graphics; // 实例化一个图形对象
graphicsObject.TranslateTransform( 0, 28 ); // 将棋盘整体下沿一定的距离,防止菜单栏挡住棋盘
for ( int row = 0; row <= board.GetUpperBound( 0 ); row++ )
{
for ( int column = 0;
column <= board.GetUpperBound( 1 ); column++)
{
// 绘制棋盘
graphicsObject.DrawImage(
( Image ) chessTile[ board[ row, column ] ],
new Point( TILESIZE * column, ( TILESIZE * row ) ) );
}
}
}
// 绘制棋盘上的棋子
private void pieceBox_Paint(
object sender, System.Windows.Forms.PaintEventArgs e )
{
// 便利整个动态数组
for ( int i = 0; i < chessPieces.Count; i++ )
GetPiece( i ).Draw( e.Graphics );
}
//检测鼠标点击区域是否有棋子
private int CheckBounds( Point point, int exclude )
{
Rectangle rectangle; // current bounding rectangle
for ( int i = 0; i < chessPieces.Count; i++ )
{
// 获得鼠标区域坐标信息
rectangle = GetPiece( i ).GetBounds();
if ( rectangle.Contains( point ) && i != exclude )
return i;
}
return -1;
}
// 鼠标点击左键产生事件
private void pieceBox_MouseDown(
object sender, System.Windows.Forms.MouseEventArgs e )
{
// 获得点击棋子的索引号
selectedIndex = CheckBounds( new Point( e.X, e.Y ), -1 );
}
// 如果选中棋子,移动产生事件
private void pieceBox_MouseMove(
object sender, System.Windows.Forms.MouseEventArgs e )
{
if ( selectedIndex > -1 )
{
Rectangle region = new Rectangle(
e.X - TILESIZE * 2, e.Y - TILESIZE * 2,
TILESIZE * 4, TILESIZE * 4 );
// 将图片中心设置为鼠标所在区域
GetPiece( selectedIndex ).SetLocation(
e.X - TILESIZE / 2, e.Y - TILESIZE / 2 );
pieceBox.Invalidate( region ); // 局部刷新棋子移动后的棋盘
}
}
// 将棋子放下时产生的事件
private void pieceBox_MouseUp( object sender, MouseEventArgs e )
{
int remove = -1;
// 如果棋子被选中
if ( selectedIndex > -1 )
{
Point current = new Point( e.X, e.Y );
//调整棋子放下时的位置
Point newPoint = new Point(
current.X - ( current.X % TILESIZE ),
current.Y - ( current.Y % TILESIZE ) );
// 检查该位置是否具有其他棋子
remove = CheckBounds( current, selectedIndex );
// 将新棋子放在新位置上
GetPiece( selectedIndex ).SetLocation( newPoint.X, newPoint.Y );
selectedIndex = -1; // 将棋子索引还原为-1,为下一次移动做准备
//如果有棋子,将其移除并换上新棋子
if ( remove > -1 )
chessPieces.RemoveAt( remove );
}
pieceBox.Invalidate(); // 刷新棋盘
}
//获得棋子
private ChessPiece GetPiece( int i )
{
return ( ChessPiece ) chessPieces[ i ];
}
//点击New Game重新开始游戏
private void newGameItem_Click(
object sender, System.EventArgs e )
{
ResetBoard(); // 重新部署棋盘
Invalidate(); // 刷新主窗口
}
}
ok!大功告成了~运行结果看看吧:
如果有兴趣,你还可以为自己的象棋添加AI,让电脑自动下棋。 不过这就不属于这个例子的范围之类了。
总之,对于那些刚刚从控制台程序转向Windwos程序的同学来说,初次这个程序可能稍微
有些困难,不过只要你能硬着头皮看下去,认真对待每一行代码,相信对你的编程能力会有提高的。
首先选择自己的武器,如果你觉得自己够牛的话,那我建议你选择C语言,编译环境为turboC. 然后拥有超强的记忆力记下一大堆的API函数..然后开始动工.. 最后在花上远远多过编写代码所用时间的时间来调试自己的程序(你也就不用再看这篇日志了)
或者说,你觉得自己一般牛B,选择MFC来编写这个程序,虽然说武器是先进了一些,但是依然无法从根本上解决API函数的复杂性。
如果你想用VB6来编写类似于这样的一个Windows程序,可以是可以。但是你永远无法学习到OOP的真正价值所在。VB6不允许程序员在类与类之间建立“is a”的关系. 对于象棋这样的小型程序尚罢..
废话不说了.. 这次的例子我选择了基于.net framew 2.0以上的平台,C#语言来编写这样一个程序。 现在开始动工吧!
首先,你应该明确这是一个Windows Form程序, 而不是一个Console程序..(这一点应该都清楚吧) 所以在这个程序中会使用许多的资源,例如棋盘,棋子图形等等..所以第一步,你需要打开你的Photoshop, 开始绘制这些图片。
棋子的图片如下:
棋盘图片如下:
大家可以根据自己的不同爱好来制作自己的图片,本人一向没有什么审美能力,所以能有这样的图片已经是很尽力了..
准备工作做完后,现在开始编码:
首先打开VS2008 ,新建一个Windows Form Project, 在Projrect中添加第一个类ChessPiece。IDE会自动帮你生成一个ChessPiece.cs文件 和一个Form1.cs文件
现在删除IDE自动在ChessPiece.cs这个文件中的代码,开始自己编写代码:
using System;
using System.Drawing;
// 创建一个棋子类
class ChessPiece
{
public enum Types //这是一个枚举类新,表示所有的棋子种类
{
KING,//国王
QUEEN,//王后
BISHOP,//象
KNIGHT,//马
ROOK,//车
PAWN//卒
}
private int currentType; // 当前类型,用于后台控制当前应该绘制的图像
private Bitmap pieceImage; // 用来储存当前图像当前图像
// 初始化一个矩形区域,这个矩形区域就是以后棋盘的边缘,用于放置整个棋盘和棋盘上的棋子
private Rectangle targetRectangle =
new Rectangle( 0, 0, 75, 75 );
// 构造函数
public ChessPiece( int type, int xLocation,
int yLocation, Bitmap sourceImage )
{
currentType = type; // 设置棋子类型
targetRectangle.X = xLocation; // 设置X坐标
targetRectangle.Y = yLocation; // 设置Y坐标
// 将棋子的子图从大图资源中切割下来
pieceImage = sourceImage.Clone(
new Rectangle( type * 75, 0, 75, 75 ),
System.Drawing.Imaging.PixelFormat.DontCare );
}
// 绘制棋子
public void Draw( Graphics graphicsObject )
{
graphicsObject.DrawImage( pieceImage, targetRectangle );
}
// 获得当前棋子坐标
public Rectangle GetBounds()
{
return targetRectangle;
}
// 设置当前棋子坐标
public void SetLocation( int xLocation, int yLocation )
{
targetRectangle.X = xLocation;
targetRectangle.Y = yLocation;
}
}
好了,这个棋子类已经写完。那应该如何让这些“死”代码活灵活现的呈现在大家面前呢?
我们还需要一个前台呈现类,也就是我们常说的UI类,或者叫Interface。
现在,将刚才在PS中绘制好的图片放置在bin/Release/images目录下
将之前IDE自动生成的Form1.cs改名为ChessGame.cs (不改也可以)
删除IDE自动生成的代码,然后自己编码:
// ChessGame.cs
//该文件实现棋盘和棋子的初始化,部署,和在窗口和面板上的绘制,移动,刷新等工作
using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
public partial class ChessGame : Form
{
private ArrayList chessTile = new ArrayList(); // 创建一个动态数组储存棋盘的图片
private ArrayList chessPieces = new ArrayList(); // 创建一个动态数组储存棋子的图片
private int selectedIndex = -1; // 所选图片的索引
private int[ , ] board = new int[ 8, 8 ]; // 将棋盘用一个 8 * 8 的数组来表示
private const int TILESIZE = 75; // 将每个图片快定义为 75 像素
// 初始化构造函数
public ChessGame()
{
// 对主窗口进行生成
InitializeComponent();
}
// 将棋盘的4种图片快存入动态数组chessTile中并对棋子进行部署
private void ChessGame_Load( object sender, EventArgs e )
{
chessTile.Add( Bitmap.FromFile( @"images\lightTile1.png" ) );
chessTile.Add( Bitmap.FromFile( @"images\lightTile2.png" ) );
chessTile.Add( Bitmap.FromFile( @"images\darkTile1.png" ) );
chessTile.Add( Bitmap.FromFile( @"images\darkTile2.png" ) );
ResetBoard(); //对棋盘上棋子进行部署
Invalidate(); // 刷新主窗口
}
// 具体实现对棋盘的部署
private void ResetBoard()
{
int current = -1;
ChessPiece piece;
Random random = new Random();
bool light = false;
int type;
chessPieces.Clear(); // 确保此时棋子的动态数组为空
// 将白方棋子大图放入临时变量whitePieces中
Bitmap whitePieces =
( Bitmap ) Image.FromFile( @"images\whitePieces.png" );
// 将黑方棋子大图放入临时变量blackPieces中
Bitmap blackPieces =
( Bitmap ) Image.FromFile( @"images\blackPieces.png" );
// 从上到下先部署白色棋子
Bitmap selected = whitePieces;
// 遍历棋盘的行
for ( int row = 0; row <= board.GetUpperBound( 0 ); row++ )
{
// 当到达第 6 行时开始部署黑色棋子
if ( row > 5 )
selected = blackPieces;
// 第二层遍历 遍历棋盘的列
for ( int column = 0;
column <= board.GetUpperBound( 1 ); column++ )
{
// 部署第一行和最后一行上的棋子
if ( row == 0 || row == 7 )
{
switch ( column )
{
case 0:
case 7: // 部署车
current = ( int ) ChessPiece.Types.ROOK;
break;
case 1:
case 6: // 部署马
current = ( int ) ChessPiece.Types.KNIGHT;
break;
case 2:
case 5: // 部署象
current = ( int ) ChessPiece.Types.BISHOP;
break;
case 3: // 部署国王
current = ( int ) ChessPiece.Types.KING;
break;
case 4: // 部署王后
current = ( int ) ChessPiece.Types.QUEEN;
break;
}
// 确定当前棋子的信息
piece = new ChessPiece( current,
column * TILESIZE, row * TILESIZE, selected );
chessPieces.Add( piece ); // 将子图存入动态数组中
} // end if
// 当遍历到第二行和倒数第二行
if ( row == 1 || row == 6 )
{
piece = new ChessPiece(
( int ) ChessPiece.Types.PAWN,
column * TILESIZE, row * TILESIZE, selected );
chessPieces.Add( piece ); // 将卒的子图存入动态数组
}
type = random.Next( 0, 2 ); // 随即决定棋盘的格式
if ( light ) // 设置亮棋盘
{
board[ row, column ] = type;
light = false;
}
else // 设置暗棋盘
{
board[ row, column ] = type + 2;
light = true;
}
}
light = !light; // 让亮色和暗色棋盘交替出现
}
}
// 在主窗口中绘制棋盘
private void ChessGame_Paint( object sender, PaintEventArgs e )
{
Graphics graphicsObject = e.Graphics; // 实例化一个图形对象
graphicsObject.TranslateTransform( 0, 28 ); // 将棋盘整体下沿一定的距离,防止菜单栏挡住棋盘
for ( int row = 0; row <= board.GetUpperBound( 0 ); row++ )
{
for ( int column = 0;
column <= board.GetUpperBound( 1 ); column++)
{
// 绘制棋盘
graphicsObject.DrawImage(
( Image ) chessTile[ board[ row, column ] ],
new Point( TILESIZE * column, ( TILESIZE * row ) ) );
}
}
}
// 绘制棋盘上的棋子
private void pieceBox_Paint(
object sender, System.Windows.Forms.PaintEventArgs e )
{
// 便利整个动态数组
for ( int i = 0; i < chessPieces.Count; i++ )
GetPiece( i ).Draw( e.Graphics );
}
//检测鼠标点击区域是否有棋子
private int CheckBounds( Point point, int exclude )
{
Rectangle rectangle; // current bounding rectangle
for ( int i = 0; i < chessPieces.Count; i++ )
{
// 获得鼠标区域坐标信息
rectangle = GetPiece( i ).GetBounds();
if ( rectangle.Contains( point ) && i != exclude )
return i;
}
return -1;
}
// 鼠标点击左键产生事件
private void pieceBox_MouseDown(
object sender, System.Windows.Forms.MouseEventArgs e )
{
// 获得点击棋子的索引号
selectedIndex = CheckBounds( new Point( e.X, e.Y ), -1 );
}
// 如果选中棋子,移动产生事件
private void pieceBox_MouseMove(
object sender, System.Windows.Forms.MouseEventArgs e )
{
if ( selectedIndex > -1 )
{
Rectangle region = new Rectangle(
e.X - TILESIZE * 2, e.Y - TILESIZE * 2,
TILESIZE * 4, TILESIZE * 4 );
// 将图片中心设置为鼠标所在区域
GetPiece( selectedIndex ).SetLocation(
e.X - TILESIZE / 2, e.Y - TILESIZE / 2 );
pieceBox.Invalidate( region ); // 局部刷新棋子移动后的棋盘
}
}
// 将棋子放下时产生的事件
private void pieceBox_MouseUp( object sender, MouseEventArgs e )
{
int remove = -1;
// 如果棋子被选中
if ( selectedIndex > -1 )
{
Point current = new Point( e.X, e.Y );
//调整棋子放下时的位置
Point newPoint = new Point(
current.X - ( current.X % TILESIZE ),
current.Y - ( current.Y % TILESIZE ) );
// 检查该位置是否具有其他棋子
remove = CheckBounds( current, selectedIndex );
// 将新棋子放在新位置上
GetPiece( selectedIndex ).SetLocation( newPoint.X, newPoint.Y );
selectedIndex = -1; // 将棋子索引还原为-1,为下一次移动做准备
//如果有棋子,将其移除并换上新棋子
if ( remove > -1 )
chessPieces.RemoveAt( remove );
}
pieceBox.Invalidate(); // 刷新棋盘
}
//获得棋子
private ChessPiece GetPiece( int i )
{
return ( ChessPiece ) chessPieces[ i ];
}
//点击New Game重新开始游戏
private void newGameItem_Click(
object sender, System.EventArgs e )
{
ResetBoard(); // 重新部署棋盘
Invalidate(); // 刷新主窗口
}
}
ok!大功告成了~运行结果看看吧:
如果有兴趣,你还可以为自己的象棋添加AI,让电脑自动下棋。 不过这就不属于这个例子的范围之类了。
总之,对于那些刚刚从控制台程序转向Windwos程序的同学来说,初次这个程序可能稍微
有些困难,不过只要你能硬着头皮看下去,认真对待每一行代码,相信对你的编程能力会有提高的。