移动平台开发综合实践 安卓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;
}
}
移动平台开发综合实践 安卓android大作业-雷net在线对战的更多相关文章
- 【读书笔记】iOS-微信公众平台开发最佳实践
一,微信是由腾讯公司广州研发中心产品团队开发,该团队经理张小龙被称为“微信之父”,公司总裁马化腾确定该产品名称为“微信”. 二,常见问题及解决方案. 1,请求URL超时. 这种情况一般是由于服务器网速 ...
- 深入浅出 - Android系统移植与平台开发(十)- Android编译系统与定制Android平台系统(瘋耔修改篇二)
第四章.Android编译系统与定制Android平台系统 4.1Android编译系统 Android的源码由几十万个文件构成,这些文件之间有的相互依赖,有的又相互独立,它们按功能或类型又被放到不同 ...
- 深入浅出 - Android系统移植与平台开发(十一)- Android系统的定制(瘋耔修改篇一)
首先非常感谢原文作者为我们提供的知识库,因为有你们的贡献,我们的开发难度更显简单 原文 : http://blog.csdn.net/mr_raptor/article/details/30113 ...
- 深入浅出 - Android系统移植与平台开发(十三)- Android的对象管理
第六章.Android的对象管理 在Java中,不再使用的对象会通过gc机制来自己主动回收.而Android系统执行时库层代码是由C++编写的,在C++中创建的对象通常使用指针来操作,一旦使用不当.轻 ...
- 深入浅出 - Android系统移植与平台开发(九)- Android系统system_server及Home启动
3.3 Zygote守护进程与system_server进程 Android的执行环境和Java执行环境有着本质的差别,在Android系统中每一个应用程序都是一独立的进程,当一个进程死掉时,不会影响 ...
- 随笔:Oracle实验课(软件系统开发综合实践)B/S结构;java——图书管理系统
以上是我需要注意的要求 -------------------------------此处为放假分割线-1-20----------------------------------- 初步完成了整个程 ...
- Android大作业 --音乐播放器
1.项目成员(本次作业主要对上一次的音乐播放器进行完善) 韦家城 学号:1600802026 班级:161 博客:https://www.cnblogs.com/ln9969cc/ 邓乾尧 学号:1 ...
- Android大作业
1.项目成员 邓乾尧 学号:1600802005 班级:161 博客:http://www.cnblogs.com/2575590018dqy/ 韦家城 学号:1600802026 班级:161 ...
- Android和PHP开发最佳实践
Android和PHP开发最佳实践 <Android和PHP开发最佳实践>基本信息作者: 黄隽实丛书名: 移动应用开发技术丛书出版社:机械工业出版社ISBN:9787111410508上架 ...
- 最初程序员的思维“修炼”之四——Android平台开发的“强制关闭”解决思路
我和我的朋友参加一个比赛——物联网应用技能大赛,这个大赛所要求的技能有,硬件技术,Android平台开发技术,.NET平台开发技术,所以这是一个团队合作的比赛,基本上没有人能同时掌握这三种技术(在校生 ...
随机推荐
- 如何使用Flutter开发执行操作系统shell命令的工具
@charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...
- 使用 GitDiagram 快速将 GitHub 仓库转换为交互式图表
前言 当面对 GitHub 上文件目录错综复杂的新项目,且你急需快速了解其系统设计或架构流程时,你可能会感到束手无策.今天大姚给大家分享一个开源利器 GitDiagram,它可以轻松将任何复杂的 Gi ...
- 一个低PE的股票池--选股策略
EP因子即市盈率因子,常被投资者使用的几个估值因子之一.一般使用PE,即Price to Earning,维基百科上的解释:市盈率指每股市价除以每股收益盈利(Earning Per Share,EPS ...
- PC端自动化测试实战教程-6-pywinauto 打印和保存控件菜单树结构之ElementNotFoundError(详细教程)
1.简介 其实前边的文章宏哥已经在控制台打印过控件菜单树结构,只是没有将其保存到文件中.只需要一个方法即可.在pywinauto中可以使用 print_control_identifiers() 方法 ...
- 推荐一个Elasticsearch ES可视化客户端工具:ES-King
ES-King:开源免费,一个现代.实用的ES GUI客户端,支持多平台. 下载地址:https://github.com/Bronya0/ES-King 功能清单 详尽的集群信息:节点信息.堆内存占 ...
- Spring AOP 面向切面编程之AOP是什么
前言 软件工程有一个基本原则叫做"关注点分离"(Concern Separation),通俗的理解就是不同的问题交给不同的部分去解决,每部分专注于解决自己的问题.这年头互联网也 ...
- Xshell 详细安装与配置教程:从下载到高效使用
引言:为什么选择Xshell? 在当今云计算和远程办公时代,高效连接Linux服务器已成为开发者和运维人员的必备技能.Xshell作为业界领先的专业SSH客户端,凭借其卓越的性能.丰富的功能和直观的用 ...
- Web前端入门第 69 问:JavaScript Promise 提供的方法都使用过吗?
Promise 这个 API 曾在 JS 领域掀起过血雨腥风,以前的大佬们都喜欢手搓一个自己的 Promise 用以理解 Promise 的原理. Promise 的诞生,应该多少都有受到 jQuer ...
- 第k个数
第K个数 给定一个长度为 n 的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k 个数. 所用方法和基本原理 快速选择算法是基于快速排序思想的一种选择算法.其基本原理如下: ...
- 《机器人SLAM导航核心技术与实战》第1季:第12章_典型自主导航系统
<机器人SLAM导航核心技术与实战>第1季:第12章_典型自主导航系统 视频讲解 [第1季]12.第12章_典型自主导航系统-视频讲解 [第1季]12.1.第12章_典型自主导航系统_ro ...