北京电子科技学院(BESTI)
实 验 报 告
课程:程序设计与数据结构
班级: 1623
姓名: 石亚鑫
学号:20162303
成绩: 2分
指导教师:娄嘉鹏 王志强
实验日期:12月15日
实验密级: 非密级
预习程度: 已预习
实验时间:10:00-12:00
必修/选修: 必修
实验序号: cs_03
实验内容
实验 分析系统架构
首先分析一下各部分代码
card类
card是用来显示2048游戏中的数字卡片,首先设定的是card的背景
/*
* LayoutParams类也只是简单的描述了宽高,宽和高都可以设置成三种值:
1,一个确定的值;
2,FILL_PARENT,即填满(和父容器一样大小);
3,WRAP_CONTENT,即包裹住组件就好。
*/
LayoutParams lp = null; //传递在子视图中布局的设计
background = new View(getContext()); //这个是Card的背景设计,是一个View
//-1代表LayoutParams.MATCH_PARENT,即该布局的尺寸将填满它的父控件;
//-2代表LayoutParams.WRAP_CONTENT,即该布局的尺寸将为其自身内容的尺寸;
lp = new LayoutParams(-1, -1);
lp.setMargins(10, 10, 0, 0); //设置子布局在父布局中的位置
background.setBackgroundColor(0x33ffffff); //33表示的是透明度 ffffff是白色
addView(background, lp); //向布局文件中添加一个子布局
label = new TextView(getContext());//在Card中有一个数字
label.setTextSize(20); //数字的大小
label.setGravity(Gravity.CENTER); //数字在Card中居中
lp = new LayoutParams(-1, -1); //控制数字在Card中width和height
lp.setMargins(10, 10, 0, 0); //控制数字在Card中的出现位置
addView(label, lp);
setNum(0); //初始化每一个card的时候都是0
}
然后根据卡片的数字决定卡片的背景颜色。
switch (num) {
case 0:
label.setBackgroundColor(0x00000000); //什么也没有的时候是黑色
break;
case 2:
label.setBackgroundColor(0xffeee4da);
break;
case 4:
label.setBackgroundColor(0xffede0c8);
break;
case 8:
label.setBackgroundColor(0xfff2b179);
break;
case 16:
label.setBackgroundColor(0xfff59563);
break;
case 32:
label.setBackgroundColor(0xfff67c5f);
break;
case 64:
label.setBackgroundColor(0xfff65e3b);
break;
case 128:
label.setBackgroundColor(0xffedcf72);
break;
case 256:
label.setBackgroundColor(0xffedcc61);
break;
case 512:
label.setBackgroundColor(0xffedc850);
break;
case 1024:
label.setBackgroundColor(0xffedc53f);
break;
case 2048:
label.setBackgroundColor(0xffedc22e);
break;
default:
label.setBackgroundColor(0xff3c3a32);
break;
Config类
这个类主要是定义2048的游戏模式,可以选择44或者是66的
GameView类
这部分代码定义向上下左右滑动,首先确定方向为手指最先触碰显示屏时确定一个坐标,手指离开显示屏的时候确定另一个坐标,两坐标之差来确定移动方向。在安卓手机设定中,左上角为原点,x轴正方向为向右,y轴正方向为向下,因此假设x轴的坐标差为负,就是向左滑
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: //如果是点击下来,获取点击地点的x和y的坐标
startX = event.getX();
startY = event.getY();
break;
case MotionEvent.ACTION_UP: //离开屏幕时的位置,获取离开屏幕时的位置,并获得位移量
offsetX = event.getX() - startX; //手指离开时的X坐标减去按下去时X的坐标
offsetY = event.getY() - startY; // 手指离开时 的Y坐标减去按下去时的Y的坐标
if (Math.abs(offsetX) > Math.abs(offsetY)) { //取offsetxX和offsetY的绝对值
if (offsetX < -5) {
swipeLeft(); //滑动向左
} else if (offsetX > 5) {
swipeRight(); //向右划
}
} else {
if (offsetY < -5) {
swipeUp(); //向上划
} else if (offsetY > 5) {
swipeDown(); //向下划
}
}
break;
}
return true; //这个地方如果是false的话,手指抬起时是不会得到坐标的
}
});
}
产生随机数,先确定整个界面有几个空格,通过Math.random()产生一个0-1的数,在乘以空格数,随机产生一个数来使对应卡片的数字换成2或者4,比例为9比1
private void addRandomNum() {
emptyPoints.clear();
for (int y = 0; y < Config.LINES; y++) {
for (int x = 0; x < Config.LINES; x++) {
if (cardsMap[x][y].getNum() <= 0) { //如果Card为空的话,则将这个card记录下来
emptyPoints.add(new Point(x, y));
}
}
}
if (emptyPoints.size() > 0) {
/*
* Math.random()会随机产生一个0-1之间的小数,假如emptyPoints.size()等于16
* (int) (Math.random() * emptyPoints.size())会产生一个0到16之间的数。
*
* */
Point p = emptyPoints.remove((int) (Math.random() * emptyPoints.size())); //从Card为0的卡片中随机移除一个
cardsMap[p.x][p.y].setNum(Math.random() > 0.1 ? 2 : 4); //被移除的card被2或者4替换,且出现2的几率更大一些
MainActivity.getMainActivity().getAnimLayer()
.createScaleTo1(cardsMap[p.x][p.y]); //给新生成的Card添加动画效果
}
}
定义滑动运算,选定一个不为零卡片如果左边为0,那将这个卡片与左边合并,同时左边的卡片数字由这个卡片替代,如果左边的卡片数字不为0,如果数字和这个卡片相等,那么左边卡片数字乘2,这个卡片设为0,如果不相等,不移动,依次遍历。其他方向等同向左滑动运算。
//滑动向左移动运算
private void swipeLeft() {
boolean merge = false;
for (int y = 0; y < Config.LINES; y++) { //LINES=4
for (int x = 0; x < Config.LINES; x++) {
for (int x1 = x + 1; x1 < Config.LINES; x1++) {//向左滑动时,将全部的数组遍历一遍,如果找到不为0的,且其左边为0时
if (cardsMap[x1][y].getNum() > 0) {
if (cardsMap[x][y].getNum() <= 0) { //cardsMap[x][y].getNum()获取card上面的数字
MainActivity
.getMainActivity()
.getAnimLayer()
.createMoveAnim(cardsMap[x1][y],
cardsMap[x][y], x1, x, y, y);
cardsMap[x][y].setNum(cardsMap[x1][y].getNum());
cardsMap[x1][y].setNum(0);
x--;
merge = true;
} else if (cardsMap[x][y].equals(cardsMap[x1][y])) {
MainActivity
.getMainActivity()
.getAnimLayer()
.createMoveAnim(cardsMap[x1][y],
cardsMap[x][y], x1, x, y, y); //使用这个方法之后,在效果上可以表示成移动了
cardsMap[x][y].setNum(cardsMap[x][y].getNum() * 2);//这个地方可以修改数字的增加原本是2
cardsMap[x1][y].setNum(0);
MainActivity.getMainActivity().addScore(
cardsMap[x][y].getNum());
merge = true;
}
break;
}
}
}
}
if (merge) {
addRandomNum();
checkComplete();
}
}
判断是否满,如果所有卡片的上下左右都没有和自己相同的卡片的数字时,游戏结束,跳出提示。
private void checkComplete() {
boolean complete = true;
ALL: for (int y = 0; y < Config.LINES; y++) {
for (int x = 0; x < Config.LINES; x++) {
/*
1.卡片的数字为0时
3.卡片的数字不为0时,x在0-3之间,且x卡片的数字和左右相邻的卡片的数字相同时
4.卡片的数字不为0时, 0<y<3,且y的卡片的数字和上下相邻的卡片的数字相同时
*/
if (cardsMap[x][y].getNum() == 0
|| (x > 0 && cardsMap[x][y].equals(cardsMap[x - 1][y]))
|| (x < Config.LINES - 1 && cardsMap[x][y]
.equals(cardsMap[x + 1][y]))
|| (y > 0 && cardsMap[x][y].equals(cardsMap[x][y - 1]))
|| (y < Config.LINES - 1 && cardsMap[x][y]
.equals(cardsMap[x][y + 1]))) {
complete = false;
break ALL; //跳出循环
}
}
}
if (complete) { //给出错误提示信息
new AlertDialog.Builder(getContext())
.setTitle(R.string.tishi)
.setMessage(R.string.falses)
.setPositiveButton(R.string.queding,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
startGame();
}
}).show();
}
}
AnimaLayer类
这个类主要是用来定义动画效果
/*
* ScaleAnimation(float fromX, float toX, float fromY, float toY,int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
*
*float fromX 动画起始时 X坐标上的伸缩尺寸
float toX 动画结束时 X坐标上的伸缩尺寸
float fromY 动画起始时Y坐标上的伸缩尺寸
float toY 动画结束时Y坐标上的伸缩尺寸
int pivotXType 动画在X轴相对于物件位置类型
float pivotXValue 动画相对于物件的X坐标的开始位置
int pivotYType 动画在Y轴相对于物件位置类型
float pivotYValue 动画相对于物件的Y坐标的开始位置
Animation.RELATIVE_TO_SELF为相对于自身
Animation.RELATIVE_TO_PARENT为相对于父控件
*
*/
public void createScaleTo1(Card target) {
ScaleAnimation sa = new ScaleAnimation(0.1f, 1, 0.1f, 1,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, //相对于Card自身的0.5f就是Card的中心
0.5f);
sa.setDuration(100); //持续时间为0.5s
target.setAnimation(null);
target.getLabel().startAnimation(sa); //给Card对应的TextView控件添加动画
}
实验一
1 git clone 小组项目
2 编译项目,提交编译成功截图(全屏,要有学号信息)
3 提交运行过程中的截图(全屏,要有学号信息)
实验二
- 在小组项目中,找一个合适的地方添加一个按钮,点击显示自己的学号
- 提交运行截图(全屏,要有学号信息)
- 在项目中找一个界面,自己复制一份命名为XXXbak,修改代码,替换原来的部分
- 提交运行截图(全屏,要有学号信息)
实验三
分析小组代码:
- 数据结构的应用情况及相关代码
- 排序算法的应用情况及相关代码
- 查找算法的应用情况及相关代码
- 完成实验报告
实验步骤
(1) 实验一
1.首先,新建一个名为20162303syxexp5的文件夹,在这个文件夹中打开git bash,然后输入命令行git clong + 项目地址。
成功clone:下面为截图。
2.编译运行
由于gradle的版本不一致,
可以看出来,拉下来的项目的gradle的版本是3.3,而我本地的是4.1
因为现场下载3.3版本的压缩包需要一定时间,所以我把拉下来的项目的gradle和.gradle文件夹换成自己本地的gradle和.gradle文件夹,还有一种解决方法就是把拉下来的项目的gradle-wrapper.properties中的最后一行的版本改成自己本地的版本,
路径见下图。
由此就可以正常的编译运行了:
(2) 实验二
1.在小组项目中,我选择在模式接受页面添加一个按钮,点击显示自己的学号,按钮文字设置为自己名字 ,给按钮设置监听器 ,点击按钮时发出一条Tost消息显示学号20162303
button3 = (Button)findViewById(R.id.syx);
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AlertDialog.Builder(moshijieshao.this)
.setTitle("syx")
.setMessage("20162303石亚鑫")
.setPositiveButton("确定", null)
.show();
}
});
- 在项目中模式介绍界面,自己复制一份命名为moshijieshaobak,修改代码,替换原来的部分
public class moshijieshaobak extends AppCompatActivity { private Button button1, button2, button3; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.moshijieshao); button3 = (Button)findViewById(R.id.syx); button3.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new AlertDialog.Builder(moshijieshaobak.this) .setTitle("syx") .setMessage("20162303石亚鑫") .setPositiveButton("确定", null) .show(); } }); } }
把原有的按钮去掉。
把AndroidManifest中标签改为20162303<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="20162303" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".WelcomActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".First_page"></activity> <activity android:name=".MainActivity"></activity> <activity android:name=".HardGameActivity"></activity> <activity android:name=".developer"></activity> <activity android:name=".moshijieshao"></activity> </application>
(3) 实验三
数据结构
card是用来显示2048游戏中的数字卡片,通过一个二维数组来设定,是每一个数字卡片的布局类
AnimLayer类是用来控制动画效果的类
GameView类,就是玩游戏的地方,它里面“装”了多个Card,还调用AnimaLayer类来实现动画效果
Config类,就是用来定义 44 还是 66
GameView类是自定义的LinerLayout,Card和GameView是自定义的Fragment
代码见博客开头
查找算法和排序算法
计划用二叉树查找和排序作出一个排行榜界面,但是目前还没有实现。