計程三口試-掃地機器人

  1. 1. 第1~5題
  2. 2. 第6~9題
  3. 3. 第10題
  4. 4. 第11題
  5. 5. 後記

口試,是通往計程四之前的最後一戰,採線上的方式進行,預約制,我預約的時間為4/23 10:00,前一天想當然的興奮到差點睡不著覺。到了當天,8:00起床,內心還是十分緊張,不斷重複確認自己的程式碼,為的就是被問問題時能夠流暢的回答出來。到了9:55,教授已經開啟了google meet,戰場就在前方,現在的我,就是一名戰士。到了10:05,口試結束,沒想到教授問的問題比我想像中的少,就大概確認一下這程式是你寫的而已,安全下庄。

總共有11題,前面9題都是為了引導你做出第10題的,最後一題則是加分用,那麼就開始吧。

第1~5題

建立一個名為Robot的class,再加上題目指定的變數即可,注意其皆為private。

C++
1
2
3
4
5
6
7
8
9
10
11
class Robot{
private:
int x;
int y;
int chargingX;
int chargingY;
int power;
int maxPower;
int targetX;
int targetY;
};

在Robot類別內,建立一個沒有參數的建構子,為變數賦值。

C++
1
2
3
4
class Robot{
public:
Robot(): x(0), y(0), chargingX(0), chargingY(0), power(100), maxPower(100), targetX(0), targetY(0){}
};

在Robot類別內,建立一個有參數的建構子,設定目前位置為傳入的值。

C++
1
2
3
4
class Robot{
public:
Robot(int a, int b): x(a), y(b), chargingX(0), chargingY(0), power(100), maxPower(100), targetX(0), targetY(0){}
};

在Robot類別內,建立題目指定之methods,使用public使其可以從外部設定與讀取。顯示資料的部分則可以利用gotoxy()函式(定義先省略),在指定位置輸出。這邊我另外加上了PowerPro(),用來回傳電量比例。

C++
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
class Robot{
public:
void SetLocation(int a, int b){
x = a;
y = b;
}
void SetChargingLocation(int cx, int cy){
chargingX = cx;
chargingY = cy;
}
void SetPower(int p){
power = p;
}
void SetMaxPower(int p){
maxPower = p;
}
double PowerPro(){
return (double)power/(double)maxPower*100.0;
}
void SetTargetLocation(int tx, int ty){
targetX = tx;
targetY = ty;
}
void Status(){
gotoxy(0, 16);
cout << "Location = (" << x << "," << y << ") ";
gotoxy(0, 17);
cout << "Charging Location = (" << chargingX << "," << chargingY << ") ";
gotoxy(0, 18);
cout << "Power = ";
cout << fixed << setprecision(2) << PowerPro() << "% ";
gotoxy(0, 19);
cout << "Target Location = (" << targetX << "," << targetY << ") ";
gotoxy(0, 20);
}
};

新增一個移動的method,根據目前位置和目的位置,對x, y做比較,不斷更新目前位置,直到其到達目的地。

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Robot{
public:
void MoveTo(){
if(x == targetX && y == targetY) return;
gotoxy(x, y);
cout << " ";
if(x < targetX) ++x;
else if(x > targetX) --x;
if(y < targetY) ++y;
else if(y > targetY) --y;
gotoxy(x, y);
cout << "R";
}
};

接著建立一個Robot型態無參數的物件robot1,利用之前所寫的method設定目前位置與目的位置,就可以開始移動了。

HideCursor()函數用來隱藏光標。

同上一題,只是變成有參數。

照著題目走即可。

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main(){
Robot *robot1 = new Robot();
Robot *robot2 = new Robot(20, 20);

robot1->SetLocation(0, 0);
robot1->SetChargingLocation(10, 10);
robot1->SetPower(150);
robot1->SetMaxPower(150);
robot1->SetTargetLocation(20, 20);

robot2->SetLocation(50, 30);
robot2->SetChargingLocation(10, 10);
robot2->SetPower(20);
robot2->SetMaxPower(100);
robot2->SetTargetLocation(15, 15);

return 0;
}

用一個迴圈讓兩部機器人輪流移動,利用Sleep()函數製造延遲,不然會機器人順移,狀態就先不顯示了。

到目前為止的程式碼如下

C++
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
#include <bits/stdc++.h>
#include <windows.h>
using namespace std;

void gotoxy(int x, int y){
COORD pos;
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);// 獲取標準輸出裝置控制代碼
pos.X = x, pos.Y = y;
SetConsoleCursorPosition(hOut, pos);//兩個引數分別是指定哪個窗體,具體位置
}

void HideCursor(){
CONSOLE_CURSOR_INFO cursor_info = {1, 0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

class Robot{
private:
int x;
int y;
int chargingX;
int chargingY;
int power;
int maxPower;
int targetX;
int targetY;
public:
Robot(): x(0), y(0), chargingX(0), chargingY(0), power(100), maxPower(100), targetX(0), targetY(0){}
Robot(int a, int b): x(a), y(b), chargingX(0), chargingY(0), power(100), maxPower(100), targetX(0), targetY(0){}
void SetLocation(int a, int b){
x = a;
y = b;
}
void SetChargingLocation(int cx, int cy){
chargingX = cx;
chargingY = cy;
}
void SetPower(int p){
power = p;
}
void SetMaxPower(int p){
maxPower = p;
}
double PowerPro(){
return (double)power/(double)maxPower*100.0;
}
void SetTargetLocation(int tx, int ty){
targetX = tx;
targetY = ty;
}
void Status(){
gotoxy(0, 16);
cout << "Location = (" << x << "," << y << ") ";
gotoxy(0, 17);
cout << "Charging Location = (" << chargingX << "," << chargingY << ") ";
gotoxy(0, 18);
cout << "Power = ";
cout << fixed << setprecision(2) << PowerPro() << "% ";
gotoxy(0, 19);
cout << "Target Location = (" << targetX << "," << targetY << ") ";
gotoxy(0, 20);
}
void MoveTo(){
if(x == targetX && y == targetY) return;
gotoxy(x, y);
cout << " ";
if(x < targetX) ++x;
else if(x > targetX) --x;
if(y < targetY) ++y;
else if(y > targetY) --y;
gotoxy(x, y);
cout << "R";
}
};

int main(){
HideCursor();
Robot *robot1 = new Robot();
Robot *robot2 = new Robot(20, 20);

robot1->SetLocation(0, 0);
robot1->SetChargingLocation(10, 10);
robot1->SetPower(150);
robot1->SetMaxPower(150);
robot1->SetTargetLocation(20, 20);

robot2->SetLocation(50, 30);
robot2->SetChargingLocation(10, 10);
robot2->SetPower(20);
robot2->SetMaxPower(100);
robot2->SetTargetLocation(15, 15);
while(true){
robot1->MoveTo();
robot2->MoveTo();
Sleep(200);
}
return 0;
}

目前成果如下

第6~9題

在移動的method中,根據電量比例做判斷,若低於10%,則將目標設為充電站位置,若否,則繼續前進。

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void MoveTo(){
if(x == targetX && y == targetY) return;
if(PowerPro() >= 10){
power -= 5;
gotoxy(x, y);
cout << " ";
if(x < targetX) ++x;
else if(x > targetX) --x;
if(y < targetY) ++y;
else if(y > targetY) --y;
gotoxy(x, y);
cout << "R";
}
else {
gotoxy(x, y);
cout << " ";
if(x < chargingX) ++x;
else if(x > chargingX) --x;
if(y < chargingY) ++y;
else if(y > chargingY) --y;
gotoxy(x, y);
cout << "R";
}
}

照著題目走即可,這邊我建立一個CapacityPro(),方便取得容量比例。

需注意Status()呼叫的是父類別的Status(),以免造成遞迴,SetTargetLocation()也是。

C++
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
class SweeperRobot:public Robot{
private:
int capacity;
int maxCapacity;
public:
SweeperRobot():Robot(){}
SweeperRobot(int a, int b, int ca, int cb, int n):Robot(a, b, ca, cb){
capacity = 0;
maxCapacity = n;
}
void SetCapacity(int c){
capacity = c;
}
void SetMaxCapacity(int c){
maxCapacity = c;
}
void SetTargetLocation(int tx, int ty){
Robot::SetTargetLocation(tx, ty);
}
double CapacityPro(){
return (1.0-(double)capacity/(double)maxCapacity)*100.0;
}
void Status(){
Robot::Status();
cout << "Capacity = ";
cout << fixed << setprecision(2) << CapacityPro() << "% ";
}
};

多了一個容量要判斷,這邊由於我實現移動的方式都寫在父類別,但是容量是屬於子類別的member,因此需在子類別的MoveTo()中呼叫父類別的MoveTo()時,傳入容量比例,也就是前面所寫的CapacityPro()。

C++
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
class Robot{
public:
void MoveTo(double CapacityPro){
if(x == targetX && y == targetY) return;
if(PowerPro() >= 10 && CapacityPro >= 5){
power -= 5;
gotoxy(x, y);
cout << " ";
if(x < targetX) ++x;
else if(x > targetX) --x;
if(y < targetY) ++y;
else if(y > targetY) --y;
gotoxy(x, y);
cout << "R";
}
else {
gotoxy(x, y);
cout << " ";
if(x < chargingX) ++x;
else if(x > chargingX) --x;
if(y < chargingY) ++y;
else if(y > chargingY) --y;
gotoxy(x, y);
cout << "R";
}
}
};
class SweeperRobot:public Robot{
private:
int capacity;
int maxCapacity;
public:
void MoveTo(){
Robot::MoveTo(CapacityPro());
}
};

第10題

前面的1~9題就是為了這題而生的,不過這裡需具現化一個垃圾的物件,內容很簡單,有x, y 座標,並且在建構的時候隨機生成在螢幕上即可。

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Garbage{
private:
int x;
int y;
public:
int GetX(){
return x;
}
int GetY(){
return y;
}
Garbage(){
x = rand() % width;
y = rand() % height;
show();
}
void show(){
gotoxy(x, y);
cout << "G";
}
};

垃圾是每秒生成一個,所以勢必要有一個容器來儲存,這裡我使用了queue,根據其「先進先出」的特性,便可將機器人目標設為最早生成的垃圾,若清除成功,則將垃圾從queue中移除,不斷重複,若場上沒垃圾則會停在上一次清除垃圾的地方。

C++
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
int main(){
int time = 0;
queue<Garbage*> q;
Garbage * gar = new Garbage();
q.push(gar);
Garbage *temp = q.front();

while(true){
Sleep(20);
++time;
if(!q.empty()){
temp = q.front();
srobot->SetTargetLocation(temp->GetX(), temp->GetY());
if(srobot->clearSuc()){ //clearSuc()用來判斷機器人是否走到垃圾位置
q.pop();
delete temp;
}
}
if(time % 50 == 0){ //每過1秒生成一個垃圾
Garbage * gar = new Garbage();
q.push(gar);
}
srobot->MoveTo();
}
return 0;
}

根據題目要求,回到充電站不能同時進行充電與清空垃圾,因次需額外判斷此次回充電站之目的為何,這邊我在父類別的MoveTo()設了一個bool型態的回傳值,若為true,代表目的為清空垃圾,回到子類別中實現,完整程式碼如下

C++
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#include <bits/stdc++.h>
#include <windows.h>
using namespace std;

#define width 80
#define height 15

void gotoxy(int x, int y){
COORD pos;
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);// 獲取標準輸出裝置控制代碼
pos.X = x, pos.Y = y;
SetConsoleCursorPosition(hOut, pos);//兩個引數分別是指定哪個窗體,具體位置
}

void HideCursor(){
CONSOLE_CURSOR_INFO cursor_info = {1, 0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

class Robot{
private:
int x;
int y;
int chargingX;
int chargingY;
int power;
int maxPower;
int targetX;
int targetY;
public:
Robot(): x(0), y(0), chargingX(0), chargingY(0), power(100), maxPower(100), targetX(0), targetY(0){}
Robot(int a, int b): x(a), y(b), chargingX(0), chargingY(0), power(100), maxPower(100), targetX(0), targetY(0){}
Robot(int a, int b, int ca, int cb): x(a), y(b), chargingX(ca), chargingY(cb), power(100), maxPower(100), targetX(0), targetY(0){}
void SetLocation(int a, int b){
x = a;
y = b;
}
void SetChargingLocation(int cx, int cy){
chargingX = cx;
chargingY = cy;
}
void SetPower(int p){
power = p;
}
void SetMaxPower(int p){
maxPower = p;
}
double PowerPro(){
return (double)power/(double)maxPower*100.0;
}
void SetTargetLocation(int tx, int ty){
targetX = tx;
targetY = ty;
}
bool clearSuc(){
if(x == targetX && y == targetY) return true;
return false;
}
void Status(){
gotoxy(0, 16);
cout << "Location = (" << x << "," << y << ") ";
gotoxy(0, 17);
cout << "Charging Location = (" << chargingX << "," << chargingY << ") ";
gotoxy(0, 18);
cout << "Power = ";
cout << fixed << setprecision(2) << PowerPro() << "% ";
gotoxy(0, 19);
cout << "Target Location = (" << targetX << "," << targetY << ") ";
gotoxy(0, 20);
}
bool MoveTo(double CapacityPro){
if(x == targetX && y == targetY) return false;
if(PowerPro() >= 10 && CapacityPro >= 5){
power -= 5;
gotoxy(x, y);
cout << " ";
if(x < targetX) ++x;
else if(x > targetX) --x;
if(y < targetY) ++y;
else if(y > targetY) --y;
gotoxy(x, y);
cout << "R";
}
else {
gotoxy(x, y);
cout << " ";
if(x < chargingX) ++x;
else if(x > chargingX) --x;
if(y < chargingY) ++y;
else if(y > chargingY) --y;
gotoxy(x, y);
cout << "R";
}
if(x == chargingX && y == chargingY){
if(PowerPro() < 10) power = maxPower;
else return true;
}
return false;
}
};

class SweeperRobot: public Robot{
private:
int capacity;
int maxCapacity;
public:
SweeperRobot():Robot(){}
SweeperRobot(int a, int b, int ca, int cb, int n):Robot(a, b, ca, cb){
capacity = 0;
maxCapacity = n;
}
void SetCapacity(int c){
capacity = c;
}
void SetMaxCapacity(int c){
maxCapacity = c;
}
void SetTargetLocation(int tx, int ty){
Robot::SetTargetLocation(tx, ty);
}
double CapacityPro(){
return (1.0-(double)capacity/(double)maxCapacity)*100.0;
}
void MoveTo(int mode = 0){
bool clearCapacity = Robot::MoveTo(CapacityPro());
if(clearCapacity) capacity = 0;
Status();
}
void Status(){
Robot::Status();
cout << "Capacity = ";
cout << fixed << setprecision(2) << CapacityPro() << "% ";
}
bool clearSuc(){
if(Robot::clearSuc()){
capacity += 5;
return true;
}
return false;
}
};

class Garbage{
private:
int x;
int y;
public:
int GetX(){
return x;
}
int GetY(){
return y;
}
Garbage(){
x = rand() % width;
y = rand() % height;
show();
}
void show(){
gotoxy(x, y);
cout << "G";
}
};

int main(){
srand(time(NULL));
HideCursor();

SweeperRobot *srobot = new SweeperRobot(0, 14, 0, 14, 100);
srobot->SetPower(100);
srobot->SetMaxPower(1000);
srobot->SetTargetLocation(50, 10);

int time = 0;
queue<Garbage*> q;
Garbage * gar = new Garbage();
q.push(gar);
Garbage *temp = q.front();

while(true){
Sleep(20);
++time;
if(!q.empty()){
temp = q.front();
srobot->SetTargetLocation(temp->GetX(), temp->GetY());
if(srobot->clearSuc()){
q.pop();
delete temp;
}
}
if(time % 50 == 0){
Garbage * gar = new Garbage();
q.push(gar);
}
srobot->MoveTo();
}
return 0;
}

執行結果

第11題

就是將本來的電力低於10%改成確保可回到充電站就好,並且前往充電站途中會耗電3。我的想法是先找出當前機器人與充電站之間的最短移動距離,並判斷若移動後所剩餘的電量是否足夠讓其走回充電站,若足夠,則繼續朝向垃圾前進,若否,則前往充電站。程式碼懶得打了= =

後記

以上就是全部的題目,感謝各位的收看,打完這篇文章的時候我已經成功晉級到計程四了,可喜可賀阿,前方究竟還有什麼挑戰在等著我呢?我也不知道,想想就刺激。