子棋的棋盤Android版實現,具體上下文可以參考上一篇的五子棋AI實現中的代碼
[java]
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
//棋盤
public class Chessboard extends View implements IChessboard{
//游戲狀態常量:
//已准備好,可開局
private static final int READY = 1;
//已開局
private static final int RUNNING = 2;
//已結束
private static final int PLAYER_TWO_LOST = 3;
private static final int PLAYER_ONE_LOST = 4;
//當前狀態,默認為可開局狀態
private int currentMode = READY;
//畫筆對象
private final Paint paint = new Paint();
//代表綠色
private static final int GREEN = 0;
private static final int NEW_GREEN = 1;
//紅色
private static final int RED = 2;
//黃色
private static final int NEW_RED = 3;
//點大小
private static int pointSize = 20;
//用於提示輸贏的文本控件
private TextView textView = null;
//不同顏色的Bigmap數組
private Bitmap[] pointArray = new Bitmap[4];
//屏幕右下角的坐標值,即最大坐標值
private static int maxX;
private static int maxY;
//第一點偏離左上角從像數,為了棋盤居中
private static int yOffset;
private static int xOffset;
//兩個玩家
//第一個玩家默認為人類玩家
private IPlayer player1 = new HumanPlayer();
//第二個則根據選擇人機戰還是對戰模式來初始化
private IPlayer player2;
//預先初始兩個第二玩家
//電腦玩家
private static IPlayer computer = AiFactory.getInstance(2);
//人類玩家
private static final IPlayer human = new HumanPlayer();
// 所有未下的空白點
private final List<Point> allFreePoints = new ArrayList<Point>();
public Chessboard(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusable(true);
//把三個顏色的點准備好,並放入數組
Resources r = this.getContext().getResources();
fillPointArrays(GREEN,r.getDrawable(R.drawable.green_point));
fillPointArrays(NEW_GREEN,r.getDrawable(R.drawable.new_green_point));
fillPointArrays(RED,r.getDrawable(R.drawable.red_point));
fillPointArrays(NEW_RED,r.getDrawable(R.drawable.new_red_point));
//設置畫線時用的顏色
paint.setColor(Color.LTGRAY);
}
//初始橫線和豎線的數目
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
maxX = (int) Math.floor(w / pointSize);
maxY = (int) Math.floor(h / pointSize);
//設置X、Y座標微調值,目的整個框居中
xOffset = ((w - (pointSize * maxX)) / 2);
yOffset = ((h - (pointSize * maxY)) / 2);
//創建棋盤上的線條
createLines();
//初始化棋盤上所有空白點
createPoints();
}
//產生棋盤上所有的線
private void createLines(){
for (int i = 0; i <= maxX; i++) {//豎線
lines.add(new Line(xOffset+i*pointSize-pointSize/2, yOffset, xOffset+i*pointSize-pointSize/2, yOffset+maxY*pointSize));
}
for (int i = 0; i <= maxY; i++) {//橫線
lines.add(new Line(xOffset, yOffset+i*pointSize-pointSize/2, xOffset+maxX*pointSize, yOffset+i*pointSize-pointSize/2));
}
}
//畫棋盤
private List<Line> lines = new ArrayList<Line>();
private void drawChssboardLines(Canvas canvas){
for (Line line : lines) {
canvas.drawLine(line.xStart, line.yStart, line.xStop, line.yStop, paint);
}
}
//線類
class Line{
float xStart,yStart,xStop,yStop;
public Line(float xStart, float yStart, float xStop, float yStop) {
this.xStart = xStart;
this.yStart = yStart;
this.xStop = xStop;
this.yStop = yStop;
}
}
//畫點
private void drawPoint(Canvas canvas,Point p,int color){
canvas.drawBitmap(pointArray[color],p.x*pointSize+xOffset,p.y*pointSize+yOffset,paint);
}
//設置運行狀態
public void setMode(int newMode) {
currentMode = newMode;
if(currentMode==PLAYER_TWO_LOST){
//提示玩家2輸了
textView.setText(R.string.player_two_lost);
currentMode = READY;
}else if(currentMode==RUNNING){
textView.setText(null);
}else if(currentMode==READY){
textView.setText(R.string.mode_ready);
}else if(currentMode==PLAYER_ONE_LOST){
//提示玩家1輸了
textView.setText(R.string.player_one_lost);
currentMode = READY;
}
}
//設置提示控件
public void setTextView(TextView textView) {
this.textView = textView;
}
//監聽鍵盤事件
@Override
public boolean onKeyDown(int keyCode, KeyEvent msg) {
if (currentMode == READY && (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || keyCode == KeyEvent.KEYCODE_DPAD_LEFT)) {
if(keyCode == KeyEvent.KEYCODE_DPAD_RIGHT){//向右鍵,人機對戰
player2 = computer;
}else if(keyCode == KeyEvent.KEYCODE_DPAD_LEFT){//向左鍵,人--人對戰
player2 = human;
}
restart();
setMode(RUNNING);
}else if(currentMode==RUNNING && keyCode == KeyEvent.KEYCODE_DPAD_DOWN){//重新開始
restart();
setMode(READY);
}else{
return false;
}
return true;
}
public void startPlayerVsComputer(int level) {
if (currentMode == READY) {
if (level < 3) {
computer = AiFactory.getInstance(level);
player2 = computer;
restart();
setMode(RUNNING);
}
else {
setMode(READY);
textView.setText(R.string.geniusWinLabel);
}
}
}
public void startPlayerVsPlayer() {
if (currentMode == READY) {
player2 = human;
}
restart();
setMode(RUNNING);
}
public void restartGame() {
if (currentMode == RUNNING) {
restart();
setMode(READY);
}
}
//根據觸摸點坐標找到對應點
private Point newPoint(Float x, Float y){
Point p = new Point(0, 0);
for (int i = 0; i < maxX; i++) {
if ((i * pointSize + xOffset) <= x
&& x < ((i + 1) * pointSize + xOffset)) {
p.setX(i);
}
}
for (int i = 0; i < maxY; i++) {
if ((i * pointSize + yOffset) <= y
&& y < ((i + 1) * pointSize + yOffset)) {
p.setY(i);
}
}
return p;
}
//重新開始
private void restart() {
createPoints();
player1.setChessboard(this);
player2.setChessboard(this);
setPlayer1Run();
//刷新一下
refressCanvas();
}
//是否已開局
private boolean hasStart(){
return currentMode==RUNNING;
}
//處理觸摸事件
@Override
public boolean onTouchEvent(MotionEvent event) {
//還沒有開局,或者是按下事件,不處理,只處理開局後的觸摸彈起事件
if(!hasStart() || event.getAction()!=MotionEvent.ACTION_UP){
return true;
}
//是否正在處理一步棋的過程中
if(onProcessing()){
return true;
}
playerRun(event);
return true;
}
private synchronized void playerRun(MotionEvent event){
if(isPlayer1Run()){//第一玩家下棋
player1Run(event);
}else if(isPlayer2Run()){//第二玩家下棋
player2Run(event);
}
}
private void player1Run(MotionEvent event){
Point point = newPoint(event.getX(), event.getY());
if(allFreePoints.contains(point)){//此棋是否可下
setOnProcessing();
player1.run(player2.getMyPoints(),point);
//playerOnePoints.add(point);
//刷新一下棋盤
refressCanvas();
//判斷第一個玩家是否已經下了
if(!player1.hasWin()){//我還沒有贏
if(player2==computer){//如果第二玩家是電腦
//10豪秒後才給玩家2下棋
refreshHandler.computerRunAfter(10);
}else{
setPlayer2Run();
}
}else{
//否則,提示游戲結束
setMode(PLAYER_TWO_LOST);
}
}
}
private void player2Run(MotionEvent event){
Point point = newPoint(event.getX(), event.getY());
if(allFreePoints.contains(point)){//此棋是否可下
setOnProcessing();
player2.run(player1.getMyPoints(),point);
// playerTwoPoints.add(point);
//刷新一下棋盤
refressCanvas();
//判斷我是否贏了
if(!player2.hasWin()){//我還沒有贏
setPlayer1Run();
}else{
//否則,提示游戲結束
setMode(PLAYER_ONE_LOST);
}
}
}
private RefreshHandler refreshHandler = new RefreshHandler();
class RefreshHandler extends Handler {
//這個方法主要在指定的時刻發一個消息
public void computerRunAfter(long delayMillis) {
this.removeMessages(0);
//發消息觸發handleMessage函數
sendMessageDelayed(obtainMessage(0), delayMillis);
}
//收到消息
@Override
public void handleMessage(Message msg) {
//電腦走一步棋子
player2.run(player1.getMyPoints(),null);
//刷新一下
refressCanvas();
if(!player2.hasWin()){
//人下
setPlayer1Run();
}else{//第二個玩家贏了
setMode(PLAYER_ONE_LOST);
}
}
};
//是否正在下某一步棋過程中,主是電腦下棋時需要較長的計算時間,這期間一定不可以再響應觸摸事件
private boolean onProcessing() {
return whoRun == -1;
}
//默認第一個玩家先行
private int whoRun = 1;
private void setPlayer1Run(){
whoRun = 1;
}
private void setOnProcessing(){
whoRun = -1;
}
//是否輪到人類玩家下子
private boolean isPlayer1Run(){
return whoRun==1;
}
//是否輪到人類玩家下子
private boolean isPlayer2Run(){
return whoRun==2;
}
private void setPlayer2Run(){
whoRun = 2;
}
private void refressCanvas(){
//觸發onDraw函數
Chessboard.this.invalidate();
}
private void drawPlayer1Point(Canvas canvas){
int size = player1.getMyPoints().size()-1;
if(size<0){
return ;
}
for (int i = 0; i < size; i++) {
drawPoint(canvas, player1.getMyPoints().get(i), GREEN);
}
//最後下的一個點標成黃色
drawPoint(canvas, player1.getMyPoints().get(size), NEW_GREEN);
}
private void drawPlayer2Point(Canvas canvas){
if(player2==null){
return ;
}
int size = player2.getMyPoints().size()-1;
if(size<0){
return ;
}
for (int i = 0; i < size; i++) {
drawPoint(canvas, player2.getMyPoints().get(i), RED);
}
//最後下的一個點標成黃色
drawPoint(canvas, player2.getMyPoints().get(size), NEW_RED);
}
//初始化好三種顏色的點
public void fillPointArrays(int color,Drawable drawable) {
Bitmap bitmap = Bitmap.createBitmap(pointSize, pointSize, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, pointSize, pointSize);
drawable.draw(canvas);
pointArray[color] = bitmap;
}
//doRun方法操作的是看不見的內存數據,此方法內容數據以圖畫的方式表現出來,所以畫之前數據一定要先准備好
@Override
protected void onDraw(Canvas canvas) {
drawChssboardLines(canvas);
//畫鼠標所在的點
drawPlayer1Point(canvas);
//畫電腦下的棋子
drawPlayer2Point(canvas);
}
@Override
public List<Point> getFreePoints() {
return allFreePoints;
}
//初始化空白點集合
private void createPoints(){
allFreePoints.clear();
for (int i = 0; i < maxX; i++) {
for (int j = 0; j < maxY; j++) {
allFreePoints.add(new Point(i, j));
}
}
}
@Override
public int getMaxX() {
return maxX;
}
@Override
public int getMaxY() {
return maxY;
}