注:本文中的week对应课程时间安排。

week1

5.15

学习了java的基本语法和结构。java是一门面向对象语言。程序文件都是由一个个类构成,代码必须在函数内执行。
python需要区别语法是,它以大括号作为分隔,并且每行语句结束需要;,其实大部分语法和cpp比较接近。对于变量都要声明类型。
hello world为例:

1
2
3
4
5
public class hello_world{
public static void main(String[] args){
System.out.println("hello world");
}
}

另外对于方法而言,Java 允许我们定义两种类型方法。

  1. static,静态方法,由类本身执行的操作。

    x = Math.sqrt(100);

  2. 非静态方法,实例方法,只能由类的特定实例执行。

    Math m = new Math();
    x = m.sqrt(100);


week2

5.17

主要学习了测试的方法,还有一些细节。
java中不可以滥用!=,因为该运算符表示的是内存地址不同,如果内容相同但是内存地址不同那么是符合这个运算符的。如果比较内容,可以使用equal()方法。
在测试中,可以使用JUnit来简化。但是需要注意的是JUnit 要求测试方法必须是 public、非 static、无参数的方法且带有 @Test 注解。这一章节只是看了看 slides。没有仔细学。

5.18

Lists

如果一个变量不是基本类型,那么它就是引用类型。当我们声明对象变量时,我们使用引用类型变量来存储对象在内存中的位置。而对于基本类型和方法,Java 采用的是pass-by-value
eg.

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
Walrus walrus = new Walrus(3500, 10.5);
int x = 9;
doStuff(walrus, x);
System.out.println(walrus);
System.out.println(x);
}
public static void doStuff(Walrus W, int x) {
W.weight = W.weight - 100;

x = x - 5;

在这段代码中,W 是引用类型,所以在doStuff方法中,与主方法指向用一个内存地址。而x 呢?x 作为基本类型,在doStuff方法中,本质是对原来主方法x的二进制位的复制。x = 9,在传入 doStuff(walrus, x) 时,是将 9 的拷贝传给 doStuff。在 doStuff 中修改的是那个拷贝的 x,对 main 中的 x 没有影响。

IntLists

使用链表构造了一个列表,对于列表长度,则使用递归来解决。当然也可以使用迭代,这里就不写了。 这里还要求构造一个获取i 索引元素的方法。使用了一个挺妙的递归:基线条件是索引为0,返回首位。否则向剩余列表获取索引为i-1的元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class IntList {
public int first;
public IntList rest;
public IntList(int f, IntList r) {
first = f;
rest = r;
}
public int size(){
if (rest == null){
return 1;
}
return 1 + this.rest.size(); // this 指的是“正在调用这个方法或访问这个变量的那个对象”。
}
public int get(int i){
if (i==0){
return first;
}
return rest.get(i-1);
}
public static void main(String[] args) {
IntList l = new IntList(15,null);
}
}

另外课后还布置了额外的练习。

5.19

今天先把昨天剩下的列表练习做了。根据UCB的课程要求,代码不能公开。先把列表部分停一停,做做proj0的2048。难绷的是,2048的tilt机翻成“倾斜”,其实是向某个方向滑动所有方块的意思。

5.23

先完成proj0 的前几个方法,对于tilt,只需要完成一个方向,其余的则使用board.setViewingPerspective(side)。算法思想是分两步走,移动+合并+移动。对于移动的实现,从上到下,对每个pos 一一确认。从顶部开始,如果非空,则移动到pos ,然后一个pos 就确认好了。如果为空,那么当前的pos 就没有被确认。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private boolean move_up(int col,int top){  
boolean moved = false;
int pos = top;
for (int row = top; row >= 0; row--) {
Tile t = board.tile(col, row);
if (t != null){
if (row != pos){
board.move(col,pos,t);
moved = true;
}
pos--;
}
}
return moved;
}

5.24

对于proj0 的合并部分,使用一个变量存储上一次的合并情况。然后一行行遍历列,如果上一次合并了,说明这次所在的行是上一次合并前的方块所在的行,所以跳过,并且重新记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private boolean merge(int col) {  
boolean merged = false;
int top = board.size() - 1;
boolean lastMerged = false; // 标记上一个 Tile 是否已合并

for (int row = top; row > 0; row--) {
if (lastMerged) {
lastMerged = false;
continue;
}

Tile current = board.tile(col, row);
Tile below = board.tile(col, row - 1);

if (current != null && below != null && current.value() == below.value()) {
board.move(col, row, below); // 合并到当前位置
score += below.value() * 2;
merged = true;
lastMerged = true; // 跳过下一个 Tile }
}
return merged;
}

因为我们的合并只是移动了below,所以方块没有到达最终位置,需要再进行一次移动。这次的移动后就不需要合并了,这是游戏的规则。
最后把两个方法集中到tilt 汇总。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public boolean tilt(Side side) {  
boolean changed;
changed = false;
board.setViewingPerspective(side);
int top = board.size() - 1;
for (int col = 0; col < board.size(); col++) { //movement
boolean moved1 = move_up(col,top);
boolean merged = merge(col);
boolean moved2 = move_up(col,top);

if (moved1 || merged || moved2){
changed = true;
}

}
board.setViewingPerspective(Side.NORTH);
checkGameOver();
if (changed) {
setChanged();
}
return changed;