{"id":19544154,"url":"https://github.com/sunset1995/ballfight","last_synced_at":"2026-05-28T16:31:03.611Z","repository":{"id":92715666,"uuid":"65063277","full_name":"sunset1995/ballfight","owner":"sunset1995","description":"Term project of course SNP at NCTU","archived":false,"fork":false,"pushed_at":"2017-08-01T16:57:00.000Z","size":4775,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-02-26T05:41:32.983Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sunset1995.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-08-06T03:35:28.000Z","updated_at":"2022-06-20T14:18:13.000Z","dependencies_parsed_at":"2023-05-15T21:30:25.322Z","dependency_job_id":null,"html_url":"https://github.com/sunset1995/ballfight","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sunset1995/ballfight","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunset1995%2Fballfight","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunset1995%2Fballfight/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunset1995%2Fballfight/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunset1995%2Fballfight/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sunset1995","download_url":"https://codeload.github.com/sunset1995/ballfight/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunset1995%2Fballfight/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33617718,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-11T03:24:52.511Z","updated_at":"2026-05-28T16:31:03.592Z","avatar_url":"https://github.com/sunset1995.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SNP 期末專題 - Ballfight\n\n![競技場螢幕截圖](images/arena.png)  \n遊戲有三個元件：\n- [競技場](http://ballfight.nctu.me/)：請用電腦瀏覽器打開，為遊戲進行的場所\n- hero.py：你可以撰寫你的策略並註冊到競技場中，以下幾種平台的教學\n    - [在 windows 上](install/ballfight-windows.md)\n    - [在 mac 上](install/ballfight-mac.md)\n    - [在 ubuntu 上](install/ballfight-ubuntu.md)\n    - [在 c9 上](install/ballfight-c9.md)\n\n有任何問題請寄信至 `s2821d3721@gmail.com`  \n\n\n\n\n## 勝負條件\n- 競技場上有紅方藍方個兩顆球\n- 當有一方兩顆球同時超出競技場外的瞬間遊戲結束\n- 若遊戲結束那一瞬間，藍方都在界外即算紅方勝利\n\n\n\n\n## 遊戲規則\n- 雙方能做的的操作只有對自己的球施予 `[fx, fy]` 的力\n- 施力會受到干擾而隨機被轉 ±30deg\n- 球的移動會受到與當前速度方向相反的摩差力影響 (`friction = [-v[0], -v[1]]`)\n- 施力最大為 `1000`，超過的話會以 `1000` 算\n- 初始競技場半徑為 `350`，且會漸漸縮小 (每秒半徑減少 `20`)\n- 雙方球的半徑皆為 `25`\n- 競技場的圓心為座標系統 `(0, 0)`，右邊為 x 軸正向，下方為 y 軸正向\n- 藍方初始位置：左上`(-200, -200)`, 右上`(200, -200)`\n- 紅方初始位置：左下`(-200, 200)`, 右下`(200, 200)`\n- 中間有一個半徑 `75` 的失控區域。當整顆球都在正中央紅色虛線範圍內時，妳的球將不會再受妳施利的影響，施力將會固定為進入該範圍瞬間的施力，直到離開該範圍。\n\n\n\n\n## 競技場介紹\n\n### 設定連線\n![設定連線螢幕截圖](images/arena-1.png)  \n有兩種模式可以選擇：  \n- 離線模式 `Offline mode`：妳只能用鍵盤操控\n- 連線模式 `Create Room`：連上伺服器建立一個 Room，之後可以多人連上此 Room 相互對決\n    - 網頁（競技場）與妳的 python （hero.py）需透過一個中央伺服器作為資料（競技場資訊，你想要的施力...）傳輸的代理人 (broker)，一個中央伺服器管理很多個不同 Room\n    - `broker url`：代理人(broker)的網址，為負責將資料送到正確的地方的中央伺服器\n    - `room name`：這個競技場的房間名\n\n\u003e 注意  \n\u003e 一個 room 在手機，競技場，hero 進行遊戲時，伺服器的網路負擔約為進出各 5KB/s  \n\u003e 建議不要使用手機計費網路進行  \n\n\n\n### 選擇角色\n![選擇角色螢幕截圖](images/arena-2.png)  \n下方四個紅藍方塊按下後，可以幫其選取角色(策略)。所有可選的角色呈現在上方的黑色方塊中，其中比較特別的是：  \n- `` 空白代表什麼都不做\n- `WASD space` 用 WASD 鍵控制方向，空白鍵加速\n- `↑←↓→ enter` 用上下左右鍵控制方向，enter 鍵加速  \n\n若妳選擇連線模式，在妳的 python 註冊策略後將可在角色欄位中看到並可以選取，但一個 python 只能玩一個角色，若妳想要一次控制兩隻，請執行兩個註冊不同名字的 _hero.py_  \n\n\n\n\n## 用 python script 進行遊戲\n\n打開 _hero.py_\n```python\nimport api\n\n\ndef strategy():\n\n    info = api.get()\n    print('=======================')\n    print('radius', api.getRadius())\n    print('me    ', api.getMe())\n    print('friend', api.getFriend())\n    print('enemy1', api.getEnemy1())\n    print('enemy2', api.getEnemy2())\n\n    return [1000, 1000, 'gogo']\n\n\napi.play('ws://sunset.nctu.me:8080/ws', 'demo-room', '君の名', strategy)\n```\n妳可以 `import` 所有妳需要用到的套件，此外一定要 `import api` 以進行連線與註冊妳的策略。  \n在函數 `strategy` 裡面實作妳的策略，透過 `api` 取得所有場面資訊然後回傳妳想要的施力大小。此函數當場面_狀態改變_時被呼叫。以下任一條件皆會造成_狀態改變_：  \n- 遊戲結束變進行中或反之\n- 遊戲進行中，任何場面資訊的更新\n\n\n### 連線\n`api.play('ws://sunset.nctu.me:8080/ws', 'demo-room', '君の名', strategy)`  \n- 第一個參數 `ws://sunset.nctu.me:8080/ws` 代表伺服器 url，請換成跟妳想要去的競技場一樣的網址\n- 第二個參數 `demo-room`，請換成跟妳想要去的競技場一樣的房間名\n- 第三個參數 `君の名` 是妳的名字，請留意別跟其他同房的人撞名\n- 第四個參數 `strategy` 為上面實作的遊戲策略的函數名\n- __注意__ 此行以下的程式碼將不會被執行，所以請把它擺在 python 程式碼的尾端\n\n\n### strategy 的回傳值\n請回傳一個 `list: [x 方向施力, y 方向施力]`  \n若想要與隊友溝通可以把字串放在第三個位置，如：`[1000, 1000, 'fight enemy1 first!!']`  \n\n\n### 取得場面資訊\n用 api 取得場面資訊的方法如下：  \n- `api.getRadius()` 回傳一 _float_ 表示當前競技場半徑\n- `api.getState()` 回傳一 _string_ 代表遊戲輸贏狀態或仍然在進行中\n- `api.getMe()`, `api.getFriend()`, `api.getEnemy1()`, `api.getEnemy2()` 回傳一 _list_ 代表該角色目前的資訊，list 長度為 5 包含該角色的所有資訊： [x, y, vx, vy, '說的話']\n- `api.get()` 提供另一種取得資訊的方式，這會回傳一 _dict_ 包含上述所有資訊，結構如下：  \n```\n{\n    'state': 'Hello',\n    'me': { 'x': 0, 'y': 0, 'vx': 0, 'vy': 0, 'say': '', },\n    'friend': { 'x': 0, 'y': 0, 'vx': 0, 'vy': 0, 'say': '', },\n    'enemy1': { 'x': 0, 'y': 0, 'vx': 0, 'vy': 0, 'say': '', },\n    'enemy2': { 'x': 0, 'y': 0, 'vx': 0, 'vy': 0, 'say': '', },\n    'radius': 0,\n}\n```\n**注意** 我們會把競技場翻轉，讓所有 _hero.py_ 看到的初始狀態為 __總是自己在左上角也就是 (-200, -200)__，而隊友在右上角  \n妳與妳的隊友的 `enemy1` 將會是同一個對手，也就是說若妳的 `enemy1` 在妳的正對面，則妳隊友看到的 `enemy1` 會在斜對面。同理 `enemy2`。  \n\n\n### 執行\n在黑盒子(terminal)進入 _hero.py_ 所在資料夾，執行 `python3 hero.py` 即會向該競技場註冊妳的策略。  \n\n\n\n\n## 附錄：二維幾何公式\n\n- 點 `(x, y)` 與原心的距離\n    - ![equation](http://latex.codecogs.com/gif.latex?%5Csqrt%7Bx%5E2%2By%5E2%7D)\n- 兩點 `(x1, y1)`，`(x2, y2)` 之間的距離\n    - ![equation](http://latex.codecogs.com/gif.latex?%5Csqrt%7B%28x_1-x_2%29%5E2%2B%28y_1-y_2%29%5E2%7D)\n- 在 `(x, y)` 往原心方向施力\n    - `(-x, -y)`\n- 在 `(x1, y1)` 往 `(x2, y2)` 的方向施力\n    - `(x2-x1, y2-y1)`\n- 施力 `(fx, fy)` 想要不改變方向但改變施力大小為 `r`\n    - 不想改變方向只想改變大小兩個維度必須乘上同一個數 i.e. `(fx * C, fy * C)`\n    - 原本施力大小為 ![equation](http://latex.codecogs.com/gif.latex?R%3D%5Csqrt%7Bfx%5E2%2Bfy%5E2%7D)\n    - ![equation](http://latex.codecogs.com/gif.latex?C%3D%5Cfrac%7Br%7D%7BR%7D)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunset1995%2Fballfight","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsunset1995%2Fballfight","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunset1995%2Fballfight/lists"}