Android中国象棋游戏

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

Android中国象棋游戏

hellolxb   2022-06-03 我要评论

实现环境:  android studio 3.2.1, 手机分辨率为: 1920 * 1080

局域网 UDP 连接
分主活动类,棋类,主机类

代码如下:

清单文件要添加的权限:

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

主活动:

package chinachess;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView tvMyIp;
    private EditText editIp;
    private EditText editReceiverPort;
    private EditText editSendrPort;
    private Button btnConn;
    private TextView tvTitle;
    private TextView tvTitle2;
    private TextView tvTitle3;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        tvMyIp = (TextView) findViewById(R.id.tvMyIp);
        editIp = (EditText) findViewById(R.id.editIp);
        editReceiverPort = (EditText) findViewById(R.id.editReceiverPort);
        editSendrPort = (EditText) findViewById(R.id.editSendrPort);
        btnConn = (Button) findViewById(R.id.btnConn);
        btnConn.setOnClickListener(this);
        tvTitle = (TextView) findViewById(R.id.tvTitle);
        tvTitle2 = (TextView) findViewById(R.id.tvTitle2);
        tvTitle3 = (TextView) findViewById(R.id.tvTitle3);
        getIp();
    }
    // 将用于连接的控件隐藏掉
    public void uiGone() {
        tvMyIp.setVisibility(View.GONE);
        editIp.setVisibility(View.GONE);
        editReceiverPort.setVisibility(View.GONE);
        editSendrPort.setVisibility(View.GONE);
        btnConn.setVisibility(View.GONE);
        btnConn.setVisibility(View.GONE);
        tvTitle.setVisibility(View.GONE);
        tvTitle2.setVisibility(View.GONE);
        tvTitle3.setVisibility(View.GONE);
    }
    // 获取局域网 ip 的方法
    private void getIp() {
        String s;
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
                NetworkInterface face = en.nextElement();
                for (Enumeration<InetAddress> enAddr = face.getInetAddresses(); enAddr.hasMoreElements(); ){
                    InetAddress addr = enAddr.nextElement();
                    if (!addr.isLoopbackAddress()) {
                        s = addr.getHostAddress();
                        // 只获取区域网 ip 地址
                        if ("192".equals(s.substring(0, 3))) {
                            tvMyIp.setText("IP: " + s);
                        }
                    }
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnConn:
                submit();
                break;
        }
    }

    private void submit() {
        // validate
        String ip = editIp.getText().toString().trim();
        if (TextUtils.isEmpty(ip)) {
            Toast.makeText(this, "IP不可为空!", Toast.LENGTH_SHORT).show();
            return;
        }

        String receiverPort = editReceiverPort.getText().toString().trim();
        if (TextUtils.isEmpty(receiverPort)) {
            Toast.makeText(this, "接收端口不可为空!", Toast.LENGTH_SHORT).show();
            return;
        }

        String sendPort = editSendrPort.getText().toString().trim();
        if (TextUtils.isEmpty(sendPort)) {
            Toast.makeText(this, "发送端口不可为空!", Toast.LENGTH_SHORT).show();
            return;
        }

        // TODO validate success, do something
        // 开启服务器或客户端,并添加到主活动中
        ServerView server = new ServerView(this, Integer.valueOf(receiverPort), Integer.valueOf(sendPort), ip);
        ViewGroup.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        addContentView(server, params);
        server.startServer();
    }
}

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="5dp"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="UDP"
        android:textColor="#000"
        android:textSize="22dp" />

    <TextView
        android:id="@+id/tvMyIp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="IP"
        android:textColor="#000"
        android:textSize="18sp" />

    <EditText
        android:id="@+id/editIp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="IP"
        android:text="192.168."
        android:textColor="#000" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center">

        <TextView
            android:id="@+id/tvTitle2"
            android:textSize="18sp"
            android:textColor="#000"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="接收端口: " />

        <EditText
            android:id="@+id/editReceiverPort"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:hint="IP"
            android:inputType="number"
            android:text="10001"
            android:textColor="#000" />

        <TextView
            android:id="@+id/tvTitle3"
            android:textSize="18sp"
            android:textColor="#000"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="发送端口: " />

        <EditText
            android:id="@+id/editSendrPort"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:hint="IP"
            android:inputType="number"
            android:text="10000"
            android:textColor="#000" />

    </LinearLayout>

    <Button
        android:id="@+id/btnConn"
        android:textSize="18sp"
        android:textStyle="bold"
        android:textColor="#FFF5C3"
        android:text="连接"
        android:background="@drawable/btn_blue"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

btn_blue.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <corners android:radius="5dp" />

    <gradient android:startColor="#0000a1" android:centerColor="#1f6ed4"
        android:angle="315"
        android:endColor="#39BAE8" />

</shape>

棋类:

package chinachess;

public class Chess {
    // 标记当前棋的颜色是红棋还是黑棋
    public static final int BLACK = 1;
    public static final int RED = 2;
    // 存储当前棋子是红棋还是黑棋
    private int player;
    // 当前棋子的名字
    private String name;
    // 图片 id
    private int imageId;
    // 图片编号
    // 0-15 红色,16-31 黑色
    // 0-帅
    // 1,2-士
    // 3,4-相
    // 5,6-马
    // 7,8-车
    // 9,10-炮
    // 11-15-卒
    // 16-黑帅
    // ...
    private int num;
    // 当前棋在棋盘中的位置,posX 为行,posY 为列
    private int posX, posY;

    public Chess(int player, String name, int num) {
        this.player = player;
        this.name = name;
        this.num = num;
        // 红旗的图片 id
        int[] redId = {R.drawable.shuai, R.drawable.shi, R.drawable.xiang, R.drawable.ma, R.drawable.che,
        R.drawable.pao, R.drawable.bing};
        // 黑旗的图片 id
        int[] blackId = {R.drawable.shuai1, R.drawable.shi1, R.drawable.xiang1, R.drawable.ma1, R.drawable.che1,
                R.drawable.pao1, R.drawable.bing1};
        // 所有的棋的种类
        String[] names = {"帅", "士", "相", "马", "车", "炮", "卒"};
        // 根据当前棋的颜色来匹配不同的图片
        if (player == RED) {
            for (int i = 0; i < names.length; i++) {
                if (names[i].equals(name)) {
                    imageId = redId[i];
                    break;
                }
            }
        } else {
            for (int i = 0; i < names.length; i++) {
                if (names[i].equals(name)) {
                    imageId = blackId[i];
                    break;
                }
            }
        }
    }
    // 获取棋编号
    public int getNum() {
        return num;
    }
    // 获取棋的颜色
    public int getPlayer() {
        return player;
    }
    // 获取棋名
    public String getName() {
        return name;
    }
    // 获取棋的图片id
    public int getImageId() {
        return imageId;
    }
    // 设置棋的行列坐标
    public void setPos(int posX, int posY) {
        this.posX = posX;
        this.posY = posY;
    }
    // 设置棋的航坐标
    public int getPosX() {
        return posX;
    }
    // 设置棋的列坐标
    public int getPosY() {
        return posY;
    }
    // 将当前的棋坐标水平翻转
    public void reverse() {
        posX = 9 - posX;
    }
}

主机类:
 

package chinachess;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

import chinachess.Chess;
import chinachess.MainActivity;
import com.example.l_b.chinachess.R;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class ServerView extends View {

    // private static final String TAG = "qaq";
    // 棋的颜色
    public static final int BLACK = 1;
    public static final int RED = 2;
    // 设置棋盘的位置,视情况而定
    public static final int MARGINTOP = 150;
    public static final int MARGINLEFT = 83;
    // 每个格子的宽度
    public static final int W = 114;
    // 棋的总数
    public static final int ALLCHESS = 32;
    // 棋盘的行
    public static final int ROW = 10;
    // 棋盘的列
    public static final int COL = 9;
    // 没有棋的棋盘坐标的标记
    public static final int NULL = -1;
    // 接受消息端口
    private int receiverPort;
    // 发送消息端口
    private int sendPort;
    // 对方 ip
    private String ip;
    // 主活动
    private MainActivity context;
    // 所有的棋
    private Chess[] allChess = new Chess[ALLCHESS];
    // 棋盘
    private int[][] map = new int[ROW][COL];
    // 当前是否可以点击
    private boolean canPlay;
    // 判断是否移动了,只可处理点击事件
    private boolean isMove;
    // 设置我方的棋的颜色
    private int player;
    // 记录第一次选择的棋
    private Chess firstSelect;
    // 用于提示消息
    private TextView tvTip;
    // 判断当前是否赢了
    private boolean isWin;
    
    private Button btnNewGame;
    
    // 通过构造方法将一些重要参数传入进来
    public ServerView(Context context, int receiverPort, int sendPort, String ip) {
        super(context);
        this.receiverPort = receiverPort;
        this.sendPort = sendPort;
        this.ip = ip;
        this.context = (MainActivity) context;
        // 添加一个可以重新开始的按钮
        ViewGroup.LayoutParams param = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 120);
        btnNewGame = new Button(context);
        // 开始游戏后才可以点击
        btnNewGame。setEnabled(false);
        btnNewGame.setX(10);
        btnNewGame.setY(10);
        btnNewGame.setBackgroundResource(R.drawable.btn_blue);
        btnNewGame.setText("重新开始");
        btnNewGame.setTextColor(Color.WHITE);
        btnNewGame.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // 重新开始游戏
                restartGame();
                sendMes("restart|");
            }
        });
        this.context.addContentView(btnNewGame, param);
        // 添加用于提示的文本框
        tvTip = new TextView(context);
        tvTip.setX(300);
        tvTip.setY(10);
        this.context.addContentView(tvTip, param);
        // 初始化棋盘
        initMapAndChess();
        // 设置触屏事件
        setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // 若当前不可以点击则不执行点击事件
                if (!canPlay) {
                    return false;
                }
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        isMove = false;
                        break;
                    case MotionEvent.ACTION_MOVE:
                        // 若有滑动事件则会执行
                        isMove = true;
                        break;
                    case MotionEvent.ACTION_UP:
                        if (!isMove) {
                            // 获取点击的 x 坐标
                            int x = ((int) event.getX() - MARGINLEFT);
                            // 获取点击的 y 坐标
                            int y = ((int) event.getY() - MARGINTOP - MARGINLEFT);
                            // 转化为棋盘的 col 列坐标
                            // x % W > W / 2 ? 1 : 0 为当前的位置的求模后是否满足大于一半的宽度,
                            // 若大于则把它安排到下一个位置,否则不变
                            x = x / W + (x % W > W / 2 ? 1 : 0);
                            // 转化为棋盘的 row 行坐标
                            y = y / W + (y % W > W / 2 ? 1 : 0);
                            // 若超出棋盘边界则不执行
                            if (x < 0 || x >= COL || y < 0 || y >= ROW) {
                                break;
                            }
                            // 如果为第一次点击
                            if (firstSelect == null) {
                                // 若当前点击的位置是空的
                                if (map[y][x] == NULL) {
                                    break;
                                }
                                // 创建一个临时变量来存储当前位置上的棋
                                Chess temp = allChess[map[y][x]];
                                // 若点击的是对方的棋
                                if (temp.getPlayer() != player) {
                                    break;
                                }
                                // 存起来
                                firstSelect = temp;
                                // 更新视图
                                invalidate();
                            } else {
                                // 已选择第一个棋后
                                // 若当前位置为空棋时
                                if (map[y][x] == NULL) {
                                    // 若能移动
                                    if (canMove(y, x)) {
                                        // 获取第一次选择的棋的编号, 范围为 0,1,2,3...31;
                                        int pos = map[firstSelect.getPosX()][firstSelect.getPosY()];
                                        // 将第一次选择的棋编号给第二次选择的位置
                                        map[y][x] = pos;
                                        // 将第一次选择的棋编号置空
                                        map[firstSelect.getPosX()][firstSelect.getPosY()] = NULL;
                                        // 将第一次选择的棋的位置改变为当前位置
                                        firstSelect.setPos(y, x);
                                        // 轮到对方下
                                        canPlay = false;
                                        // 将存储的第一个棋置空
                                        firstSelect = null;
                                        // 发送我方移动信息给客户单,“|” 为分隔符,用于分割信息,
                                        // 最后要用 "|" 结尾,不然最后一个信息个出错
                                        sendMes("move|" + pos + "|" + y + "|" + x + "|");
                                        // 设置提示消息
                                        tvTip.setText("对方下");
                                        // 更新视图
                                        invalidate();
                                    }
                                } else {
                                    // 若当前的位置不为空棋
                                    // 获取当前的棋编号
                                    int pos = map[y][x];
                                    // 若当前的棋为我方棋时,则把第一次选择的棋替换为当前棋
                                    if (allChess[pos].getPlayer() == player) {
                                        firstSelect = allChess[pos];
                                        invalidate();
                                    } else {
                                        // 是否可以移动
                                        if (canMove(y, x)) {
                                            // 将第一次选择的棋编号置空
                                            map[firstSelect.getPosX()][firstSelect.getPosY()] = NULL;
                                            // 将第一次选择的棋编号给第二次选择的位置
                                            map[y][x] = firstSelect.getNum();
                                            // 将第一次选择的棋的位置改变为当前位置
                                            firstSelect.setPos(y, x);
                                            // 发送我方移动信息给客户单
                                            sendMes("move|" + firstSelect.getNum() + "|" + y + "|" + x + "|");
                                            // 若当前吃掉的棋为帅时
                                            if ("帅".equals(allChess[pos].getName())) {
                                                sendMes("winner|");
                                                tvTip.setText("我方获胜");
                                            } else {
                                                tvTip.setText("对方下");
                                            }
                                            // 将存储的第一个棋置空
                                            firstSelect = null;
                                            // 将吃掉得棋置空
                                            allChess[pos] = null;
                                            // 轮到对方下
                                            canPlay = false;
                                            invalidate();
                                        }
                                    }
                                }
                            }
                        }
                }
                return true;
            }
        });
    }
    // 判断是否可以移动
    private boolean canMove(int r2, int c2) {
        // 要移动的棋名
        String name = firstSelect.getName();
        // 存储第一次的行坐标
        int r1 = firstSelect.getPosX();
        // 存储第一次的列坐标
        int c1 = firstSelect.getPosY();

        if ("帅".equals(name)) {
            // 只能直线移动
            if (r1 != r2 && c1 != c2) {
                return false;
            }
            // 判断是否可以将军
            if (map[r2][c2] != NULL && "帅".equals(allChess[map[r2][c2]].getName())) {
                // 保持 r1 一定小于 r2
                // c1 一定等于 c2
                if (r1 > r2) {
                    int t = r1;
                    r1 = r2;
                    r2 = t;
                }
                // 标记两个帅之间是否有棋子,若有,则不可以移动
                boolean flag = true;
                for (int i = r1 + 1; i < r2 && flag; i++) {
                    if (map[i][c1] != NULL) {
                        flag = false;
                    }
                }
                return flag;
                // 只可移动一格
            } else if (Math.abs(c1 - c2) != 1 && Math.abs(r2 - r1) != 1) {
                return false;
            }
            // 只可在田字中移动
            return r2 >= 7 && c2 >= 3 && c2 <= 5;
        } else if ("士".equals(name)) {
            // 不能直线移动
            if (r1 == r2 || c1 == c2) {
                return false;
            }
            // 只能移动一格
            if (Math.abs(r1 - r2) != 1 || Math.abs(c1 - c2) != 1) {
                return false;
            }
            // 只可在田字中移动
            return r2 >= 7 && c2 >= 3 && c2 <= 5;
        } else if ("相".equals(name)) {
            // 不可以过河,越界
            if (r2 <= 4) {
                return false;
            }
            // 只能走田字
            if (Math.abs(r1 - r2) != 2 || Math.abs(c1 - c2) != 2) {
                return false;
            }
            // 是否被挡住了
            return map[(r1 + r2) / 2][(c1 + c2) / 2] == NULL;
        } else if ("马".equals(name)) {
            // 只能走 日 字
            if (Math.abs(r1 - r2) * Math.abs(r1 - r2) + Math.abs(c1 - c2) * Math.abs(c1 - c2) != 5) {
                return false;
            }
            // 向下走时
            if (r2 - r1 == 2) {
                // 是否被挡住了
                if (map[r1 + 1][c1] != NULL) {
                    return false;
                }
                // 向上走时
            } else if (r2 - r1 == -2) {
                if (map[r1 - 1][c1] != NULL) {
                    return false;
                }
                // 向左走时
            } else if (c2 - c1 == 2) {
                if (map[r1][c1 + 1] != NULL) {
                    return false;
                }
                // 向右走时
            } else if (c2 - c1 == -2) {
                if (map[r1][c1 - 1] != NULL) {
                    return false;
                }
            }
        } else if ("车".equals(name)) {
            // 只能直线移动
            if (r2 != r1 && c2 != c1) {
                return false;
            }
            // 一个临时变量
            int t;
            // 左右走
            if (r2 == r1) {
                // 确保 c1 一定 小于 c2
                if (c1 > c2) {
                    t = c1;
                    c1 = c2;
                    c2 = t;
                }
                // 之间是否被挡住了
                for (int i = c1 + 1; i < c2; i++) {
                    if (map[r1][i] != NULL) {
                        return false;
                    }
                }
            } else {
                // 上下走
                // 确保 r1 一定 小于 r2
                if (r1 > r2) {
                    t = r1;
                    r1 = r2;
                    r2 = t;
                }
                for (int i = r1 + 1; i < r2; i++) {
                    if (map[i][c1] != NULL) {
                        return false;
                    }
                }
            }
        } else if ("炮".equals(name)) {
            // 只能走直线
            if (r1 != r2 && c1 != c2) {
                return false;
            }
            int t;
            // 平常走时,跟车一样
            if (map[r2][c2] == NULL) {
                if (r2 == r1) {
                    // 确保 c1 一定在 c2 的上面
                    if (c1 > c2) {
                        t = c1;
                        c1 = c2;
                        c2 = t;
                    }
                    for (int i = c1 + 1; i < c2; i++) {
                        if (map[r1][i] != NULL) {
                            return false;
                        }
                    }
                } else {
                    if (r1 > r2) {
                        t = r1;
                        r1 = r2;
                        r2 = t;
                    }
                    for (int i = r1 + 1; i < r2; i++) {
                        if (map[i][c1] != NULL) {
                            return false;
                        }
                    }
                }
                // 可以吃子时
            } else {
                // 用于记录之间有几个棋子,只能为一
                int count = 0;
                if (r2 == r1) {
                    // 确保 c1 一定在 c2 的上面
                    if (c1 > c2) {
                        t = c1;
                        c1 = c2;
                        c2 = t;
                    }
                    for (int i = c1 + 1; i < c2; i++) {
                        if (map[r1][i] != NULL) {
                            count++;
                            if (count > 1) {
                                return false;
                            }
                        }
                    }
                } else {
                    if (r1 > r2) {
                        t = r1;
                        r1 = r2;
                        r2 = t;
                    }
                    for (int i = r1 + 1; i < r2; i++) {
                        if (map[i][c1] != NULL) {
                            count++;
                            if (count > 1) {
                                return false;
                            }
                        }
                    }
                }
                if (count != 1) {
                    return false;
                }
            }
        } else if ("卒".equals(name)) {
            // 只能往前走
            if (r2 > r1) {
                return false;
            }
            // 若过河了
            if (r1 <= 4) {
                // 只能走直线
                if (r2 != r1 && c2 != c1) {
                    return false;
                }
                // 只能走一格
                if (Math.abs(c1 - c2) != 1 && r1 - r2 != 1) {
                    return false;
                }
            } else {
                // 若没有过河,则只能前走一格
                if (c2 != c1 || r1 - r2 != 1) {
                    return false;
                }
            }
        }
        return true;
    }

    private void initMapAndChess() {
        // 将编号全置空
        for (int i = 0; i < ROW; i++) {
            for (int j = 0; j < COL; j++) {
                map[i][j] = NULL;
            }
        }
        // 将,两士,两相,两马,两车,两炮,五卒
        // 32 个棋子在地图上的 x 坐标,红棋先
        // 前16个棋的 x 坐标
        int[] mapX = {4, 3, 5, 2, 6, 1, 7, 0, 8, 1, 7, 0, 2, 4, 6, 8};
        // 前16个棋的 y 坐标
        int[] mapY = {0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 3, 3, 3, 3, 3};
        // 前16个棋的棋名
        String[] strings = {"帅", "士", "士", "相", "相", "马", "马", "车", "车", "炮", "炮", "卒", "卒", "卒", "卒", "卒"};
        // 临时存储行和列
        int row, col;
        for (int i = 0; i < allChess.length; i++) {
            // 小于16为红旗
            if (i < 16) {
                row = mapY[i];
                col = mapX[i];
                // 初始化棋子
                allChess[i] = new Chess(RED, strings[i], i);
                // 给相应的棋盘位置安排编号
                map[row][col] = i;
                // 设置棋子在棋盘中的初始位置
                allChess[i].setPos(row, col);
            } else {
                row = ROW - mapY[i - 16] - 1;
                col = COL - mapX[i - 16] - 1;
                allChess[i] = new Chess(BLACK, strings[i - 16], i);
                map[row][col] = i;
                allChess[i].setPos(row, col);
            }
        }
//        showChess();
    }

//    private void showChess() {
//        String s;
//        for (int i = 0; i < ROW; i++) {
//            s = "";
//            for (int j = 0; j < COL; j++) {
//                s += map[i][j] + " ";
//            }
//            Log.d(TAG, "showChess: " + s);
//        }
//        for (int i = 0; i < allChess.length; i++) {
//            Log.d(TAG, "showChess: " + allChess[i].getName() + "-" + allChess[i].getNum() + "-" + allChess[i].getPosX() + "-" + allChess[i].getPosY());
//        }
//    }
    // 翻转棋盘
    private void reverseMap() {
        int t;
        // 默认为黑下红上
        // 主机为黑下红上
        // 客户端为黑上红下,所以是客户端时将双方棋的位置换一下
        for (int i = 0; i < ROW / 2; i++) {
            for (int j = 0; j < COL; j++) {
                t = map[i][j];
                map[i][j] = map[ROW - i - 1][j];
                map[ROW - i - 1][j] = t;
            }
        }
        for (Chess c : allChess) {
            c.reverse();
        }
//        showChess();
    }

    public void startServer() {
        // 将主活动的辅助控件隐藏掉
        context.uiGone();
        tvTip.setText("等待连接...");
        // 开启接收信息的线程
        new MessageThread().start();
    }

    public void sendMes(final String s) {
        // 发送信息,要在线程里执行(异步执行)
        new Thread(new Runnable() {
            @Override
            public void run() {
                DatagramSocket ds = null;
                try {
                    ds = new DatagramSocket();
                    byte[] buffer;
                    buffer = s.getBytes();
                    InetAddress ia = InetAddress.getByName(ip);
                    DatagramPacket dp = new DatagramPacket(buffer, buffer.length, ia, sendPort);
                    ds.send(dp);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (ds != null) {
                        ds.close();
                    }
                }
            }
        }).start();
    }
    // 每次调用 invalidate 就会执行这个方法
    @Override
    protected void onDraw(Canvas canvas) {
        // 画笔,用于设置线条样式
        Paint paint = new Paint();
        paint.setStrokeWidth(5);
        paint.setStyle(Paint.Style.STROKE);
        // 设置棋盘图片,宽高视手机分辨率而定
        canvas.drawBitmap(getBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.bg), 1080, 1195), 0, MARGINTOP, paint);
        // 画棋
        for (Chess allChes : allChess) {
            // 若没有被吃掉
            if (allChes != null) {
                // x 坐标为列坐标乘以格子的宽度然后减去一半的格子宽度,让棋子的中心对齐坐标顶点
                int x = allChes.getPosY() * W + MARGINLEFT - W / 2;
                int y = allChes.getPosX() * W + MARGINTOP + MARGINLEFT - W / 2;
                canvas.drawBitmap(getBitmap(BitmapFactory.decodeResource(getResources(), allChes.getImageId()), W, W), x, y, paint);
            }
        }
        // 若第一次选择了则画一个矩阵边框来显示已选中
        if (firstSelect != null) {
            paint.setColor(Color.RED);
            int x = firstSelect.getPosY() * W + MARGINLEFT - W / 2;
            int y = firstSelect.getPosX() * W + MARGINTOP + MARGINLEFT - W / 2;
            // 画线
            int[] posX = {x, x + W, x + W, x, x};
            int[] posY = {y, y, y + W, y + W, y};
            Path path = new Path();
            path.moveTo(posX[0], posY[0]);
            for (int i = 1; i < posX.length; i++) {
                path.lineTo(posX[i], posY[i]);
            }
            canvas.drawPath(path, paint);
        }
    }
    // 自定义图片宽高
    private Bitmap getBitmap(Bitmap rootImg, int w, int h) {
        int rW = rootImg.getWidth();
        int rH = rootImg.getHeight();
        Matrix matrix = new Matrix();
        matrix.postScale(w * 1.0f / rW, h * 1.0f / rH);
        return Bitmap.createBitmap(rootImg, 0, 0, rW, rH, matrix, true);
    }
    // 重新开始游戏
    private void restartGame() {
        // 重新初始化棋盘
        initMapAndChess();
        // 若是黑棋则先下
        canPlay = true;
        String tip = "已重新开始游戏,我下";
        if (player == RED) {
            reverseMap();
            canPlay = false;
            tip = "已重新开始游戏,对方下";
        }
        isWin = false;
        // 给提示,在线程中更新 UI 时需转到主线程上
        setTip(tip);
        // 刷新视图
        updateOnUI();
    }
    // 接受信息的线程
    class MessageThread extends Thread {
        @Override
        public void run() {
            try {
                DatagramSocket ds = new DatagramSocket(receiverPort);
                byte[] buffer = new byte[100];
                DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
                // 发送加入信息
                sendMes("join|");
                while (true) {
                    ds.receive(dp);
                    String s = new String(buffer);
                    // 指定分割符分割信息
                    String[] array = s.split("\\|");
                    switch (array[0]) {
                        // 只有主机才会接收到
                        case "join":
                            player = BLACK;
                            canPlay = true;
                            sendMes("conn|");
                            setTip("我是黑棋,我下");
                            context.runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    btnNewGame.setEnabled(true);
                                }
                            });
                            break;
                        // 只有客户端才会接收到
                        case "conn":
                            player = RED;
                            // 要翻转棋盘
                            reverseMap();
                            updateOnUI();
                            canPlay = false;
                            setTip("我是红棋,对方下");
                            context.runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    btnNewGame.setEnabled(true);
                                }
                            });
                            break;
                        // 接受到了移动信息
                        case "move":
                            // 判断是否赢了,若赢了则后面的不执行
                            if (isWin) {
                                continue;
                            }
                            // 对方走的棋编号
                            int originalPos = Integer.valueOf(array[1]);
                            // 要走的行坐标
                            int y2 = ROW - Integer.valueOf(array[2]) - 1;
                            // 要走的列坐标
                            int x2 = Integer.valueOf(array[3]);
                            // 我方当前的对方要走的棋行列坐标
                            int y1 = allChess[originalPos].getPosX();
                            int x1 = allChess[originalPos].getPosY();
                            // 存储要走向的坐标在棋盘的编号
                            int movePos = map[y2][x2];
                            // 将原来的位置置空
                            map[y1][x1] = NULL;
                            // 要走的位置设置为对方的棋编号
                            map[y2][x2] = originalPos;
                            // 更新其坐标
                            allChess[originalPos].setPos(y2, x2);
                            // 判断要走的位置是否有棋,若有,则置空
                            if (movePos != NULL && allChess[movePos] != null) {
                                allChess[movePos] = null;
                            }
                            // 更新视图
                            updateOnUI();
                            // 我方可以下棋
                            canPlay = true;
                            setTip("我下");
                            break;
                        // 对方赢了
                        case "winner":
                            isWin = true;
                            whoWin();
                            break;
                        // 重新开始游戏
                        case "restart":
                            restartGame();
                            break;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    // 有赢家后
    private void whoWin() {
        canPlay = false;
        setTip("对方获胜");
    }
    // 更新视图
    private void updateOnUI() {
        context.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                invalidate();
            }
        });
    }
    // 设置提示信息
    private void setTip(final String s) {
        context.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                tvTip.setText(s);
            }
        });
    }
}

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们