1、 引言
黑白棋, 又叫反棋 (Reversi)、 奥赛罗棋 (Othello)、 苹果棋、 翻转棋。 黑白棋在西方和日本很流行。 黑白棋的棋盘是一个有 8*8 方格的棋盘。 开始时在棋盘正中有两白两黑 4 个棋子交叉放置, 黑棋总是先下子。 游戏通过相互翻转对方的棋子(当自己放下的棋子在横 、 竖 、 斜 8 个方向內有一个自己的棋子, 则被夹在中间的全部翻转会成为自己的棋子), 如果玩家在棋盘上没有地方可以下子, 则该玩家对手可以连下。 最后以棋盘上谁的棋子多来判断胜负。 本文使用 Flash AS3 开发黑白棋游戏程序。 该游戏具有显示执棋方可以落棋子的位置提示功能和判断胜负功能。 在游戏过程中, 点击 “帮助” 按钮则显示执棋方可落子位置 (图片 表示可落子位置)。 游戏运行界面如图 1 所示。
2、 黑白棋游戏设计思路
2.1 棋子和棋盘
游戏开发时, 需要事先准备黑白两色棋子 (如图 2 所示)和棋盘图片, 这里设计棋子影片剪辑使用图 2 中 4 张图, 黑白两色棋子各一帧, 图片 (表示可落子位置) 第 3 帧, 游戏背景方格是第 4 帧。 游戏最初显示时, 棋盘上布满所有 64个棋子影片剪辑 (播放到第 4 帧), 游戏过程中根据需要每个棋子影片剪辑播放不同帧。 棋盘在设计时直接放在 Fla 文件的舞台上。
这里为了便于处理, 采用一个 qizi 二维数组用来存储棋盘上棋子状态, 一个 qipan 一维数组用来存储棋子影片。
2.2 翻转对方的棋子
需要从自己落子 (x1, y1) 为中心的横、 竖、 斜 8 个方向上判断是否需要翻转对方的棋子, 程序中由棋子影片剪辑 qi的 MouseEvent.CLICK 事件实现的。 在 MouseEvent.CLICK 事件中参数 event 对象含有被单击对象信息。 event.target 可以获取被单击棋子影片剪辑对象 thisQi。
剪辑对象 thisQi 位置像素信息 (thisQi.x, thisQi.y) 转换成棋盘坐标 (x1, y1)。 从左, 左上、 上、 右上、 右、 右下、 下、左下 8 个方向上调用过程 DirectReverse (x1, y1, dx, dy) 翻转对方的棋子。 而具体棋子的翻转由 FanQi (x, y) 实现。 FanQi (x,y) 修改数组 qizi 的 (x, y) 处保存棋盘上的棋子信息, 同时播放到指定帧。
private function FanQi(x, y:int):void {
if (qizi[x][y] == BLACK) {
qizi[x][y] = WHITE;
qipan[8 * x + y].gotoAndStop(WHITE);
//显示 WHITE 棋子图形
} else {
qizi[x][y] = BLACK;
qipan[8 * x + y].gotoAndStop(BLACK);
//显示 BLACK 棋子图形
}
2.3 显示执棋方可落子位置
Can_go ( x1, y1) 从左 、 左上 、 上 、 右上 、 右 、 右下 、下、 左下 8 个方向上调用函数 CheckDirect (x1, y1, dx, dy) 判断某方向上是否形成夹击之势, 如果形成且中间无空子则返回True, 表示 (x1, y1) 可以落子。 (x1, y1) 处可以落子则用图片显示。
2.4 判断胜负功能
qizi 二维数组保存棋盘上的棋子信息 , 其中元素保存 1,表示此处为黑子; 元素保存 2, 表示此处为白子; 元素保存 0,表示此处为无棋子。 通过对 qizi 数组中各方棋子数统计, 在棋盘无处可下时, 根据各方棋子数判断出输赢。
3、 设计步骤
3.1 创建 Flash 文件
打开 Flash CS6 软件后, 选择 “文件” → “新建” 选项,系统将弹出 “新建文档” 窗口, 在窗口中选择 “ActionScript3.0” 选项。
3.1.1 设置文档属性
选择菜单 “修改”, 再选择 “文档” 选项, 调出 “文档属性” 对话框。 设置场景的尺寸为 720*780 像素, 背景颜色为浅绿色, 然后单击 “确定” 按钮。 在属性面板设置文档类为Main。
3.1.2 设计棋子影片剪辑元件
选择菜单 “插入” → “新建元件”。 在新弹出的 “新建元件” 窗口中, 将元件名称设置为 “棋子”, 将元件类型设置为“影片剪辑 ”, 单击 “确定 ” 按钮后 , Flash 界面将转变为 “棋子” 元件的编辑区。 导入图 2 所示 4 幅图, 注意每幅图大小80*80。
3.2 设计游戏文档类 (Main.as)
选择 “文件” → “新建” 选项, 系统将弹出 “新建文档”窗口。 在窗口中选择 “ActionScript 文件” 选项。 这样在 Flash中新建一个 ActionScript 类文件, 将其命名为 Main.as。 导入包及相关类:
package {
import flash.display.*;
import flash.events.*;
import flash.text.*;
import flash.utils.Timer;
类成员变量定义:
public class Main extends MovieClip {
//常量
private static const BLACK:int = 1;
private static const WHITE:int = 2;
private static const KONG:int = 0;
private var qizi:Array =new Array();//构造一个二维
//数组用来存储棋子状态
private var qipan:Array =new Array();//构造一个一
//维数组用来存储棋子影片
private var curQizi :int = BLACK;// 当前走棋方
var hitTimer:Timer=new Timer(1000);//计时器,定时
//清除提示图形
构造函数对保存棋盘上的棋子信息的 qizi 数组初始化, 实例化所有棋子影片对象。 同时在棋盘上显示初始的 4 个棋子。
public function Main():void {
//构造函数
var i,j:int;
for (i=0; i<8; i++) {
qizi[i]=new Array();
for (j=0; j<8; j++) {
qizi[i][j]=KONG;
var qi:Qi=new Qi();//棋子实例
qi.y=80*j+42;//确定位置
qi.x=80*i+42;
qi.gotoAndStop(4);//显示棋子图形
qipan.push(qi);
qi.addEventListener(MouseEvent.CLICK,clickQi);
addChild(qi);//加到显示列表
}
}
// 棋盘上初始 4 个棋子
qizi[3][3] = WHITE;
qipan[8 * 3 + 3].gotoAndStop(2);//显示白色棋子图形
qizi[4][4] = WHITE;
qipan[8 * 4 + 4].gotoAndStop(2);//显示白色棋子图形
qizi[3][4] = BLACK;
qipan[8 * 3 + 4].gotoAndStop(1);//显示黑色棋子图形
qizi[4][3] = BLACK;
qipan[8 * 4 + 3].gotoAndStop(1);//显示黑色棋子图形
help_btn.addEventListener (MouseEvent.
CLICK,clickHelp);
message_txt.text="该黑棋走子";
}
构造函数同时对所有的棋子影片对象和 “帮助” 按钮添加鼠标单击事件的侦听。 如果是 help_btn 按钮被单击, 则执行clickHelp 函数显示可以落子的位置提示。
public function clickHelp(event:MouseEvent) {
showCanPosition();//显示可以落子的位置
}
Show_Can_Position () 用图片 显示可以落子的位置 。 同时启动定时器控制图片 仅显示 1 秒钟。
private function showCanPosition():void {
//显示可以落子的位置
var i,j:int;
var n:int = 0;//可以落子的位置统计
for (i = 0; i <= 7; i++) {
for (j = 0; j <= 7; j++) {
if (qizi[i][j] == 0 && Can_go(i, j)) {
n = n + 1;
qipan[8 * i + j].gotoAndStop(3);//显示提示图形
}
}
}
hitTimer.start();
hitTimer.addEventListener (TimerEvent.
TIMER, clsCanPosition);
}
private function clsCanPosition(event:Event) {
var i,j:int;
for (i = 0; i <= 7; i++) {
for (j = 0; j <= 7; j++) {
if (qizi[i][j] == 0 && Can_go(i, j)) {
qipan[8 * i + j].gotoAndStop(4);//显示背景图形
}
}
}
hitTimer.removeEventListener (TimerEvent.
TIMER, clsCanPosition);
}
如果是棋子影片对象 (此时显示是背景第 4 帧) 被单击,则此剪辑对象 thisQi 位置像素信息 (thisQi.x, thisQi.y) 可以转换成棋盘坐标 (x1, y1), 然后判断当前位置 (x1, y1) 是否可以放棋子 (符合夹角之势), 如果可以则此棋子影片对象调用gotoAndStop (curQizi) 显示自己棋子图形, 调用 FanALLQi (i, j)从左、 左上、 上、 右上等 8 个方向翻转对方的棋。 最后判断对方是否有棋可走, 如果对方可以走棋则交换走棋方。 如果对方不可以走棋, 则自己可以继续走棋, 直到双方都不能走棋, 显示输赢信息。
public function clickQi(event:MouseEvent) {
var x1:int, y1:int;
var thisQi:Qi = (event.target as Qi);// what Qi?
x1=(thisQi.x-42)/80;
y1=(thisQi.y-42)/80;
if (Can_go(x1, y1)) {// 判断当前位置是否可以放棋子
trace("can");
qizi[x1][y1] = curQizi;
FanALLQi(x1, y1);// 从 8 个方向翻转对方的棋
qipan[8 * x1 + y1].gotoAndStop(curQizi);//显示棋子图形
//统计棋盘已下棋子数量 n
for (x = 0; x <= 7; x++) {
for (y = 0; y <= 7; y++) {
if (qizi[x][y]! =0)
n = n + 1;
}
}
if(n==64){//棋盘已下满,显示输赢信息
isLoseWin();
return;
}
//判断对方是否有棋可走,如有交换走棋方
if (curQizi==WHITE &&checkNext(BLACK)
||curQizi==BLACK &&checkNext(WHITE)) {
if (curQizi==WHITE) {
curQizi=BLACK;
message_txt.text="该黑棋走子";
} else {
curQizi=WHITE;
message_txt.text="该白棋走子";
}
} else if (checkNext(curQizi)) {
//判断自己是否有棋可走,如有,给出提示
message_txt.text="对方无棋可走,请继续";
} else {//双方都无棋可走,游戏结束,显示输赢信息
isLoseWin();
}//统计双方的棋子数量,显示输赢信息。
} else {
message_txt.text="不能落子! ";
}
}
Can_go (x1, y1) 从左, 左上、 上、 右上、 右、 右下 、 下 、左下 8 个方向判断 (x1, y1) 处可否落子。
private function Can_go(x1, y1:int):Boolean {
//从左、左上、上、右上、右、右下、下、左下 8 个方
//向判断
if (CheckDirect(x1, y1, -1, 0) == true) return true;
if (CheckDirect(x1, y1, -1, -1) == true) return true;
if (CheckDirect(x1, y1, 0, -1) == true) return true;
if (CheckDirect(x1, y1, 1, -1) == true) return true;
if (CheckDirect(x1, y1, 1, 0) == true) return true;
if (CheckDirect(x1, y1, 1, 1) == true) return true;
if (CheckDirect(x1, y1, 0, 1) == true) return true;
if (CheckDirect(x1, y1, -1, 1) == true) return true;
}
checkNext (i:int) 验证参数代表的走棋方是否还有棋可走。
/**
* @param i 代表走棋方,1 为黑方,2 为白方
* @return true/false
*/
private function checkNext(i:int):Boolean {
if ( Can_Num()>0) {
return true;
} else {
return false;
}
}
Can_Num () 统计可以落子的位置数。
private function Can_Num():int {//统计可以落子的
//位置数
var i, j, n = 0;
for (i = 0; i <= 7; i++) {
for (j = 0; j <= 7; j++) {
if (Can_go(i, j)) {
n = n + 1;
}
}
}
return n;//可以落子的位置个数
}
isLoseWin () 统计双方的棋子数量, 显示输赢信息。
// 显示输赢信息
private function isLoseWin() {
var whitenum:int = 0;
var blacknum:int = 0;
var n = 0,x,y:int;
for (x = 0; x <= 7; x++) {
for (y = 0; y <=7; y++) {
if (qizi[x][y] ! = 0) {
n = n + 1;
if (qizi[x][y] == 2) {
whitenum += 1;
}
if (qizi[x][y] == 1) {
blacknum += 1;
}
}
}
}
if (blacknum > whitenum) {
message_txt.text=" 游戏结束黑方胜利,
黑方:" +
String (blacknum)+ " 白方 :" + String
(whitenum);
} else {
message_txt.text=" 游戏结束白方胜利,
黑方:" +
String (blacknum)+ " 白方 :" + String
(whitenum);
}
}
FanALLQi (int x1, int y1) 从左 、 左上 、 上 、 右上 、 右 、右下、 下、 左下 8 个方向翻转对方的棋子。
private function FanALLQi(x1, y1:int):void {
//从左、左上、上、右上、右、右下、下、左下 8 个方
//向翻转
if (CheckDirect(x1, y1, -1, 0) == true) {
DirectReverse(x1, y1, -1, 0);
}
if (CheckDirect(x1, y1, -1, -1) == true) {
DirectReverse(x1, y1, -1, -1);
}
if (CheckDirect(x1, y1, 0, -1) == true) {
DirectReverse(x1, y1, 0, -1);
}
if (CheckDirect(x1, y1, 1, -1) == true) {
DirectReverse(x1, y1, 1, -1);
}
if (CheckDirect(x1, y1, 1, 0) == true) {
DirectReverse(x1, y1, 1, 0);
}
if (CheckDirect(x1, y1, 1, 1) == true) {
DirectReverse(x1, y1, 1, 1);
}
if (CheckDirect(x1, y1, 0, 1) == true) {
DirectReverse(x1, y1, 0, 1);
}
if (CheckDirect(x1, y1, -1, 1) == true) {
DirectReverse(x1, y1, -1, 1);
}
}
CheckDirect () 判断某方向上是否形成夹击之势 , 如果形成且中间无空子则返回 True。
private function CheckDirect (x1, y1, dx, dy:int):
Boolean {
var x, y:int;
var flag:Boolean;
x = x1 + dx;
y = y1 + dy;
flag = false;
while (InBoard(x, y) && ! Ismychess(x, y) &&
qizi[x][y] ! = 0) {
x += dx;
y += dy;
flag = true;//构成夹击之势
}
if (InBoard(x, y) && Ismychess(x, y) && flag == true) {
return true;//该方向落子有效
}
return false;
}
DirectReverse () 针对已形成夹击之势某方向上的对方棋子进行翻转。
private function DirectReverse (x1, y1, dx, dy:int):
void {
var x, y:int;
var flag:Boolean;
x = x1 + dx;
y = y1 + dy;
flag = false;
while (InBoard(x, y) && ! Ismychess(x, y) &&
qizi[x][y] ! = 0) {
x += dx;
y += dy;
flag = true;//构成夹击之势
}
if (InBoard(x, y) && Ismychess(x, y) && flag
== true) {
do {
x -= dx;
y -= dy;
if ((x ! = x1 || y ! = y1)) {
FanQi(x, y);
}
} while ((x ! = x1 || y ! = y1));
}
}
FanQi (int x, int y) 将存储 (x, y) 处棋子信息 qizi [x] [y]
的反色处理。
private function FanQi(x, y:int):void {
if (qizi[x][y] == BLACK) {
qizi[x][y] = WHITE;
qipan[8 * x + y].gotoAndStop(WHITE);
//显示棋子图形
} else {
qizi[x][y] = BLACK;
qipan[8 * x + y].gotoAndStop(BLACK);
//显示棋子图形
}
}
InBoard () 判断 (x,y) 是否在棋盘界内 , 如果在界内则返回真, 否则返回假。
private function InBoard(x,y :int):Boolean {
if (x >= 0 && x <= 7 && y >= 0 && y <= 7) {
return true;
} else {
return false;
}
}
至此就完成黑白棋游戏设计了, 运行程序效果如图 1 所示。
4、 结语
用 Flash ActionScript3.0 实现经典的黑白棋游戏基本功能,并且能够判断输赢, 如果用户可以进一步改进翻转效果和增加双方棋子数量提示, 则使得游戏更具吸引力。
第5章总结随着计算机的普及和网络技术的发展,人们获取信息主要途径以及从书籍、期刊等传统的方式转向从互联网中获...
第一章绪论1.1课题的背景及意义。随着计算机技术的发展,计算机应用逐渐由大规模科学计算转向以数据库管理系统为开发环境,对大规模的事务处理和对工作流管理的管理信息系统[1-2].计算机已经渗入到了我们日常生活、工作和学习的方方面面,同时也包括了...
第五章功能流程及实现5.1系统功能流程图5.1.1简历模块程序流程图个人用户,即应聘者成功登录后,进入个人用户主界面,单击个人信息设置按钮并判断是否添加个人信息,如果添加则返回个人用户的主界面反之进入添加页面。单击创建个人简历按钮并同时判断...
第4章系统的设计与实现4.1数据库整体设计与E-R图数据库设计是结合收集到的实际情况和数据,在编制数据库系统时,根据设计数据库的原则和编写、计数据库的原则,在符合系统平台的功能性需求和数据算法符合当前所设定的数据库编写、传输的过程。数据库...
高校领导主要是对所有人员信息的查看,部门主任主要是对于部门人员信息的查看和该部门请假的添加、修改;而普通教职工是查看自己的个人信息。系统采用的JAVA语言,编码工具为My Eclipse,数据库管理系统为Oracle,Web服务器为Tomcat。...
与传统书店相比,网上书店拥有许多的优势。网上书店的建立可以大大减少图书销售中的中间环节,节省大量的人力、物力,并且能够提供更多的书目信息。...
传统的记载方式将数据进行统计对于一个较大的公司或者企业甚至只是一个大型超市来说都是一个庞大的工程,而这个进销存系统无论是友好性还是功能性都是毋庸置疑的,同时他的出错率极低安全性较高,可见这个系统有着极大的可推广和可行性。...
引言目前,由于很多高校对实验室设备管理的自动化水平不高,许多设备的管理方法都是采用Excel表格形式进行管理,这样当数据量越来越大时,不但给管理员管理数据带来很大的工作量,还会给数据的准确性及安全性带来问题,并且在很多情况下,设备信息存档之后...
总结与展望1.总结。本课题结合企业工程项目管理业务,对基于Web的项目管理系统做出了总体设计和实施。课题从调研企业工程项目管理业务入手,对用户的业务管理流程进行了梳理与需求分析,在此基础上,依据功能、性能与安全性等需求,进行了系统的总体设计...
新唐山电视播控中心的投入使用,顺应了数字电视的普及浪潮。我们根据系统的使用反馈,从安全性、实用性等多方面考虑,对系统进行了多次合理化改造升级。提高了工作效率的同时,电视节目质量也有了质的飞跃。...