---
# System prepended metadata

title: 泰坦號
tags: [程式設計 I]

---

<style>
    .red{
        color : #f15b6c;
    }
    .black{
        color : 000000;
    }
    .blue{
        color : #87ceeb;
    }
</style>    
# 泰坦號 🏄‍♂️
![titan_pic](https://hackmd.io/_uploads/B1oB1HwC3.jpg)

## 教學前的提醒
看不懂，聽不懂的部分可以直接提出，要改進也可以寫在留言區
著重目標在讓未學習過程式的同學，透過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)
![](https://hackmd.io/_uploads/rJRqfRa0h.png =30%x)

## 遊戲介紹
* ### 遊玩方式
     利用鍵盤上的方向鍵，將一個指定字串(hello)，移動到另個指定字串(,Titanic)的旁邊
     成功後會再中間跳出success
* ### 畫面
     左上角可移動字串，右下角目標字串、有2隻小魚隨著hello走，還有海草、海浪
         ![](https://hackmd.io/_uploads/rkf0g06Rn.png =80%x)


## 前置⚙️

1. 在桌面新增資料夾

    ![](https://hackmd.io/_uploads/HJ46kuo03.png)

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

    ![](https://hackmd.io/_uploads/SyXmxdiCh.png)

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

    ![](https://hackmd.io/_uploads/SyOgXujCn.png)
    ![](https://hackmd.io/_uploads/ByDHX_s02.png)

4. 選擇是，我信任作者

    ![](https://hackmd.io/_uploads/rJzt7_sR3.png)

5. 點擊新增檔案

    ![](https://hackmd.io/_uploads/BygYrdoRh.png)

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

    ![](https://hackmd.io/_uploads/S1GaBuiCn.png)

   
## **程式碼剖析**🔍

> 3個大綱 函式庫->背景->可移動字串 ，在三個大綱中再分細項去講解

1. ### 函式庫的引入
    * ### code示範 
        如果只打上
        ```c=
        int main() {
            printf("hello,Titanic\n");
            return 0;
        }
        ```
        ![](https://hackmd.io/_uploads/B1UOUQ0A2.png)


        系統會告訴你無法編譯與執行

        那加上 printf() 所屬的標頭檔 <stdio.h> 再試試
        
        ```c=
        #include<stdio.h>

        int main() {
            printf("hello,Titanic\n");
            return 0;
        }
        ```
        ![](https://hackmd.io/_uploads/SkCYLmAA3.png)

        發現可以正常執行了!!

        因為在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 時就會運作，但函式就像副程式一樣，我們不去呼叫他，就不會去運行
            ![](https://hackmd.io/_uploads/B1IBcjCC3.png =50%x)

        > 魚的部分有點多了，就跳過[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，因為游標在後方
                ![](https://hackmd.io/_uploads/By7pUVk16.png)

        * #### 首先運用 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;
            }
            ```
            ![](https://hackmd.io/_uploads/SyDUsaJkp.png =50%x)


            我們的輸出會在指定的y與x上，而非最左上角

            > 值得注意的是 draw() 是先 y 再來 x 的喔 [color=#E1F1E7]
            > 也運用到了前面所講的定義與標頭檔!!

    * ### go to xy
        >將游標移到，指定的 x,y [color=#E1F1E7]  
        1. `COORD`代表著 x,y 的座標
            ![](https://hackmd.io/_uploads/rkviaRTCh.png =40%x)
        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;
            }
            ```
            ![](https://hackmd.io/_uploads/SydV6Lk16.png =50%x)

            會發現到，第二次的 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;
                }
                ```

                ![](https://hackmd.io/_uploads/SyNfpHykp.png)


        * **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;
                }
                ```
                ![](https://hackmd.io/_uploads/rJP49p1k6.png)


                當 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;
                }
                ```
                ![](https://hackmd.io/_uploads/HJmKwX0R2.png)


              > && 是 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 -> 條件式(成立)....
            ![](https://hackmd.io/_uploads/H1S-fLky6.png =70%x)


            
            * 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)