<style>
.red{
color : #f15b6c;
}
.black{
color : 000000;
}
.blue{
color : #87ceeb;
}
</style>
# 泰坦號 🏄♂️

## 教學前的提醒
看不懂,聽不懂的部分可以直接提出,要改進也可以寫在留言區
著重目標在讓未學習過程式的同學,透過printf學習一些基礎的程式概念
## 新聞 📰
[新聞網址](https://tw.stock.yahoo.com/news/%E6%B3%B0%E5%9D%A6%E8%99%9F%E7%B6%93%E6%AD%B7-%E5%9E%82%E7%9B%B4%E4%B8%8B%E5%A2%9C71%E7%A7%92-%E5%B0%88%E5%AE%B6-5%E4%BA%BA%E6%84%8F%E8%AD%98%E5%88%B0%E5%8D%B3%E5%B0%87%E5%96%AA%E5%91%BD-073409567.html)

## 遊戲介紹
* ### 遊玩方式
利用鍵盤上的方向鍵,將一個指定字串(hello),移動到另個指定字串(,Titanic)的旁邊
成功後會再中間跳出success
* ### 畫面
左上角可移動字串,右下角目標字串、有2隻小魚隨著hello走,還有海草、海浪

## 前置⚙️
1. 在桌面新增資料夾

2. 資料夾名稱不可以是中文且中間不可以有空格

3. 開啟 VSCode 並開啟剛剛新增的資料夾


4. 選擇是,我信任作者

5. 點擊新增檔案

6. 取檔名且後面要是.c檔,按 enter 就可以新增檔案了

## **程式碼剖析**🔍
> 3個大綱 函式庫->背景->可移動字串 ,在三個大綱中再分細項去講解
1. ### 函式庫的引入
* ### code示範
如果只打上
```c=
int main() {
printf("hello,Titanic\n");
return 0;
}
```

系統會告訴你無法編譯與執行
那加上 printf() 所屬的標頭檔 <stdio.h> 再試試
```c=
#include<stdio.h>
int main() {
printf("hello,Titanic\n");
return 0;
}
```

發現可以正常執行了!!
因為在C語言中,當你使用某個函數(比如 printf )時,你需要在程式的開頭引入相應函式所屬的標頭檔(例如 <stdio.h>)。這樣做可以讓編譯器知道這是一個合法的函式,並且了解它的返回類型以及參數的類型。如果你沒有包括相應的標頭檔,程式在預處理的時候就找不到相對應函式,因此在後續編譯時編譯器會報錯
2. ### 加入背景(海水、海草、魚)
讓大家先體驗printf、for、函式基礎、while、_getch()
* #### 背景的設定 background()
* 製作海的方法
把海的樣,想像成 ~ 的延長版
- 第一種、可以透過一次 printf 大量的 \~\~\~\~\~\~\~ 來完成,但這樣程式碼會看起來不簡潔,所以我們這邊學習
- 第二種、for迴圈
* 體驗用一次 printf 完跟 for 迴圈的差異👉
```c=
#include<stdio.h>
int main() {
printf("~\n");//一個~
printf("~~~~~~~~~~~~~~~~~~~~\n");//20個~
for(int i=0;i<20;i++){//20個~
printf("~");
}
printf("\n");
return 0;
}
```
* **<span class ="red">Q :</span> [for迴圈](https://openhome.cc/Gossip/CGossip/forStatement.html)是甚麼?迴圈感覺就很難**
**<span class ="red">ANS :</span> 其實不會**
日後很常會用到,經過長期的練習,for迴圈就像呼吸一樣簡單理解
這邊先簡單講我們這邊的for再幹嘛
for()裡面通常會有兩個;三樣東西,但不是一定要三樣!!
但先以正常版去講解
```
for (初始變數; 判斷式; 遞增式) {
陳述句一;
陳述句二;
}
```
那以這個為例
```C=
for(int i = 0; i < 20; i++){
printf("~");
}
```
我們的初始變數是從 0 開始,那遞增式是 +1,判斷式是到第 20 次就要停
判斷的方式是,i = 0 開始,接著跑到判斷式看i有沒有小於20有的話
執行printf,再去做 i++ 的動作
* background() 示範code 👉
```c=
#include<stdio.h>
void background() {
int lim = 4;
for (int k = 0; k < 2; k++) {
for (int i = 0; i < lim * 9; i++)
printf("~");//輸出海浪
printf("\n");
}
for (int i = 0; i < lim*2; i++)
printf("\n"); //海底的樣
printf("\n");
for (int i = 0; i < lim*3; i++)
printf("\\|/");//輸出海草
printf("\n");
}
int main() {
background();
return 0;
}
```
* **完成方式**
基礎的 printf 出想要的畫面,像是海浪的部分,~ 用這個去思考,搭配上一個lim去限制我們 printf 出的次數,那中間我們就當作海底不加上東西,所以 \n 換行,一直到海草的部分 \\|/,用上述的相同概念完成
* [函式](https://openhome.cc/Gossip/CppGossip/FunctionABC.html)
可能有人發現前面的background <span class ="red">為什麼程式碼沒在main裡面</span>
理解:
main 就像主程式一樣,我們 run_code 時就會運作,但函式就像副程式一樣,我們不去呼叫他,就不會去運行

> 魚的部分有點多了,就跳過[color=#E1F1E7]
3. ### 基本畫面(讓 hello 跟 ,Titanic 能正常操作)
* ### draw 🖼
> 將指定的字串放在指定的位置上[color=#E1F1E7]
* #### 單純 printf 的作法
* 我們一個一個去 printf 出來
* 作法
```c=
#include<stdio.h>
int main() {
printf("\n\n\n\n\n\n\n");
printf("hello,Titanic");//游標此時會在hello,Titanic的後方
return 0;
}
```
* 不過這樣就會有一個問題點,就是無法回到起點去printf,因為游標在後方

* #### 首先運用 gotoxy 去將游標指向指定的 x,y
* #### 再來去 printf (該字串)
* #### 示範 code 👉
```c=
#include<stdio.h>
#include<windows.h>
#define TARGET ",Titanic"
#define MOVER "Hello"
void gotoxy(int x, int y) {
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
void draw(int y, int x, const char* str) {
gotoxy(x, y);
printf("%s", str);
}
int main() {
printf("\n\n\n\n\n\n\n");
draw(10, 8, MOVER);
draw(10, 13, TARGET);
return 0;
}
```

我們的輸出會在指定的y與x上,而非最左上角
> 值得注意的是 draw() 是先 y 再來 x 的喔 [color=#E1F1E7]
> 也運用到了前面所講的定義與標頭檔!!
* ### go to xy
>將游標移到,指定的 x,y [color=#E1F1E7]
1. `COORD`代表著 x,y 的座標

2. 因為上述的[SetConsoleCursorPosition](https://learn.microsoft.com/zh-tw/windows/console/setconsolecursorposition)跟[GetStdHandle](https://learn.microsoft.com/zh-tw/windows/console/getstdhandle)、[COORD](https://learn.microsoft.com/zh-tw/windows/console/coord-str)我們會運用到 `<windows.h>`
3. 示範code
``` c=
#include<stdio.h>
#include<windows.h>
void gotoxy(int x, int y) {
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
int main() {
printf("hello,Titanic\n");
gotoxy(15, 10);
printf("hello,Titanic\n");
return 0;
}
```

會發現到,第二次的 Hello,World 並非出現在第一次的正下方,而是在 x = 15, y = 10 的位置
* ### main
* #### **變數定義**
* `maxY`、`maxX`:定義整個邊界大小。
* `len_mover`、`len_target`:用於判斷這兩個字串的長度。
* `targetY`、`targetX`:判斷指定的答案位置。
* `moverY`、`moverX`:控制要移動的字串的位置。
<br/>
```c=
int maxY = 25, maxX = 80; // Assume a default console size
int len_mover = strlen(MOVER);
int len_target = strlen(TARGET);
int targetY = maxY - 1, targetX = maxX - len_target;
int moverY = 0, moverX = (maxX - len_mover) / 2;
int ch;
```
* 示範[strlen](https://cplusplus.com/reference/cstring/strlen/)的功能👉
>簡單來說,就是用來計算字串的長度 [color=#E1F1E7]
```c=
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define TARGET ",Titanic"
#define MOVER "Hello"
int main() {
int len_target = strlen(TARGET);
int len_mover = strlen(MOVER);
printf("%d\n", len_target); // 9
printf("%d\n", len_mover); // 5
return 0;
}
```

* **draw(target)、draw(mover)**
* 先將 MOVER 的位置放到畫面上的原因,以保持游標始終在移動中的字串後面
<br/>
```c=
draw(targetY, targetX, TARGET);
draw(moverY, moverX, MOVER);
```
* **if**
* 判斷是否到達指定的位置,如果是,則在中間顯示 "Success" 並離開 while 迴圈。
```c=
if (moverY == targetY && moverX >= (targetX - 6) && moverX <= targetX + len_target - len_mover) {
gotoxy(maxX / 2 - 3, maxY / 2);
printf("Success");
break;
}
```
* 示範if的code👉
```C=
#include<stdio.h>
int main() {
int value = 72;
if (value == 72) {
printf("hello,Titanic\n");
}
else {
printf("Not match\n");
}
value = 30;
if (value == 72) {
printf("hello,Titanic\n");
}
else {
printf("Not match\n");
}
return 0;
}
```

當 if() 內的事件成立時<span class ="red">(TRUE)</span>,就會執行 if{} 起來的,反之,則會執行 else{} 起來的
* 示範&&的code👉
```c=
#include<stdio.h>
int main() {
int value1 = 5;
int value2 = 10;
printf("%d\n",value1 > 0 && value2 > 0);//兩者成立
printf("%d\n",value1 < 0 && value2 > 0);//其一不成立
printf("%d\n",value1 < 0 && value2 < 0);//都不成立
if (value1 > 0 && value2 > 0) {
printf("It match!!\n");
}
else {
printf("Not match QAQ\n");
}
value1 = 0; //更改成不符合其中一個條件
if (value1 > 0 && value2 > 0) {
printf("It match!!\n");
}
else {
printf("Not match QAQ\n");
}
return 0;
}
```

> && 是 C 語言中的邏輯運算符號,當左右兩邊成立時會回傳 TRUE,反之為 FALSE[color=#E1F1E7]
第一次的 if 我們兩邊的值都有成立,所以 printf 出 It match,但當第二次其中一個不成立時,就 printf 出 Not match
* **[_getch()](https://learn.microsoft.com/zh-tw/cpp/c-runtime-library/reference/getch-getwch?view=msvc-170)**
* 用來判斷鍵盤輸入的一個函式。
`ch = _getch();`
* getch()示範code👉
這段code是用來測試,_getch()他會輸出按下去的按鈕
```c=
#include<stdio.h>
#include<conio.h>
#include<windows.h>
int main() {
int number = 0;
number = _getch();//先得到224這個key
number = _getch();
printf("%d\n", number);
return 0;
}
```
> 值得注意的是,當按下方向鍵時,會先輸出一個 224
> _getch() 會首先返回 224,表示這是一個特殊的鍵盤輸入(功能鍵、方向鍵)
> 接著才有上方向鍵 72、下方向鍵 80、左方向鍵 75、右方向鍵77[color=#E1F1E7]
</br>
> <span class = "red">但這樣的 CODE 會有問題,沒有辦法持續輸入方向鍵
> 所以要來搭配個 while</span>
* **[while](https://openhome.cc/Gossip/CppGossip/whileStatement.html)**
while 也是一種迴圈,不過他的寫法跟 for 不太一樣
```
while(條件式) {
陳述句一;
陳述句二;
}
```
那如果條件式是一個 while(1) 的話就會形成無限迴圈,因為 1 在布林值中也代表著 TRUE,條件式一直成立的情況下,就會一直執行陳述句
步驟:
條件式(成立) -> 陳述句1 -> 陳述句2 -> 條件式(成立)....

* while 跟 _getch 搭配 code
```c=
#include<stdio.h>
#include<conio.h>
#include<windows.h>
int main() {
int number = 0;
while(1) {
number = _getch();
printf("%d\n", number);
Sleep(1000);
}
return 0;
}
```
* 不過就會發現到畫面一堆數字,所以會運用到`system("cls")`
`system("cls")` 會把已經輸出的文字清掉,然後再把游標移回到第一行最前面的位置
* 搭上 system("cls") code
```c=
#include<stdio.h>
#include<conio.h>
#include<windows.h>
int main() {
int number = 0;
while(1) {
system("cls");
number = _getch();
number = _getch();
printf("%d\n", number);
Sleep(1000);
}
return 0;
}
```
<br/>
## 程式碼玩看看 🎮
* **程式碼**
```c=
//引入標頭檔
#include<stdio.h>
#include<string.h>
#include<windows.h>
#include<conio.h>
//定義字串
#define TARGET ",Titanic"
#define MOVER "hello"
#define FISH "><(((º>"
//讓游標到指定位置
void gotoxy(int x, int y) {
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
//將字串printf到指定位置
void draw(int y, int x, const char* str) {
gotoxy(x, y);
printf("%s", str);
}
//畫面載入(魚、海草、海水)
void background() {
int lim = 4;
for (int k = 0; k < 2; k++) {
for (int i = 0; i < lim * 9; i++)
printf("\033[5m\033[34m~\033[0m");//輸出海浪
printf("\n");
}
for (int i = 0; i < lim*2; i++)
printf("\n"); //海底的樣
printf("\n");
for (int i = 0; i < lim*3; i++)
printf("\033[32m\033[5m\\|/\033[0m"); //輸出海草
printf("\n");
}
//魚的移動控制
void fishmove(int *fishx,int *fishy,int maxX) {
*fishx += 1;
if (maxX < *fishx) {
*fishx = 0;
}
}
int main() {
int maxY = 10, maxX = 30; //邊界大小
int len_mover = strlen(MOVER); //各字串長度
int len_target = strlen(TARGET);
int targetY = maxY - 1, targetX = maxX - len_target;//目標座標
int moverY = 3, moverX = 0; //移動字串座標
int ch; //方向鍵的
int fishX = 10,fishY=5; //魚的座標
int fish2X = 10, fish2Y = 10;
while (1) {
system("cls");
background();//海底畫面
draw(fishY, fishX, FISH);
draw(fish2Y, fish2X, FISH);
draw(targetY, targetX, TARGET);
draw(moverY, moverX, MOVER);
fishmove(&fishX,&fishY,maxX);
fishmove(&fish2X, &fish2Y, maxX);
if (moverY == targetY && moverX >= (targetX - 5) && moverX <= targetX + len_target - len_mover) {//達成畫面
gotoxy(maxX / 2 - 3, maxY / 2);
printf("Success");
break;
}
ch = _getch();
switch (ch) { //選擇移動方式
case 72: // Up
if (moverY > 0) moverY--;
break;
case 80: // Down
if (moverY < maxY - 1) moverY++;
break;
case 75: // Left
if (moverX > 0) moverX--;
break;
case 77: // Right
if (moverX < maxX - len_mover) moverX++;
break;
case 'q':
return 0;
default:
break;
}
}
while (_getch() != 'q'); //按下q離開
return 0;
}
```
> **提示若不能用,邊界的大小(maxX、maxY)記得要做更改喔!!!** [color=#E1F1E7]
</br></br>
* 參考
* [格式指令字](https://ithelp.ithome.com.tw/articles/10282210)
* [特殊字元](https://openhome.cc/Gossip/CGossip/PrintfScanf.html)
* [更改顏色](https://blog.51cto.com/u_15333014/3879949)
* [GetStdHandle](https://learn.microsoft.com/zh-tw/windows/console/getstdhandle)
* [getch_](https://learn.microsoft.com/zh-tw/cpp/c-runtime-library/reference/getch?view=msvc-170)
* [setconsolecursorposition](https://learn.microsoft.com/zh-tw/windows/console/setconsolecursorposition)
* [HTML_emoji](https://www.w3schools.com/charsets/ref_emoji_smileys.asp)
* [什麼是函式](http://program-lover.blogspot.com/2008/11/what-is-function_25.html)
* [for](https://openhome.cc/Gossip/CGossip/forStatement.html)