移动平台开发综合实践 安卓android大作业-雷net在线对战
转自我的安卓课程作业
图片应该是没了,懒得搞了
移动平台开发综合实践 大作业-雷net在线对战
一、实验目的
完成雷net在线对战的开发
一切都是命运石之门的选择
• 用户界面设计:
- 使用Android Studio中的布局编辑器设计游戏客户端的用户界面。
- 熟悉常见的布局管理器(如LinearLayout、RelativeLayout、ConstraintLayout等)。
- 使用自定义View或SurfaceView实现游戏画面的绘制和交互。
• 多媒体和图形处理:
- 加载和显示游戏资源,如图像、音频等。
- 使用Android提供的多媒体类库(如MediaPlayer)管理游戏中的音效和背景音乐。
- 使用Canvas绘制游戏中的复杂图形和动画效果。
• 网络通信:
- 使用Java中的Socket编程实现客户端与服务器的通信。
- 处理网络请求和响应,包括数据的发送、接收和解析。
• 并发和多线程编程:
- 使用Thread等机制处理后台任务和异步操作。
- 确保在主线程以外的任务不会阻塞UI线程。
• 用户输入和事件处理:
- 处理触摸事件和用户输入,实现游戏中的交互操作。
- setOnTouchListener(new OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event) {…
• 游戏逻辑和状态管理:
- 设计和实现游戏的规则和逻辑,包括游戏的状态转换、动作解析和胜负判定。
- 管理游戏中的各种状态和数据,确保游戏运行的一致性和正确性。
二、实验环境
实验所使用的设备名称及规格,网络管理工具简介、版本等。
- 设备名称:华硕天选4
- 软件工具:android studio lguana 2023
- 运行设备:
三、实验内容与实验要求
3.1实验内容:雷net在线对战
什么是雷net?
雷net是命运石之门游戏及动画中架空的桌游,但是实际上真的存在,是一个心理博弈类的暗棋。下面介绍一下雷net:
3.1.1棋盘
雷net的棋盘为8×8的正方形组成。
棋盘分为普通格子与exit(其实叫入口可能更合适)玩家可以在靠近自己一侧的普通格子上随意摆放棋子(不可以在入口上放)
棋子可以在对方exit格子上或者经过exit格子都可以突破棋盘的边界进入对方的server区域,此时棋子重新回到自己手上,其作用到胜利条件再说。
3.1.2棋子
在雷net中有两种棋子,分别为link(链接)与virus(病毒),后简称l与v。每个棋子正常情况下只能一次走一格(不包括斜着走)。
这两种棋子可以在规定范围内随意摆放,比如这样:
这个游戏的胜利条件非常单纯:
吃掉对方link数量+进入对方server的link数量=4,胜利
吃virus+进virus=4,失败(一般不会把自己virus进对面server里)
3.1.3终端卡
终端卡类似于装备和技能的合称,一共有4种(原作)分别为:link boost、Firewall、virus cherker与404 not found。
1、link boost
link boost可以说是雷net中最核心的技能,它的效果朴实无华,就是让你的一个棋子可以一次走两格,或者可以斜着走一格(本质上是拐弯走两格)
一局游戏只有一个,但可以循环使用,可以看做装备。给一个棋子装上花费一回合,卸下了也花费一回合。如果装有link boost的棋子被吃了,link boost则会回到自己手上,可以在自己回合再次使用。
2、firewall
Firewall,防火墙,可以放在任何一个没有棋子的普通格子上(在新版中可以放在任何一个没有对方棋子的普通格子上,且自己棋子可以进入),然后谁都无法进入或结果此格子
与boost一样,一局游戏只有一个,但可以循环使用,可以看做装备。给一个普通格子装上花费一回合,卸下了也花费一回合。
3、virus cherker
virus cherker,使对方一个棋子翻面,从而能知道是virus还是link。
一次性技能,使用花费一个回合。
4、404 not found
404 not found 简称404,可以自己场上两个棋子交换或者不交换位置(不交换也同样使用404,且会有404动画,主要起一个迷惑作用)若这两个棋子中有翻面的棋子,则重新盖好。
一次性技能,使用花费一个回合。
3.2原理分析
Socket:
首先要对战的话,就要web连接一下,这次我选socket内网连接,比较简单方便,也是我们课程学习过的内容。Server一个机子,client一个机子,然后进行通信,两边都有一个棋盘,棋子都是单独的,然后我这边进行一个操作,需要对面看到的时候,就传消息给对面,对面同步一下即可。
初始化和Chess类:
棋盘需要初始化一下,列好八个棋子的初始位置,然后让玩家决定是link还是virus,棋子使用一个chess类统一管理一下。刚开始就是决定自己的link和virus,都是四个,并且要等待对面的选择完毕,才能开始游戏。注意自己是看不见对面的选择的,就是自己安排好了,看对面还是一片空白的。
回合逻辑:
每个棋子都是横竖走一格,轮流下。然后技能也占用一回合,这是回合制。注意下棋是要监听触屏动作的,要定位一下触屏的地方,然后标注一下代表选中了,保存第一个操作,点第二下棋子才进行移动。注意我这边移动棋子那边也要看得见,所以需要通信同步。然后最重要的棋盘里面需要坐标定位,棋盘布置好之后,用绝对位置太麻烦了,就是数格子的相对位置就行。标注格子里有没有棋子什么的就是要用一个数组去存。
技能逻辑:
使用技能的话,就是第一个是boost,改变棋子的移动方式,这个需要标注出来,自己和对面都能看见是哪个棋子有boost。第二个是check,本来看不见对面棋子是link还是virus的,check一下就能看见了,就是加载一下对面棋子的图片。第三个是firewall,就是设置一个格子不让棋子进来,这个if一下就行。第四个是notfound,就是选择是否交换自己的两个棋子,可以交换也可以不交换,但是对面不知道你到底换没换,还要注意本来被check的棋子交换之后也是看不见了的。
其他优化:
另外就是放个音乐,加强游戏氛围。
3.3具体实验要求
写个像这个一样的:雷net Access Battle
(我本来想抄一下的,但是他这个用cocos搞的,和安卓基本上没啥关系,只能自己写了一个,刚开始看了看csdn上一个象棋对战的代码,非常有用。)
四、实验过程与分析
4.1实验记录
4.1.1 Socket连接
写的界面就是this:
主要是让mainactivity去管理一下server和client的连接。
按钮逻辑,一个sever一个client。
// 根据按钮来判断作为主机还是客户端
final ViewGroup.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
serverBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ServerView serverView = new ServerView(MainActivity.this, ip, MainActivity.this);
addContentView(serverView, params);
serverView.startConn();
// 将当前控件隐藏掉
viewGone();
}
});
clientBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ipText.getText().toString().isEmpty()) {
Toast.makeText(MainActivity.this, "IP 不能为空!", Toast.LENGTH_SHORT).show();
return;
}
ClientView clientView = new ClientView(MainActivity.this, ipText.getText().toString(), ip, MainActivity.this);
addContentView(clientView, params);
clientView.startJoin();
// 将当前控件隐藏掉
viewGone();
}
});
然后server里写启动服务器:
// 开启服务器
public void startConn() {
// 只能在线程(异步)中执行 172.30.4.74
new Timer().schedule(new TimerTask() {
@Override
public void run() {
try {
serverSocket = new ServerSocket(port);
// 获取客户端信息,若无客户端连接则会一直暂停在这
socket = serverSocket.accept();
setTip("连接成功!");
// 发送已连接给客户端
sendMes("conn|");
// 开启接受消息的线程
new MessageThread().start();
// 更新视图
updateOnUI();
} catch (IOException e) {
e.printStackTrace();
}
}
}, 0);
}
Client里写连接:
public void startJoin() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
try {
socket = new Socket(ip, port);
setTip("已连接");
// 存储当前输入的 ip
mainActivity.setMyIp();
sendMes("join|");
new MessageThread().start();
} catch (IOException e) {
e.printStackTrace();
}
}
}, 0);
}
想要通信的话,创建线程,两边都一样:
// 接受信息的线程
class MessageThread extends Thread {
@Override
public void run() {
BufferedReader br = null;
InputStreamReader isr = null;
try {
String t;
while (true) {
isr = new InputStreamReader(socket.getInputStream());
br = new BufferedReader(isr);
if (br.ready()) {
String cmd = br.readLine();
String[] array = cmd.split("\\|");
switch (array[0]) {
然后有发信息的函数,两边都一样:
private void sendMes(final String s) {
new Thread(new Runnable() {
@Override
public void run() {
try {
pw = new PrintWriter(socket.getOutputStream());
pw.println(s);
pw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
4.1.2初始化和Chess类
这个主要是改的象棋棋盘。
初始化代码,主要是列一下棋子,然后标注一下位置和哪一边的。
private void initMapAndChess() {
// 将编号全置空
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
map[i][j] = NULL;
}
}
// 16 个棋子在地图上的 x 坐标,红棋先
// 前8个棋的 x 坐标
int[] mapX = {0, 1, 2, 3, 4, 5, 6, 7};
// 前8个棋的 y 坐标
int[] mapY = {0, 0, 0, 1, 1, 0, 0, 0};
// 前8个棋的棋名
String[] strings = {"none", "none", "none", "none", "none", "none", "none", "none",
"none", "none", "none", "none", "none", "none", "none", "none"};
// 临时存储行和列
int row, col;
for (int i = 0; i < allChess.length; i++) {
// 小于8为红旗
if (i < 8) {
row = mapY[i];
col = mapX[i];
// 初始化棋子
allChess[i] = new Chess(BLACK, strings[i], i);
// 给相应的棋盘位置安排编号
map[row][col] = i;
// 设置棋子在棋盘中的初始位置
allChess[i].setPos(row, col);
} else {
row = ROW - mapY[i - 8] - 1;
col = COL - mapX[i - 8] - 1;
allChess[i] = new Chess(RED, strings[i - 8], i);
map[row][col] = i;
allChess[i].setPos(row, col);
}
}
}
要画一下棋盘和棋子,还有技能什么的,这里用canvas画:
// 每次调用 updateOnUI 就会执行这个方法
@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.header), 1080, 350), 0, 0, paint);
canvas.drawBitmap(getBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.qipan1), 1080, 1285), 0, MARGINTOP, paint);
canvas.drawBitmap(getBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.boost2), W, W), MARGINLEFT+2*W, 1825, paint);
canvas.drawBitmap(getBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.check2), W, W), MARGINLEFT+3*W, 1825, paint);
canvas.drawBitmap(getBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.wall2), W, W), MARGINLEFT+4*W, 1825, paint);
canvas.drawBitmap(getBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.notfound2), W, W), MARGINLEFT+5*W, 1825, paint);
// 画棋
for (Chess allChes : allChess) {
// 若没有被吃掉
if (allChes != null) {
// x 坐标为列坐标乘以格子的宽度然后减去一半的格子宽度,让棋子的中心对齐坐标顶点
int x = allChes.getPosY() * W + MARGINLEFT;
int y = allChes.getPosX() * W + MARGINTOP + MARGINLEFT + 100;
canvas.drawBitmap(getBitmap(BitmapFactory.decodeResource(getResources(), allChes.getImageId()), W, W), x, y, paint);
Paint paint_x = new Paint();
paint_x.setAlpha(150);
if(allChes.getBoost()!=0){
canvas.drawBitmap(getBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.boost), W, W), x, y, paint_x);
}
if(allChes.getCheck()!=0){
canvas.drawBitmap(getBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.check), W, W), x, y, paint_x);
}
if(allChes.getnotfound()!=0){
canvas.drawBitmap(getBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.notfound), W, W), x, y, paint_x);
}
}
}
这个效果:
4.1.3回合
回合制的逻辑就是两边都有一个canplay参数,自己下完了就canplay=false,对面棋子移动了或者用技能了,就是发信息给这边,这边canplay=true。
简要代码逻辑:
我:
// 若能移动
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 + "|");
// 设置提示消息
setTip("对方下");
// 更新视图
updateOnUI();
}
对面:
// 接受到了移动信息
case "move":
// 对方走的棋编号
int originalPos = Integer.valueOf(array[1])-8;
// 要走的行坐标
int y2 = ROW - Integer.valueOf(array[2]) - 1;
// 要走的列坐标
int x2 = COL - Integer.valueOf(array[3]) - 1;
// 我方当前的对方要走的棋行列坐标
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;
}
// 我方可以下棋
canPlay = true;
setTip("我下");
whoWin();
updateOnUI();
break;
要是进行吃的操作,还要更新记分牌(代码略)
if (allChess[pos].getName().equals("link")) {
eat_link2 += 1;
} else if (allChess[pos].getName().equals("virus")) {
eat_virus2 += 1;
}
if(allChess[pos].getBoost()==1){
boost_num2=-1;
}
newdatatip();
sendMes("eat|" + firstSelect.getNum() + "|" + y + "|" + x + "|"
+ eat_link1 + "|" + eat_link2 + "|" +
eat_virus1 + "|" + eat_virus2 + "|");
updateOnUI();
4.1.4技能
Boost,给个boost_num标记一下:
if (map[y][x] != NULL && map[y][x]!=-2 && map[y][x]!=-3) {
// 获取当前的棋编号
int pos = map[y][x];
// 若当前的棋为我方棋时
if (allChess[pos].getPlayer() == player) {
skill = 0;
if(boost_num!=0){
allChess[boost_num].setBoost(0);
}
allChess[pos].setBoost(1);
boost_num=pos;
// 轮到对方下
canPlay = false;
sendMes("boost|"+ pos + "|" + y + "|" + x + "|");
}
}
Check,显示一下对面的图片:
if (map[y][x] != NULL && map[y][x]!=-2 && map[y][x]!=-3) {
// 获取当前的棋编号
int pos = map[y][x];
// 若当前的棋为敌方棋时
if (allChess[pos].getPlayer() != player) {
allChess[pos].showimage();
allChess[pos].setCheck(1);
skill = 0;
check_chance=false;
// 轮到对方下
canPlay = false;
sendMes("check|"+ pos+"|");
}
}
Firewall,地图数组标记一下不能走了:
if(map[y][x] != NULL && map[y][x] != -2){
setTip("有子,不能使用firewall");
updateOnUI();
break;
}
else if (wall_chance==true && map[y][x] == NULL) {
map[y][x]=-2;
skill = 0;
wall_chance=false;
setTip("使用firewall");
sendMes("wall|"+ pos + "|" + y + "|" + x + "|"+0+"|");
// 轮到对方下
canPlay = false;
}
else if(wall_chance==false && map[y][x]==-2){
map[y][x]=NULL;
skill = 0;
wall_chance=true;
setTip("取消使用firewall");
sendMes("wall|"+ pos + "|" + y + "|" + x + "|"+1+"|");
// 轮到对方下
canPlay = false;
}
updateOnUI();
Notfound,替换两个棋子,注意地图数组和allchess都得换一下,但是boost是不换的,然后check得消除掉。
另外这里需要一个弹窗来选择到底换不换:
if (map[y][x] != NULL && map[y][x]!=-2 && map[y][x]!=-3) {
// 获取当前的棋编号
int posn = map[y][x];
// 若当前的棋为我方棋时,则把第一次选择的棋替换为当前棋
if(posn==map[firstSelect.getPosX()][firstSelect.getPosY()]){
firstSelect=null;
updateOnUI();
} else if (allChess[posn].getPlayer() == player) {
// 获取第一次选择的棋的编号, 范围为 0,1,2,3...31;
int posnn = map[firstSelect.getPosX()][firstSelect.getPosY()];
int ynn = firstSelect.getPosX();
int xnn = firstSelect.getPosY();
int yn = allChess[posn].getPosX();
int xn = allChess[posn].getPosY();
allChess[posn].benotfound();
allChess[posnn].benotfound();
allChess[posn].setCheck(0);
allChess[posnn].setCheck(0);
updateOnUI();
//选择换还是不换
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("选择一项");
builder.setItems(new CharSequence[]{"交换", "不交换"}, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
Log.d("server", "换");
map[ynn][xnn] = posn;
map[yn][xn] = posnn;
allChess[posn].setPos(ynn, xnn);
allChess[posnn].setPos(yn, xn);
if(allChess[posn].getBoost()==1){
allChess[posn].setBoost(0);
allChess[posnn].setBoost(1);
}
if(allChess[posnn].getBoost()==1){
allChess[posnn].setBoost(0);
allChess[posn].setBoost(1);
}
notfound_chance = false;
canPlay = false;
// 将存储的第一个棋置空
firstSelect = null;
skill = 0;
sendMes("notfound|" + posn + "|" + posnn + "|");
updateOnUI();
break;
case 1:
Log.d("server", "不换");
notfound_chance = false;
canPlay = false;
// 将存储的第一个棋置空
firstSelect = null;
skill = 0;
sendMes("nonotfound|" + posn + "|" + posnn + "|");
updateOnUI();
break;
}
}
});
AlertDialog dialog = builder.create();
dialog.show();
updateOnUI();
}
4.1.5点击逻辑
点击逻辑巨烦。要考虑第一次点击,第二次点击。第一次点如果是棋子,标注并且记录保存一下,第二次如果是一个可以走的格子,那就进行move。如果说你点了一下还想换个棋子或者你忽然想用技能了,还要清空一下保存的第一次点击,然后重新进入整个流程。
这里就简单列一下分支,内容太多了。
// 设置触屏事件
setOnTouchListener(new OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isMove = false;
break;
case MotionEvent.ACTION_UP:
if (!isMove) {
// 获取点击的 x 坐标
int x = ((int) event.getX() - MARGINLEFT);
// 获取点击的 y 坐标
int y = ((int) event.getY() - MARGINTOP - MARGINLEFT - 100);
// 转化为棋盘的 col 列坐标
x = x / W;
// 转化为棋盘的 row 行坐标
y = y / W;
// 如果还没选择完link virus
if (!choice_finish)
{这里就是选棋子,你选完这个choice_finish才会变true,进入下一个环节}
// 若当前不可以点击则不执行点击事件
else if (!canPlay) {
return false;
} else {//第一次点击
可能点的是棋子也可以是技能,这里需要根据点击的位置进行一个判断,点棋子的话要保存一下第一次点击。要是点了别的空白区域就不响应。
而且notfound技能交换的时候,这个比较特殊需要点三下的,所以就让它头一次进来是这里,第二次进来就是选棋子进来也是这里,相当于第二次选棋子也作为了第一次点击。
}
//第二次点击
else {
//若第一次选择了技能
if (skill != 0) {这里switch,执行一下技能,每个技能都需要自己的变量去存储一些信息,注意需要时刻保存和传递信息给对面。
注意中途想换技能或者不想用技能了,想走棋子,还得写一下取消的代码
注意用任何技能都需要给对面发信息,让对面知道。
注意第四个技能需要一个弹窗,来选择到底交换不交换棋子。
}
else {
// 已选择第一个棋后
// 若当前位置为空棋
try {
if (map[y][x] == NULL) {
// 若能移动
if (canMove(y, x)) {这里就move,注意要给对面发一下move信息}
} else if (map[y][x] != -2 && map[y][x] != -3) {
// 若当前的位置不为空棋
// 获取当前的棋编号
// 若当前的棋为我方棋时,则把第一次选择的棋替换为当前棋
if (allChess[pos].getPlayer() == player) {
firstSelect = allChess[pos];
updateOnUI();
} else {
// 是否可以移动
if (canMove(y, x)) {
// 将第一次选择的棋编号置空
这里就eat对面的棋,和move差不多,给对面多发一个eat哪个的信息
(还有个就是eat了带boost的棋子的话,对面也需要知道,但不需要给对面发这个信息,对面自己检查被吃掉的有没有boost就行)
}
}
}
} catch (ArrayIndexOutOfBoundsException e) {
// 若超出棋盘边界则检查是不是进家里了
就是检查可不可以进对面家里,可以的话就给对面发getin信息,然后注意这个棋子需要置空,然后对面家里画个贴图(自己能看到link还是virus,对面看还是看不出来是啥)。}
4.2运行结果
一台服务器,一台客户端,两台机子连接一下:
华为手机:
虚拟机:
激烈战斗:
运行视频
4.3发生的故障和问题
(由于bug多到记不清楚了,就随便回忆几个出来写了)
问题1:两边信息不同步,比如我吃了对面的棋子,我自己更新了link和virus的得分数目,但是对面有时候会更新,有时候不更新。
问题1解决:我本来写那个eat,我是先move然后再eat,用sendMes函数连续发了move和eat两个请求。由于间隔时间太短了,接收的线程接收有问题,有时候两个信息都接收到了,有时候有其中一个没有接收到。改一下eat请求,使其包含move的内容,去掉move只剩下eat,然后就正常了。
问题2:我带有boost的棋子进对面的家里,然后新增一个boost给一个新棋子,直接闪退,报错说数组越界。
问题2解决:进对面家里的时候,没有重置boost_num(存储哪个棋子带有boost的变量),导致下一次给boost的时候,下面这段if代码就爆了。
// 获取当前的棋编号
int pos = map[y][x];
// 若当前的棋为我方棋时
if (allChess[pos].getPlayer() == player) {
skill = 0;
if (boost_num != -1) {
allChess[boost_num].setBoost(0);
}
allChess[pos].setBoost(1);
boost_num = pos;
// 轮到对方下
canPlay = false;
sendMes("boost|" + pos + "|" + y + "|" + x + "|");
}
解决方法就是getin的地方加个判断,还原一下boost_num
sendMes("getin|" + getin_link + "|" +
getin_virus + "|" +
firstSelect.getNum() + "|" +
firstSelect.getPosX() + "|" +
firstSelect.getPosY() + "|");
getin += 1;
getin_type = firstSelect.getImageId();
**if(allChess[map[firstSelect.getPosX()][firstSelect.getPosY()]].getBoost()==1){
boost_num=-1;
}**
问题3:boost的棋子可以走两格,可以直接越过一个棋子。
问题3解决:多写判断就好了,判断一下中间有没有棋子
// 可以斜着一格或者横竖一两格
if (Math.abs(c1 - c2) + Math.abs(r2 - r1) > 2) {
return false;
}
if(r1==r2) {
if (c1 - c2 == 2 && map[r1][c1 - 1] != NULL) {
return false;
}
if (c2 - c1 == 2 && map[r1][c1 + 1] != NULL) {
return false;
}
}
if(c1==c2) {
if (r1 - r2 == 2 && map[r1 - 1][c1] != NULL) {
return false;
}
if (r2 - r1 == 2 && map[r1 + 1][c1] != NULL) {
return false;
}
}
if(Math.abs(c1 - c2)==1&&Math.abs(r1 - r2)==1){
if(map[r2][c2]==-2||map[r2][c2]==-3){
return false;
}
}