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 實現經典的黑白棋游戲基本功能,并且能夠判斷輸贏, 如果用戶可以進一步改進翻轉效果和增加雙方棋子數量提示, 則使得游戲更具吸引力。