CS106-lec0~3

文章发布时间:

最后更新时间:

文章总字数:
2.5k

预计阅读时间:
12 分钟

这一次的作业拖了整整一个月,不过读源码的能力上升了!

Lec 0 Debugger in Qt

在某行设置断点后,当程序以 Debug 模式执行,Qt 会弹出多个 Debug 界面
左下方call sack调用堆栈,表明当前所处函数体,点击函数名可跳至所点击函数位置(在编辑器中)
栈中的函数可能包含调用关系,灰色靠后的函数为程序自动调用,可忽略。

单步执行等 Debug 按钮位于调用堆栈上方,值的变化会随程序执行在右侧显示
两天没看课,检索了网络上的各路信息,决定认怂改做 CS106B 的作业,B 和 X 的作业画风差别也太大了,x 的作业也太瞧得起我了,Assignment1 完成就得 400+行代码,B 的作业就正常多了,大概。

Assignment1 Life Game

不行!就要做 x 的作业!在codeStepByStep里发现了 lifeGame 的拆分练习,现在网页里继续,然后分块搬到 Qt 的作业项目里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void gameOfLife(Grid<int>& board){
Grid<int> temp = board;
int width = board.width();
int height = board.height();
for (int i = 0; i < height; i++){
for (int j = 0; j < width; j++){
int count = -1; //besides the cell itself

for (int ni = -1; ni < 2; ni++){
for (int nj = -1; nj < 2; nj++){
int x = i + ni;
int y = j + nj;

if (x >= 0 && y >= 0 && x < height && y < width && board[x][y] == 1)
count++;
}
}

if (count <= 1 && board[i][j] == 1)
temp[i][j] = 0;
else if (count > 3 && board[i][j] == 1)
temp[i][j] = 0;
else if (count == 3 && board[i][j] == 0)
temp[i][j] = 1;
}
}

board = temp;

}

Assignment1 档案翻译
读 life-graphics.h 的接口注释
lifeGraphy.h
接下来是试着实现一个 3x3 的网格模拟,分析需求
_ 先实现 3x3 网格
_ TODO 初始化 Grid
_ TODO 单回合 cell 变化
_ TODO cell 年龄变化
_ TODO 输出结果并循环
_ TODO 显示图形
* TODO 退出

在 main 函数里实现
gameOfLife-codeStoS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
int main() {
LifeDisplay display;
display.setTitle("Game of Life");
welcome();


Grid<int> board = {
{0, 1, 1},
{0, 1, 0},
{1, 1, 0}
};
Grid<int> temp = board;
int age = 1;

while (age <= kMaxAge){
display.setDimensions(board.numRows(), board.numCols());

for (int i = 0; i < board.numRows(); i++){
for (int j = 0; j < board.numCols(); j++){
int count = 0;

for (int ni = -1; ni < 2; ni++){
for (int nj = -1; nj < 2; nj++){
int x = i + ni;
int y = j + nj;
if (x < 0 || x >= board.numRows() || y < 0 || y >= board.numCols())
continue;
count += board[x][y];

}
}
printf("%d, %d has %d neibours\n", i, j, count);
if (board[i][j] == 1)
display.drawCellAt(i, j, age);

count -= board[i][j];

if (count <= 1 && board[i][j] == 1)
temp[i][j] = 0;
else if (count > 3 && board[i][j] == 1)
temp[i][j] = 0;
else if (count == 3 && board[i][j] == 0)
temp[i][j] = 1;
}
}
display.printBoard();
display.repaint();

board = temp;
age++;
getLine("Hit [enter] to continue..... ");
}

getLine("Hit [enter] to end. ");
return 0;
}

接下来的工作:

  • 随机生成地图/读取地图文件
  • 地图稳定性检测 - 细胞数为 0 时退出?
  • cell 独立显示年龄 - 新建一个二维数组来存储
  • 添加年龄死亡判定?

试着把之前都放在 main 里的函数功能拆分到各个函数内,发现 display 无法正常工作了

使用 sstream 库逐次读取字符,别忘了前两位是 board 的大小信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/**
* 先实现3x3网格
* TODO 初始化Grid
* TODO 单回合cell变化
* TODO cell 年龄变化
* TODO 输出结果并循环
* TODO 显示图形
* TODO 退出
*/


/**
* 初始化Grid
* TODO 读取配置文件
* TODO 随机生成
*
* random
*/
Grid<int> initRandomBoard(){
/*
srand(time(0));
int rowRandom = rand() % 100;
int colRandom = rand() % 100;
Grid<int> board(rowRandom, colRandom);

int height = rowRandom;
int width = colRandom;

for (int i = 0; i < height; i++){
for (int j = 0; j < width; j++){
board[i][j] = rand() % 2;


}
}
*/

int height = randomInteger(3, 100);
int width = randomInteger(3, 100);
Grid<int> board(height, width);

for (int i = 0; i < height; i++){
for (int j = 0; j < width; j++){
board[i][j] = randomInteger(0, 1);
}
}

return board;
}

//读取地图文件
Grid<int> initfBoard(){


ifstream input;
input.open("../res/files/Fish");
if (!input){
printf("打开文件失败,使用随机地图!\n");
return initRandomBoard();
}

int height; int width;

input >> height;
input >> width;

Grid<int> board(height, width);
string cell;

int i = 0;int j = 0;
while (input >> cell){
if (cell == "-"){
board[i][j] = 0;
}
else if (cell == "X"){
board[i][j] = 1;
}
}

input.close();
return board;
}

//return age integer
int displayAge(int i, int j, Grid<int> &ageboard){

return ageboard[i][j];
}

//稳定性检验-放在每次循环board更新之前
bool stableCheck(Grid<int> &board){
int cellLeft = 0;
for (int i = 0; i < board.numRows(); i++){
for (int j = 0; j < board.numCols(); j++){

cellLeft += board[i][j];
}
}
if (cellLeft == 0){
printf("细胞全部死亡,模拟结束。\n");
return false;
}
return true;
}


/**
* cell 0/1逻辑
*/
void reboard(Grid<int> &board, Grid<int> &ageBoard){
Grid<int> temp = board;
for (int i = 0; i < board.numRows(); i++){
for (int j = 0; j < board.numCols(); j++){
int count = 0;

for (int ni = -1; ni < 2; ni++){
for (int nj = -1; nj < 2; nj++){
int x = i + ni;
int y = j + nj;
if (x < 0 || x >= board.numRows() || y < 0 || y >= board.numCols())
continue;
count += board[x][y];

ageBoard[i][j]++;

}
}
//printf("%d, %d has %d neibours\n", i, j, count);

count -= board[i][j];

if (count <= 1 && board[i][j] == 1)
temp[i][j] = 0;
else if (count > 3 && board[i][j] == 1)
temp[i][j] = 0;
else if (count == 3 && board[i][j] == 0)
temp[i][j] = 1;
}
}
//display.printBoard();
//display.repaint();

board = temp;
}

【已编辑】部分代码实现,正确性未测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

/**
* Function: main
* --------------
* Provides the entry point of the entire program.
*/
int main() {
LifeDisplay display;
display.setTitle("Game of Life");
welcome();

//Where game map from
Grid<int> board = initRandomBoard();
//Grid<int> board = initfBoard();
Grid<int> ageboard = initAgeboard(board);

display.setDimensions(board.numRows(), board.numCols());
int age = 0;

while (true){


for (int i = 0; i < board.numRows(); i++){
for (int j = 0; j < board.numCols(); j++){
age = displayAge(i, j, ageboard);
display.drawCellAt(i, j, age);
display.repaint();
}
}



stableCheck(board);

reboard(board, ageboard);

//temp func call
//display.printBoard();

//_sleep(1000);
//getLine("Hit [enter] to continue..... ");
}

getLine("Hit [enter] to end. ");
return 0;
}

以上代码直接导致程序卡死,又考虑到项目程序被拆的七零八落,遂重写,过程中注意到一些问题:
细胞死亡后应重置其对应位置的年龄信息

通过重读 graph 函数接口注释,发现 set 函数需要放进 while 内部,每回合刷新
另外,对于新生细胞无法绘制且 3x3 测试地图中心细胞不死亡且年龄正常增长问题,发现地图信息中的新细胞出现了,但没有绘制,没有 age 信息
进一步发现在更新 age 参数时使用的是与其一同更新的 board 数组,但其值在函数末端才更新,临时变化都存储在 temp 数组中,将 age++的执行条件由board == 1改为 temp == 1即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
Grid<int> initRandomBoard(){
Grid<int> board = {
{0, 1, 0},
{0, 1, 0},
{0, 1, 0}
};

return board;
}

Grid<int> initAgeBoard(Grid<int> &board){
Grid<int> ageBoard(board.numRows(), board.numCols());

for (int i = 0; i < board.numRows(); i++){
for (int j = 0; j < board.numCols(); j++){
if (board[i][j] == 1){
ageBoard[i][j] = 1;
} else {
ageBoard[i][j] = 0;
}
}
}

return ageBoard;
}

int getAge(int i, int j, Grid<int> &ageBoard){
return ageBoard[i][j];
}

void next(Grid<int> &board, Grid<int> &ageBoard){
Grid<int> temp = board;
Grid<int> tempAge = ageBoard;
for (int i = 0; i < board.numRows(); i++){
for (int j = 0; j < board.numCols(); j++){
int count = 0;

for (int ni = -1; ni < 2; ni++){
for (int nj = -1; nj < 2; nj++){
int x = i + ni;
int y = j + nj;
if (x < 0 || x >= board.numRows() || y < 0 || y >= board.numCols())
continue;
count += board[x][y];

}
}
count -= board[i][j];

if (count <= 1 && board[i][j] == 1)
temp[i][j] = 0;
else if (count > 3 && board[i][j] == 1)
temp[i][j] = 0;
else if (count == 3 && board[i][j] == 0)
temp[i][j] = 1;


if (temp[i][j] == 1){
tempAge[i][j]++;
}
else if (temp[i][j] == 0){
tempAge[i][j] = 0;
}
}
}
board = temp;
ageBoard = tempAge;
}


/**
* Function: main
* --------------
* Provides the entry point of the entire program.
*/
int main(){
LifeDisplay display;
display.setTitle("Game of Life");
welcome();

Grid<int> board = initRandomBoard();
int height = board.numRows();
int width = board.numCols();

Grid<int> ageboard = initAgeBoard(board);

while (stableCheck(board)){
display.setDimensions(height, width);

for (int i = 0; i < height; i++){
for (int j = 0; j < width; j++){
if (board[i][j] == 1){
int age = getAge(i, j, ageboard);

display.repaint();
display.drawCellAt(i, j, age);
}
}
}
display.printBoard();
next(board, ageboard);

getLine("Hit [enter] to continue.... ");
}
getLine("Hit [enter] to end. ");
return 0;
}

接下来的任务:

  • 导入地图文件
  • 稳定性检验

不过说实话,折腾这么久,其实一直都在写我已经很熟悉了的东西,然而关于文件读写其实一直都没什么进展,这会是接下来的关键部分。不过做这个的时候确实学会去读接口注释,库文件了。

简单读取文本文件

1
2
3
4
5
6
7
ifstream input;
input.open("./res/files/Fish");
string line;
while (getline(input, line)){
cout << line << endl;
}
input.close();

简单研究了一下,其实不难
用 getline 读取的时候注意头为#的要当成注释忽略掉
然后读取配置每行的 cell 时依然是套两层循环,i,j 都读出来了,用 stoi 函数处理一下字符串型的高,宽值
注意别把 getline 放到第二层循环里去了,不然没两下就跑去读下一行的内容了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Grid<int> initFileBoard(){
ifstream input;
input.open("./res/files/Fish");

string height;
string width;
string line;

//排除注释
do{
getline(input, height);
getline(input, width);
}while (height[0] == '#' && width[0] == '#');

int h = stoi(height);
int w = stoi(width);
Grid<int> fboard(h, w);

for (int i = 0; i < h; i++){
getline(input, line);

for (int j = 0; j < w; j++){
if (line[j] == '-'){
fboard[i][j] = 0;
} else if (line[j] == 'X'){
fboard[i][j] = 1;
}
}
}

input.close();
return fboard;
}

至此基本上本作业的难点都做完了,拖了一个月真是不好意思
接下来就是用户选择文件或随机,没什么难度。
要加快进度了,做完稳定性检测就开下一题。