點選左上角 File -> Open
選擇 桌面 -> EEcamp -> EEcamp_6.22.1 -> main.cpp 及 pacman.hpp -> 點選 open
開啟檔案後點選上方 Tools -> Compiler Options
勾選
Add the following commands when calling the compiler
並新增:
-std=c++14 -static
再點選 ok✅
在你的程式可以執行之後設定Image Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
- 對彈出的視窗(終端機terminal)上方白色區域點擊右鍵
- 選擇內容
- 設定字型大小 28
- 設定字體種類 Consolas
- 點擊下方確定
#include <bits/stdc++.h>
#include <windows.h>
#define HEIGHT 15
#define WIDTH 66 // 定 義 遊 戲 區 域 的 高 度 和 寬 度
#define powerNum 4
#define ghostNum 4 // 能 量 球 和 鬼 的 數 量
#include "pacman.hpp"
//#define __VScode // 若 使 用 VScode 請 取 消 註 解
using namespace std;
int dir[5][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}, {0, 0}}; // d,s,a,w,stop
bool gameRunning = true;
// 檢 查 是 否 與 牆 壁 碰 撞
bool checkNotCollideWall(string grid[], int i, int j) {
int n = HEIGHT, m = WIDTH;
if (/*question 1*/)
return false; // 如 果 超 出 邊 界 或 碰 到 牆 壁 , 回 傳 false
return true; // 否 則 回 傳 true
}
// 檢 查 是 否 與 鬼 碰 撞
bool checkCollideGhost(int (*ghostPos)[2], int playerPos[2]) {
for (int i = 0; i < ghostNum; i++) {
if (/*question 2*/) {
return true; // 如 果 pacman 與 鬼 的 座 標 相 同 , 回 傳true
}
}
return false; // 否 則 回 傳 false
}
// 計 算 最 佳 方 向
int bestDir(string grid[], int curPos[2], int curDir, int target[2]) {
int n = HEIGHT, m = WIDTH;
int ans = 4; // 預 設 為 停 止
int minDis = INT16_MAX; // 最 小 距 離 初 始 化 為 最 大 值
for (int i = 0; i < 4; i++) {
if (i == (curDir + 2) % 4)
continue; // 不 考 慮 反 方 向
int newI = curPos[0] + dir[i][0]; // 新 的 i 座 標
int newJ = curPos[1] + dir[i][1]; //新 的 j 座 標
int dis = /*question 3*/ ;
if (dis < minDis && checkNotCollideWall(grid, newI, newJ)) {
ans = i; // 更 新 最 佳 方 向
minDis = dis; // 更 新 最 小 距 離
}
}
return ans; // 回 傳 最 佳 方 向
}
// 移 動 鬼
template <int _N>
void moveGhost(string grid[], int (&ghostPos)[_N][2], int (&ghostDir)[_N],
char ghostType[], int playerPos[2],
int playerDir, bool scaredMode) {
for (int i = 0; i < ghostNum; i++) {
int legalRoute = 0; // 合 法 路 徑 計 數
int possibleDir[3]; // 存 儲 可 能 的 方 向
for (int j = 0; j < 4; j++) {
if ((checkNotCollideWall(grid, ghostPos[i][0] + dir[j][0],
ghostPos[i][1] + dir[j][1])) &&
(j != (ghostDir[i] + 2) % 4)) {
possibleDir[legalRoute] = j; // 存 儲 有 效 方 向
legalRoute++;
}
}
legalRoute++;
if (!scaredMode) { // 如 果 不 是 驚 嚇 模 式
if (legalRoute == 1) {
ghostDir[i] = (ghostDir[i] + 2) % 4; // 反 向 移 動
} else if (legalRoute == 2) {
ghostDir[i] = possibleDir[0]; // 隨 機 選 擇 一 個 方 向
} else if (legalRoute == 3 || legalRoute == 4) {
int target[2] = {};
if (ghostType[i] == 'a') { // 紅 鬼 :目 標 pacman 位 置
/*question 4*/
} else if (ghostType[i] == 'b') { // 粉 紅 鬼 :目 標 pacman 位 置 4 格 前
/*question 5*/
} else if (ghostType[i] == 'c') { // 淺 藍 鬼 :目 標 與 紅 色 鬼 對 稱 中 心 在 pacman 前 方 2 格
int indexOfRed = 0;
for (int j = 0; j < ghostNum; j++) {
if (ghostType[j] == 'a') {
indexOfRed = j;
break;
}
}
int center[2];
center[0] = playerPos[0] + dir[playerDir][0] * 2;
center[1] = playerPos[1] + dir[playerDir][1] * 2;
/*question 6*/
} else if (ghostType[i] == 'd') { // 橙 鬼 :保 持 4 格 距 離
double a, b;
a = ghostPos[i][0] - playerPos[0];
b = ghostPos[i][1] - playerPos[1];
target[0] = round(playerPos[0] + a * 4 / sqrt(a * a + b * b));
target[1] = round(playerPos[1] + b * 4 / sqrt(a * a + b * b));
}
ghostDir[i] = bestDir(grid, ghostPos[i], ghostDir[i], target); // 計 算 最 佳 方 向
}
} else { // 如 果 是 驚 嚇 模 式
if (isupper(ghostType[i])) {
if (legalRoute == 1) {
ghostDir[i] = (ghostDir[i] + 2) % 4; // 反 向 移 動
} else if (legalRoute == 2) { // 不 回 頭
ghostDir[i] = possibleDir[0];
} else if (legalRoute == 3 || legalRoute == 4) { // 不 回 頭
ghostDir[i] = possibleDir[rand() % (legalRoute - 1)];
}
}
if (islower(ghostType[i])) { // 復 活 先 不 動
ghostDir[i] = 4;
}
}
if (checkNotCollideWall(grid, ghostPos[i][0] + dir[ghostDir[i]][0],
ghostPos[i][1] + dir[ghostDir[i]][1])) {
ghostPos[i][0] += dir[ghostDir[i]][0]; // 根 據 方 向 移 動
ghostPos[i][1] += dir[ghostDir[i]][1];
}
}
}
// 吃 掉 鬼
void eatGhost(string grid[], int playerPos[2], int (*ghostPos)[2],
int (&ghostDir)[ghostNum], char (&ghostType)[ghostNum],
int &score) {
for (int i = 0; i < ghostNum; i++) {
if (ghostPos[i][0] == playerPos[0] && ghostPos[i][1] == playerPos[1]) {
score += 200; // 吃 掉 鬼 的 得 分
/*question 7*/
ghostDir[i] = 4; // 設 置 鬼 的 方 向 為 停 止
ghostType[i] = tolower(ghostType[i]); // 將 鬼 的 類 型 改 為 小 寫
}
}
}
int main() {
srand(time(0)); // 設 置 隨 機 數 種 子
#ifndef __VScode
SetConsoleCtrlHandler(ConsoleHandler, TRUE);
system("chcp 65001");
#endif
string initGrid[HEIGHT] = {
"#################################################################",
"#...............................................................#",
"#..###...#...##...##.....#####....#......#...#######...#######..#",
"#..#.#...#....##.##.....##...##...#......#...#.........#........#",
"#..#.###.#.....###......#.........#......#...#######...#######..#",
"#..#...#.#......#.......##...##...##....##...#.........#........#",
"#..#...###......#........#####.....######....#######...#######..#",
"#...............................0...............................#",
"#...#####.....#####....##....##...########...#######...#######..#",
"#..##...##...## ##...###..###...# #.........#...#........#",
"#..#.........#######...#.####.#...########...#######...#######..#",
"#..##...##...#.....#...#..##..#...#..........#...............#..#",
"#...#####....#.....#...#......#...#..........#######...#######..#",
"#...............................................................#",
"#################################################################"};
int pointsCnt = 0;
int playerPos[2]; // pacman 位 置
int playerDir = 3; // 初 始 方 向 為 'w'
int ghostPos[ghostNum][2]; // 鬼 的 位 置
int ghostDir[ghostNum] = {4, 4, 4, 4}; // 鬼 的 方 向
char ghostType[ghostNum]; // 鬼 的 類 型
int score = 0; // 分 數
int scaredTime = 0; // 驚 嚇 時 間
bool scaredMode = false; // 是 否 在 驚 嚇 模 式
string pointsGrid[HEIGHT]; // 點 數 網 格
string gridDisplay[HEIGHT]; // 顯 示 網 格
string prevGridDisplay[HEIGHT]; // 前 一 個 顯 示 網 格
for (int i = 0; i < HEIGHT; i++) {
string row = "";
row.append(WIDTH, ' ');
pointsGrid[i] = row; // 初 始 化 點 數 網 格
gridDisplay[i] = row; // 初 始 化 顯 示 網 格
prevGridDisplay[i] = row; // 初 始 化 前 一 個 顯 示 網 格
}
for (int i = 0; i < HEIGHT; i++) { // 設 定 pacman 位 置 的 初 始 狀 態
for (int j = 0; j < WIDTH; j++) {
char c = initGrid[i][j];
if (c == '0') {
/*question 8*/
}
}
}
// 隨 機 放 置 能 量 球
for (int p = 0; p < powerNum; p++) {
int x, y;
do {
x = rand() % HEIGHT; // 隨 機 列
y = rand() % WIDTH; // 隨 機 行
} while (/*question 9*/);
initGrid[x][y] = '*'; // 放 置 能 量 球
}
// 隨 機 放 置 鬼
for (int i = 0; i < ghostNum; i++) {
int x, y;
do {
x = rand() % HEIGHT; // 隨 機 列
y = rand() % WIDTH; // 隨 機 行
} while (!(initGrid[x][y] == '.' &&
((x - playerPos[0]) * (x - playerPos[0]) +
(y - playerPos[1]) * (y - playerPos[1])) >= 49));
// 確 保 放 置 在 合 法 位 置 且 距 離 pacman 有 一 定 距 離
ghostPos[i][0] = x; // 設 置 鬼 的 行
ghostPos[i][1] = y; // 設 置 鬼 的 列
ghostType[i] = 'a' + i; // 分 配 鬼 的 類 型 a, b, c, d
}
for (/*question 10-1*/) { // 設 定 點 數 網 格 的 初 始 狀 態
for (/*question 10-2*/) {
char c = initGrid[i][j];
gridDisplay[i][j] = c; // 初 始 化 顯 示 網 格
if (c == '.' || c == '*') {
pointsGrid[i][j] = c; // 設 置 點 數 網 格
pointsCnt++; // 增 加 點 數 計 數
}
}
}
KeyManager keyM; // 創 建 鍵 盤 管 理 器
int curDir = playerDir; // 當 前 方 向
bool gameOver = false; // 遊 戲 是 否 結 束
cout << "\x1B[2J\x1B[H" << "\033[?25l"; // 初 始 化 螢 幕 , 清 除 並 隱 藏 鼠 標
update(initGrid, playerPos, ghostPos, ghostDir, ghostType, pointsGrid, pointsCnt, gridDisplay, score, scaredMode, scaredTime);
gridPrint(gridDisplay, prevGridDisplay);
cout << "\033[" << (HEIGHT + 1) << ";1H\033[K";
cout << "press any key to start game (use arrows or WASD to move pacman)" << endl;
keyM.anyKeyToContinue();
for (int i = 3; i > 0; i--) {
cout << "\033[" << (HEIGHT + 1) << ";1H\033[K";
cout << "starting in " << i << "........." << endl;
Sleep(1000);
}
while (!gameOver && gameRunning) {
// 移 動 pacman
movePacman(initGrid, playerPos, playerDir, keyM);
// 檢 查 是 否 與 鬼 碰 撞
if (checkCollideGhost(ghostPos, playerPos)) {
if (scaredMode)
eatGhost(initGrid, playerPos, ghostPos, ghostDir, ghostType, score); // 吃 掉 鬼
else
gameOver = true; // 如 果 不 是 驚 嚇 模 式 , 遊 戲 結 束
}
// 移 動 鬼
if (!(scaredMode && scaredTime % 2)) // 如 果 是 驚 嚇 模 式 只 有 一 半 的 時 間 會 移 動 鬼
moveGhost(initGrid, ghostPos, ghostDir, ghostType, playerPos, playerDir, scaredMode);
// 再 次 檢 查 是 否 與 鬼 碰 撞
if (checkCollideGhost(ghostPos, playerPos)) {
if (scaredMode)
eatGhost(initGrid, playerPos, ghostPos, ghostDir, ghostType, score); // 吃 掉 鬼
else
gameOver = true; // 如 果 不 是 驚 嚇 模 式 , 遊 戲 結 束
}
// 更 新 遊 戲 狀 態
update(initGrid, playerPos, ghostPos, ghostDir, ghostType, pointsGrid, pointsCnt, gridDisplay, score, scaredMode, scaredTime);
// 輸 出 遊 戲 網 格
gridPrint(gridDisplay, prevGridDisplay);
cout << "\033[" << (HEIGHT + 1) << ";1H\033[K"; // 移 動 鼠 標 到 顯 示 分 數 的 位 置
cout << "Score: " << score << endl; // 顯 示 分 數
Sleep(500); // 暫 停 0.5 秒
if (pointsCnt == 0) gameOver = true; // 如 果 所 有 點 數 都 被 吃 掉 , 遊 戲 結 束
}
// 遊 戲 結 束
cout << "\033[?25h" << "\033[" << (HEIGHT + 2) << ";1H"; // 恢 復 鼠 標 顯 示
if (pointsCnt == 0) {
cout << "You win! Final score: " << score << endl; // 如 果 贏 了 , 顯 示 勝 利 信 息
} else {
cout << "Game Over! Final score: " << score << endl; // 如 果 遊 戲 結 束 , 顯 示 結 束 信 息
}
return 0; // 回 傳 0 , 結 束 程 序
}
#pragma once
#include <bits/stdc++.h>
#include <windows.h>
using namespace std;
extern int dir[5][2];
extern bool gameRunning;
bool checkNotCollideWall(string grid[], int i, int j);
bool checkCollideGhost(int (*ghostPos)[2], int playerPos[2]);
int bestDir(string grid[], int curPos[2], int curDir, int target[2]);
template <int _N>
void moveGhost(string grid[], int (&ghostPos)[_N][2], int (&ghostDir)[_N],
char ghostType[], int playerPos[2],
int playerDir, bool scaredMode);
void eatGhost(string grid[], int playerPos[2], int (*ghostPos)[2],
int (&ghostDir)[ghostNum], char (&ghostType)[ghostNum],
int &score);
// 鍵 盤 管 理 器 結 構
struct KeyManager {
char lastDirC; // 最 後 按 下 的 方 向 鍵 字 符
int lastDir; // 最 後 按 下 的 方 向
std::map<char, int> wasdToDir = {{'d', 0}, {'s', 1}, {'a', 2}, {'w', 3}}; // WASD 鍵 對 應 的 方 向
bool start; // 是 否 開 始
bool newInput; // 是 否 有 新 輸 入
KeyManager() : lastDirC('w'), lastDir(4), start(true), newInput(false) {
thread rK(&KeyManager::updateStatus, this); // 啟 動 更 新 狀 態 的 線 程
rK.detach(); // 分 離 線 程
}
~KeyManager() {
start = false; // 停 止 更 新 狀 態
INPUT inputs[3] = {};
ZeroMemory(inputs, sizeof(inputs)); // 清 空 輸 入
inputs[1].type = INPUT_KEYBOARD;
inputs[1].ki.wVk = 'w'; // 模 擬 按 下 'w' 鍵
inputs[2].type = INPUT_KEYBOARD;
inputs[2].ki.wVk = 'w';
inputs[2].ki.dwFlags = KEYEVENTF_KEYUP; // 模 擬 釋 放 'w' 鍵
UINT unsent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT)); // 發 送 輸 入
}
// 更 新 鍵 盤 狀 態
void updateStatus() {
char cur = 'w';
lastDirC = cur;
while (start) {
cur = readKey(); // 讀 取 當 前 按 鍵
newInput = true;
if (cur == 'w' || cur == 'a' || cur == 's' || cur == 'd') {
lastDirC = cur;
lastDir = wasdToDir[lastDirC]; // 更 新 方 向
}
}
return;
}
// 讀 取 按 鍵
char readKey() {
INPUT_RECORD inputRecord;
DWORD written;
HANDLE stdInH = GetStdHandle(STD_INPUT_HANDLE);
while (true) {
ReadConsoleInputA(stdInH, &inputRecord, 1, &written); // 讀 取 控 制 台 輸 入
if (inputRecord.EventType == KEY_EVENT &&
inputRecord.Event.KeyEvent.bKeyDown)
break; // 如 果 是 按 鍵 事 件 且 按 鍵 被 按 下 , 則 退 出 循 環
}
switch (inputRecord.Event.KeyEvent.wVirtualScanCode) {
case 72:
return 'w'; // 回 傳 'w'
case 75:
return 'a'; // 回 傳 'a'
case 80:
return 's'; // 回 傳 's'
case 77:
return 'd'; // 回 傳 'd'
default:
return inputRecord.Event.KeyEvent.uChar.AsciiChar; // 回 傳 其 他 字 符
}
}
// 獲 取 最 後 按 下 的 鍵
int getLastKey() {
return lastDir;
}
void anyKeyToContinue() {
newInput = false;
while (!newInput) {}
return;
}
};
// 輸 出 遊 戲 網 格
void gridPrint(string grid[], string (&prevGrid)[HEIGHT]) {
int n = HEIGHT, m = WIDTH;
vector<vector<string>> gridDisplay(n, vector<string>(m, " "));
vector<vector<string>> gridDisplayColor(n, vector<string>(m, ""));
vector<vector<string>> gridDisplayThick(n, vector<string>(m, ""));
// 輸 出 顏 色 的 函 數
function<string(string, string, string)> outputColor =
[&](string color, string thick, string output) {
string ans = "\033[";
std::map<string, string> colorGrid = {
{"black", "0"},
{"red", "196"},
{"green", "46"},
{"yellow", "11"},
{"blue", "27"},
{"purple", "105"},
{"lightblue", "87"},
{"white", "255"},
{"pink", "219"},
{"skin", "224"},
{"orange", "214"}};
std::map<string, string> thickGrid = {
{"bold", ";1"},
{"normal", ""},
{"light", ";2"}};
ans += "38;5;" + colorGrid[color] + thickGrid[thick] + "m";
ans += output;
ans += "\033[0m";
return ans;
};
// 檢 查 上 方 是 否 有 牆 壁
function<bool(int, int)> checkUp = [&](int x, int y) {
if (0 <= x && x < n && 0 <= y && y < m)
if (grid[x][y] == '#')
return 1; // 如 果 是 牆 壁 , 回 傳 1
return 0; // 否 則 回 傳 0
};
// 填 充 網 格 顯 示
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '#') {
// 當 前 位 置 只 看 上 下 左 右 有 幾 個 #
// 一 個 : ─ or │ // 兩 個 : ─ │ ┌ ┐ └ ┘
// 三 個 : 看 缺 少 的 邊 對 馬 步 的 兩 個 角
// 有 一 個 為 空 就 是 ├ ┤ ┬ ┴ 都 是 滿 的 就 是 ─ │
// 四 個 : 根 據 缺 角
// 缺 一 個 ┌ ┐ └ ┘ 缺 兩 個 相 鄰 ├ ┤ ┬ ┴
// 缺 兩 個 對 角 ┼ 缺 三 個 以 上 ┼
gridDisplayColor[i][j] = "blue"; // 牆 壁 顏 色
gridDisplayThick[i][j] = "normal"; // 牆 壁 粗 細
string type[16] = {"X", "─", "│", "╮", "─", "─", "╭", "┬", "│", "╯", "│", "┤", "╰", "┴", "├", "┼"};
int next = checkUp(i + 1, j) + checkUp(i - 1, j) +
checkUp(i, j + 1) + checkUp(i, j - 1);
if (next == 1 || next == 2) {
int iCheck = checkUp(i - 1, j) * 8 +
checkUp(i, j + 1) * 4 +
checkUp(i + 1, j) * 2 +
checkUp(i, j - 1);
gridDisplay[i][j] = type[iCheck]; // 根 據 周 圍 牆 壁 的 數 量 選 擇 顯 示 的 字 符
} else if (next == 3) {
if (!checkUp(i + 1, j) &&
(checkUp(i - 1, j + 1) && checkUp(i - 1, j - 1)))
gridDisplay[i][j] = type[1];
else if (!checkUp(i - 1, j) &&
(checkUp(i + 1, j + 1) && checkUp(i + 1, j - 1)))
gridDisplay[i][j] = type[1];
else if (!checkUp(i, j + 1) &&
(checkUp(i - 1, j - 1) && checkUp(i + 1, j - 1)))
gridDisplay[i][j] = type[2];
else if (!checkUp(i, j - 1) &&
(checkUp(i - 1, j + 1) && checkUp(i + 1, j + 1)))
gridDisplay[i][j] = type[2];
else {
int iCheck = checkUp(i - 1, j) * 8 +
checkUp(i, j + 1) * 4 +
checkUp(i + 1, j) * 2 +
checkUp(i, j - 1);
gridDisplay[i][j] = type[iCheck];
}
} else if (next == 4) {
int angle = checkUp(i + 1, j + 1) + checkUp(i - 1, j + 1) +
checkUp(i + 1, j - 1) + checkUp(i - 1, j - 1);
if (angle == 4) {
gridDisplay[i][j] = " "; // 如 果 四 周 都 有 牆 , 顯 示 空 格
} else if (angle == 3) {
if (!checkUp(i + 1, j + 1))
gridDisplay[i][j] = type[6]; // ┌
else if (!checkUp(i - 1, j + 1))
gridDisplay[i][j] = type[12]; // └
else if (!checkUp(i + 1, j - 1))
gridDisplay[i][j] = type[3]; // ┐
else if (!checkUp(i - 1, j - 1))
gridDisplay[i][j] = type[9]; // ┘
} else if (angle == 2) {
if (!checkUp(i + 1, j + 1) && !checkUp(i + 1, j - 1))
gridDisplay[i][j] = type[13]; // ┴
else if (!checkUp(i - 1, j + 1) && !checkUp(i - 1, j - 1))
gridDisplay[i][j] = type[7]; // ┬
else if (!checkUp(i + 1, j + 1) && !checkUp(i - 1, j + 1))
gridDisplay[i][j] = type[11]; // ┤
else if (!checkUp(i + 1, j - 1) && !checkUp(i - 1, j - 1))
gridDisplay[i][j] = type[14]; // ├
else
gridDisplay[i][j] = type[15]; // ┼
} else
gridDisplay[i][j] = type[15]; // ┼
}
} else if (grid[i][j] == '0') {
gridDisplayColor[i][j] = "yellow"; // pacman 顏 色
gridDisplayThick[i][j] = "bold"; // pacman 粗 細
gridDisplay[i][j] = "●"; // pacman 符 號
} else if (grid[i][j] == '*') {
gridDisplayColor[i][j] = "skin"; // 能 量 球 顏 色
gridDisplayThick[i][j] = "normal"; // 能 量 球 粗 細
gridDisplay[i][j] = "○"; // 能 量 球 符 號
} else if (grid[i][j] == '.') {
gridDisplayColor[i][j] = "skin"; // 點 數 顏 色
gridDisplayThick[i][j] = "normal"; // 點 數 粗 細
gridDisplay[i][j] = "."; // 點 數 符 號
} else if (grid[i][j] == ' ') {
gridDisplayColor[i][j] = "white"; // 空 白 顏 色
gridDisplayThick[i][j] = "normal"; // 空 白 粗 細
gridDisplay[i][j] = " "; // 空 白 符 號
} else if (isalpha(grid[i][j])) {
if (grid[i][j] == 'G') { // 嚇 人 的 鬼
gridDisplayColor[i][j] = "white";
gridDisplayThick[i][j] = "normal";
gridDisplay[i][j] = "ö"; // 嚇 人 的 鬼 符 號
} else if (isupper(grid[i][j])) {
gridDisplayColor[i][j] = "purple"; // 鬼 的 顏 色
gridDisplayThick[i][j] = "normal"; // 鬼 的 粗 細
gridDisplay[i][j] = "ö"; // 鬼 符 號
} else if (islower(grid[i][j])) {
string ghost[4] = {"red", "pink", "lightblue", "orange"};
gridDisplayColor[i][j] = ghost[grid[i][j] - 'a']; // 小 鬼 的 顏 色
gridDisplayThick[i][j] = "normal"; // 小 鬼 的 粗 細
gridDisplay[i][j] = "∩"; // 小 鬼 符 號
}
}
}
}
cout << "\033[H"; // 移 動 鼠 標 到 左 上 角
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (prevGrid[i][j] != grid[i][j]) {
cout << "\033[" << (i + 1) << ";" << (j + 1) << "H"; // 移 動 光 標 到 指 定 位 置
cout << outputColor(gridDisplayColor[i][j], "normal", gridDisplay[i][j]); // 輸 出 顏 色 和 符 號
}
}
}
cout << "\033[" << (n + 1) << ";1H"; // 移 動 鼠 標 到 下 一 行
for (int i = 0; i < n; i++) {
prevGrid[i] = grid[i]; // 更 新 前 一 個 網 格
}
}
// 移 動 pacman
void movePacman(string grid[], int (&playerPos)[2], int &curDir, KeyManager &keyM) {
int input = keyM.getLastKey(); // 獲 取 最 後 按 下 的 鍵
if (checkNotCollideWall(grid, playerPos[0] + dir[input][0],
playerPos[1] + dir[input][1])) {
playerPos[0] += dir[input][0]; // 更 新 pacman 位 置
playerPos[1] += dir[input][1];
curDir = input; // 更 新 當 前 方 向
} else {
curDir = 4; // 如 果 碰 到 牆 壁 , 設 置 為 停 止
}
return;
}
// 重 新 生 成 鬼 的 位 置
void respawnGhost(string grid[], int (&ghostPos)[2], int playerPos[2]) {
int maxDistance = -1;
int farX = -1, farY = -1;
for (int x = 0; x < HEIGHT; x++) {
for (int y = 0; y < WIDTH; y++) {
if (grid[x][y] == '.' && (x != playerPos[0] || y != playerPos[1])) {
int distance = (x - playerPos[0]) * (x - playerPos[0]) +
(y - playerPos[1]) * (y - playerPos[1]);
if (distance > maxDistance) {
maxDistance = distance;
farX = x;
farY = y; // 更 新 最 遠 位 置
}
}
}
}
ghostPos[0] = farX; // 設 置 鬼 的 位 置
ghostPos[1] = farY;
}
// 更 新 遊 戲 狀 態
void update(string grid[], int playerPos[2], int (*ghostPos)[2],
int ghostDir[], char ghostType[],
string (&pointsGrid)[HEIGHT], int &pointsCnt, string (&display)[HEIGHT],
int &score, bool &scaredMode, int &scaredTime) {
int n = HEIGHT, m = WIDTH;
for (int i = 0; i < n; i++) {
string row = "";
row.append(WIDTH, ' ');
display[i] = row; // 初 始 化 顯 示
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '#') {
display[i][j] = '#'; // 牆 壁
}
if (pointsGrid[i][j] == '.') {
display[i][j] = '.'; // 點 數
}
if (pointsGrid[i][j] == '*') {
display[i][j] = '*'; // 能 量 球
}
}
}
if (pointsGrid[playerPos[0]][playerPos[1]] == '.') {
pointsGrid[playerPos[0]][playerPos[1]] = ' '; // 吃 掉 點 數
score += 10; // 正 常 得 分
pointsCnt--; // 減 少 點 數 計 數
} else if (pointsGrid[playerPos[0]][playerPos[1]] == '*') {
pointsGrid[playerPos[0]][playerPos[1]] = ' '; // 吃 掉 能 量 球
score += 50; // 能 量 球 得 分
scaredMode = true; // 啟 動 驚 嚇 模 式
scaredTime = 20; // 設 置 驚 嚇 時 間 為 20 , 實 際 時 間 0.5*20 為 10 秒
// 將 鬼 的 類 型 改 為 大 寫
for (int i = 0; i < ghostNum; i++) {
ghostType[i] = toupper(ghostType[i]);
}
}
// 減 少 驚 嚇 時 間
if (scaredMode) {
scaredTime--;
if (scaredTime <= 0) {
scaredMode = false; // 停 止 驚 嚇 模 式
// 將 鬼 的 類 型 改 回 小 寫
for (int i = 0; i < ghostNum; i++) {
ghostType[i] = tolower(ghostType[i]);
}
}
}
display[playerPos[0]][playerPos[1]] = '0'; // pacman 位 置
for (int i = 0; i < ghostNum; i++) {
// 如 果 剩 下 3 秒 , 改 變 鬼 的 顏 色
if (scaredMode && scaredTime <= 6) {
display[ghostPos[i][0]][ghostPos[i][1]] = 'G'; // 在 驚 嚇 模 式 下 用 'G' 表 示 鬼
} else {
display[ghostPos[i][0]][ghostPos[i][1]] = ghostType[i]; // 正 常 鬼 顯 示
}
}
}
#ifndef __VScode
BOOL WINAPI ConsoleHandler(DWORD signal) {
if (signal == CTRL_C_EVENT || signal == CTRL_CLOSE_EVENT) {
gameRunning = false;
return TRUE;
}
return FALSE;
}
#endif
KeyManager
持續偵測鍵盤輸入,紀錄最後按下的方向鍵或 W,A,S,D 鍵
void gridPrint(string grid[], string (&prevGrid)[HEIGHT])
傳入 grid 後會將其美化上色並輸出,另外需傳入 prevGrid 僅輸出有變化的部分以提高效率
void movePacman(string grid[], int (&playerPos)[2], int &curDir, KeyManager &keyM)
根據最後被按下的按鍵移動pacman
void respawnGhost(string grid[], int (&ghostPos)[2], int playerPos[2])
復活被pacman吃掉的鬼,使其距離pacman盡量遠
void update(string grid[], int playerPos[2], int (*ghostPos)[2],
int ghostDir[], char ghostType[],
string (&pointsGrid)[HEIGHT], int &pointsCnt, string (&display)[HEIGHT],
int &score, bool &scaredMode, int &scaredTime)
更新玩家和鬼的位置,檢查玩家是否吃掉了點數或能量球,並更新分數。它還會處理驚嚇模式的啟動和結束,並更新顯示的遊戲狀態
if (/*question 1*/)
return false; // 如果超出邊界或碰到牆壁,回傳false
return true; // 否則回傳true
目標:
檢查超出邊界或碰到牆壁
- 1.
i < 0 || i > n || j < 0 || j > m || grid[i][j] == '#'
- 2.
i < 0 || i >= n || j < 0 || j >= m || grid[i][j] == '#'
- 3.
i < 0 || i >= n || j < 0 || j >= m || grid[i][j] == '.'
- 4.
i < 0 || i >= n || j < 0 || j >= m
if (/*question 2*/) {
return true; // 如果pacman與鬼的座標相同,回傳true
}
目標:
判斷pacman與鬼的座標是否相同
- 1.
ghostPos[i][1] == playerPos[1] && ghostPos[i][2] == playerPos[2]
- 2.
ghostPos[i][0] == playerPos[0] || ghostPos[i][1] == playerPos[1]
- 3.
ghostPos[i][0] == playerPos[0] && ghostPos[i][1] == playerPos[1]
- 4.
ghostPos[i] == playerPos
int dis = /*question 3*/ ;
目標:
計算 newI, newJ 和 target 的直線距離的平方
- 1.
(newI - target[0]) * (newI - target[0]) + (newJ - target[1]) * (newJ - target[1])
- 2.
(newI - target[0]) + (newJ - target[1])
- 3.
(newI - target[i]) + (newJ - target[j])
- 4.
(newI + newJ) - target
if (ghostType[i] == 'a') { // 紅鬼:目標pacman位置
/*question 4*/
}
目標:
找到紅色鬼的目標位置(pacman位置)
- 1.
target == playerPos;
- 2.
target = playerPos;
- 3.
target[0] == playerPos[0]; target[1] == playerPos[1];
- 4.
target[0] = playerPos[0]; target[1] = playerPos[1];
else if (ghostType[i] == 'b') { // 粉紅鬼:目標pacman位置4格前
/*question 5*/
}
目標:
找到粉紅鬼的目標位置(pacman位置前方4格)
- 1.
target = playerPos + 4 dir[playerDir];
- 2.
target[0] == playerPos[0] + dir[playerDir][0] * 4; target[1] == playerPos[1] + dir[playerDir][1] * 4;
- 3.
target[0] = playerPos[0] + dir[playerDir][0] * 4; target[1] = playerPos[1] + dir[playerDir][1] * 4;
- 4.
target[0] = playerPos[0] + 4 dir[playerDir][0]; target[1] = playerPos[1] + 4 dir[playerDir][1];
int center[2];
center[0] = playerPos[0] + dir[playerDir][0] * 2;
center[1] = playerPos[1] + dir[playerDir][1] * 2;
/*question 6*/
目標:
找到淺藍鬼的目標位置(與紅色鬼對稱中心在pacman前方2格)
- 1.
target[0] = center[0] * 2 - ghostPos[indexOfRed][0]; target[1] = center[1] * 2 - ghostPos[indexOfRed][1];
- 2.
target[0] + ghostPos[indexOfRed][0] = center[0] * 2; target[1] + ghostPos[indexOfRed][1] = center[1] * 2;
- 3.
target = center * 2 - ghostPos[indexOfRed];
- 4.
target[0] = center[0] - ghostPos[indexOfRed][0]; target[1] = center[1] - ghostPos[indexOfRed][1];
if (ghostPos[i][0] == playerPos[0] && ghostPos[i][1] == playerPos[1]) {
score += 200; // 吃掉鬼的得分
/*question 7*/
ghostDir[i] = 4; // 設置鬼的方向為停止
ghostType[i] = tolower(ghostType[i]); // 將鬼的類型改為小寫
}
目標:
鬼被吃掉之後應該做什麼?
- 1.
checkCollideGhost(ghostPos, playerPos, ghostNum);
- 2.
respawnGhost(grid, ghostPos[i], playerPos);
- 3.
ghostPos[i][0] = 0; ghostPos[i][1] = 0;
- 4.
checkNotCollideWall(grid, ghostPos[i][0], ghostPos[i][1]);
if (c == '0') {
/*question 8*/
}
目標:
紀錄pacman的初始位置
- 1.
playerPos[i] = '0'; playerPos[j] = '0';
- 2.
playerPos = i,j;
- 3.
playerPos[0] = '0'; playerPos[1] = '0';
- 4.
playerPos[0] = i; playerPos[1] = j;
int x, y;
do {
x = rand() % HEIGHT; // 隨機列
y = rand() % WIDTH; // 隨機行
} while (/*question 9*/);
initGrid[x][y] = '*'; // 放置能量球
目標:
確保能量球放置在原本是點數位置(.
是點數,*
是能量球)
- 1.
initGrid[x][y] == '.'
- 2.
initGrid[x][y] != '.'
- 3.
initGrid[x][y] = '.'
- 4.
initGrid[x][y] = .
for (/*question 10-1*/) { // 設定點數網格的初始狀態
for (/*question 10-2*/) {
char c = initGrid[i][j];
gridDisplay[i][j] = c; // 初始化顯示網格
if (c == '.' || c == '*') {
pointsGrid[i][j] = c; // 設置點數網格
pointsCnt++; // 增加點數計數
}
}
}
目標:
如何設定全部點數網格的初始狀態
- 1.
10-1: int i = 0; i < HEIGHT; i++ 10-2: int j = 0; j < WIDTH; j++
- 2.
10-1: int i = 0; i < HEIGHT; i+1 10-2: int j = 0; j < WIDTH; j+1
- 3.
10-1: int i = 0; i <= HEIGHT; i++ 10-2: int j = 0; j <= WIDTH; j++
- 4.
10-1: int i = 0; i < HEIGHT; i+1 10-2: int j = i; j < WIDTH; j+1
Learn More →
Learn More →
Learn More →
設計⼀個程式,提供以下功能選項:程式可以重複執⾏,每次開始時要詢問使⽤功能
Jun 23, 2025void gridPrint(string grid[], string (&prevGrid)[HEIGHT]) {int n = HEIGHT, m = WIDTH;vector<vector<string>> gridDisplay(n, vector<string>(m, " "));vector<vector<string>> gridDisplayColor(n, vector<string>(m, ""));vector<vector<string>> gridDisplayThick(n, vector<string>(m, ""));
Jun 22, 2025記得先填完表單~~~
Apr 28, 2025/
Apr 27, 2025or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up