거북이 명령 프로그램 Turtle.zip
수업 과제로 있길래 만들어봤다.
// Example.mp4
// Turtle.cpp
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 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | #include <iostream> #include <fstream> #include <vector> #include <queue> #include <string> #include <Windows.h> using namespace std; #define MAX_LINE_LENGTH 100 void gotoxy(int x, int y) { COORD Pos = { x, y }; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), Pos); } class Vector2 { public: Vector2() { x = 0; y = 0; } Vector2(int x, int y) { this->x = x; this->y = y; } Vector2& operator+=(const Vector2& vector) { x += vector.x; y += vector.y; return (*this); } int x, y; }; class Turtle { public: void readFile(char *fileName) { ifstream commandFile; char command[MAX_LINE_LENGTH]; init(); // 객체 초기화 // 파일 읽기 commandFile.open(fileName, ios::in); if (commandFile.fail()) { fprintf(stderr, "파일이 열리지 않습니다.\n"); system("pause"); exit(-1); } while (!commandFile.eof()) { commandFile.getline(command, MAX_LINE_LENGTH); commandList.push_back(command); } commandFile.close(); // 계산 compute(0, commandList.size() - 1); } void draw() { // 배열 생성 및 초기화 size_t width = (arrSize[1] + -arrSize[3]) * 2 + 1, height = (arrSize[0] + -arrSize[2]) * 2 + 1; // * 2 : 간선과 정점, + 1 : 시작 점 포함 char ***drawArr = createDrawArr(width, height); char *drawChar; pos = Vector2(-arrSize[3] * 2, -arrSize[2] * 2); // start = (Left, Down) // 배열에 그리기 while (!dirQueue.empty()) { dirIndex = dirQueue.front(); if (dirIndex % 2 == 0) drawChar = "│"; // Up or Down else drawChar = "─"; // Right or Left pos += dirArr[dirIndex]; drawArr[pos.y][pos.x] = drawChar; // 간선 그리기 pos += dirArr[dirIndex]; drawArr[pos.y][pos.x] = "*"; // 정점 그리기 dirQueue.pop(); } // 화면에 그리기 for (int i = height - 1; i >= 0; i--) { for (int j = 0; j < width; j++) printf("%s", drawArr[i][j]); printf("\n"); } destroyDrawArr(drawArr); } void sequenceDraw(unsigned int delay) { size_t width = (arrSize[1] + -arrSize[3]) * 2 + 1, height = (arrSize[0] + -arrSize[2]) * 2 + 1; // * 2 : 간선과 정점, + 1 : 시작 점 포함 char *drawChar; pos = Vector2(-arrSize[3] * 2, -arrSize[2] * 2); // start = (Left, Down) while (!dirQueue.empty()) { dirIndex = dirQueue.front(); if (dirIndex % 2 == 0) drawChar = "│"; // Up or Down else drawChar = "─"; // Right or Left pos += dirArr[dirIndex]; gotoxy(pos.x * 2, height - pos.y - 1); printf("%s", drawChar); // 간선 그리기 Sleep(delay); pos += dirArr[dirIndex]; gotoxy(pos.x * 2, height - pos.y - 1); printf("%s", "*"); // 정점 그리기 dirQueue.pop(); Sleep(delay); } gotoxy(0, height + 1); } private: void init() { commandList.clear(); dirQueue = queue<int>(); dirIndex = 0; pos = Vector2(0, 0); memset(arrSize, 0, sizeof(int) * 4); } int classifyCommand(string command) { // 탭 제거 int i, len = command.size(); for (i = 0; i < len && command[i] == '\t'; i++); // 분류 if (command[i] == 'r') { if (command[i + 1] == 'e') return 0; // repeat else if (command[i + 1] == 'i') return 2; // right } else if (command[i] == 'g') return 1; // go else if (command[i] == 'l') return 3; // left return -1; } void compute(int start, int end) { for (int i = start + 1; i < end; i++) { switch (classifyCommand(commandList[i])) { case 0: // repeat int repeatNum, tapNum, endTapNum, endLine; sscanf(commandList[i].c_str(), "%*s %d", &repeatNum); // 반복 횟수 가져오기 // end 문자열 찾기 for (tapNum = 0; commandList[i][tapNum] == '\t'; tapNum++); for (endLine = i + 1; i < end; endLine++) { for (endTapNum = 0; commandList[endLine][endTapNum] == '\t'; endTapNum++); if (endTapNum == tapNum) break; } if (commandList[endLine][endTapNum] != 'e') { fprintf(stderr, "올바른 형식이 아닙니다.\n"); system("pause"); exit(-2); } for (int j = 0; j < repeatNum; j++) compute(i, endLine); i = endLine; break; case 1: // go dirQueue.push(dirIndex); pos += dirArr[dirIndex]; switch (dirIndex) { case 0: if (pos.y > arrSize[0]) arrSize[0] = pos.y; break; // Up case 1: if (pos.x > arrSize[1]) arrSize[1] = pos.x; break; // Right case 2: if (pos.y < arrSize[2]) arrSize[2] = pos.y; break; // Down case 3: if (pos.x < arrSize[3]) arrSize[3] = pos.x; break; // Left } break; case 2: // right dirIndex = (++dirIndex % 4); break; case 3: // left if (--dirIndex < 0) dirIndex = 3; break; } } } char ***createDrawArr(size_t width, size_t height) { char ***drawArr; drawArr = new char **[height]; for (int i = 0; i < height; i++) { drawArr[i] = new char *[width]; for (int j = 0; j < width; j++) drawArr[i][j] = " "; } return drawArr; } void destroyDrawArr(char ***& drawArr) { if (drawArr != nullptr) { size_t height = (arrSize[0] + -arrSize[2]) * 2 + 1; for (int i = 0; i < height; i++) delete[] drawArr[i]; delete[] drawArr; drawArr = nullptr; } } private: const Vector2 dirArr[4] = { Vector2(0, 1), Vector2(1, 0), Vector2(0, -1), Vector2(-1, 0) }; // Up, Right, Down, Left vector<string> commandList; queue<int> dirQueue; int dirIndex; Vector2 pos; int arrSize[4]; // Up, Right, Down, Left }; int main(int argc, char *argv[]) { Turtle turtle; char *fileName; if (argc <= 1) { fileName = "input.txt"; system("cls"); turtle.readFile(fileName); turtle.sequenceDraw(25); system("cls"); turtle.readFile(fileName); turtle.draw(); system("pause"); return 1; } for (int i = 1; i < argc; i++) { fileName = argv[i]; system("cls"); turtle.readFile(fileName); turtle.sequenceDraw(25); system("cls"); turtle.readFile(fileName); turtle.draw(); system("pause"); } } | cs |
// 설명.txt
핵심 알고리즘은 Dynamic Programming(큰 문제를 작은 문제로 쪼개 해결하는 것)을 기초로 하였다.
제일 중요한 코드는 Turtle 클래스의 compute 함수다.
compute 함수는 commandList 배열에 들어있는 명령어들을 처리하여, dirQueue 에 dirArr의 index를 뜻하는 정수들을 넣어준다.
매개변수 start와 end는 처리할 commandList 배열의 시작과 끝을 말한다.
repeat 명령어의 처리는 반복문과 재귀함수를 이용했다.
0.program
1. go
2. repeat 3
3. go
4. right
5. end
6.end
위와 같이 입력예제가 있다고 가정해보자.
일단, 반복 횟수인 3을 추출한다
repeat와 end 사이에 있는 3-4번줄 명령어는 또 다른 코드라고 볼 수 있다.
따라서, compute(2, 5)를 호출하여 사이에 있는 명령어를 처리할 수 있다.
이를 3번 반복하기 위해서 다음과 같은 코드가 되게 한다.
int num = 3;
for (int i = 0; i < num; i++)
compute(2, 5);
이후 5번 줄까지의 명령어는 이미 처리되었기 때문에, 다음에 처리할 명령어의 줄을 6으로 설정해야 한다.
i = endLine;
위 코드가 해당 역할을 해준다.
실수했던 점은 repeat에 대응하는 end 문자열을 찾는 일이였다.
처음에는 맨아래서부터 tap의 길이를 무시하고 end를 찾아 올라왔었는데,
0. program
1. repeat 3
2. go
3. end
4. repeat 2
5. go
6. end
7. end
이런 코드가 있을 경우 제대로된 처리가 되지 않았다.
따라서, 기존에 했던 방법을 철회하고 해당 repeat 줄을 기점으로 차례대로 내려가면서 같은 tap의 길이이면서 end문자열이 있는 줄을 찾는 방식으로 프로그래밍 하였다.
위 예제의 경우 기존 코드는 1번줄 repeat에 6번줄 end를 대응시키는데, 변경시킨 코드의 경우 1번줄 repeat에 3번줄 end를 대응시킨다.
이로써 해당 문제를 해결하였다.
'프로그래밍' 카테고리의 다른 글
유튜브 뮤직 컨트롤러 (0) | 2020.09.07 |
---|---|
[개인 공부] 하노이 타워 (0) | 2017.05.20 |