블록 회전처리 구현
- 일단 회전상태는 0 → 1 → 2 → 3  에 %4를 사용하여 0으로 되돌리는 식으로 구현하면 될 듯하다.
- Rotate Block은 현재 블록 체크하는거까지는 동일하다.
- 반시계 회전은  ((rotation - 1) + 4) % 4;를 해주면 된다.
- 위처럼 +4 를 더해주면 음수 범위 보정이 되어  0 → 3 → 2 → 1 로 안전하게 변환이 된다.
 
void RotateBlock(BlockType type, int rotation, int& startX, int& startY) {
	bool moveFlag = true;
	vector<pair<int, int>> nextBlocks;
	vector<pair<int, int>> curBlocks;
	map<pair<int, int>, bool> treeMap;
	// 포인터 = 배열
	int* dx = nullptr;
	int* dy = nullptr;
	int blockSize = 4;
	// BlockType 에 맞는 dx, dy 배열을 설정
	SetBlockOffsets(type, rotation, dx, dy);
	// 유효성 검사
	for (int i = 0; i < 4; i++) {
		int x = startX + dx[i];
		int y = startY + dy[i];
		if (!CheckBoundary(y, x)) {
			cerr << "Block이 유효범위를 벗어났음.\\n";
			exit(EXIT_FAILURE);
		}
	}
	// 먼저 현재블록들 위치를 treeMap에 true로 두고
	// 현재 블록들 위치를 vector 형태로 저장한다.
	for (int i = 0; i < blockSize; i++) {
		int curX = startX + dx[i];
		int curY = startY + dy[i];
		pair<int, int> curPosPair = make_pair(curY, curX);
		curBlocks.push_back({ curY, curX });
		treeMap[curPosPair] = true;
	}
그 다음이 다르다. 회전이후 위치들을 살펴보고 놓을 수 있는지 판단해야한다.
- 당연히 이전처럼 기존 자기자신이 위치했던 위치는 treeMap을 통해 무시한다.
- 로직은 이동과 구현방식이 사실상 거의 동일하므로 이전에 만든 이동코드를 복사 붙여넣기 후에 조금 수정하면된다.
- ClockWise 와 CounterClockWise 2개를 두면 된다.
void RotateClockWiseBlock(BlockType type, int& rotation, int& startX, int& startY) {
	bool moveFlag = true;
	vector<pair<int, int>> nextBlocks;
	vector<pair<int, int>> curBlocks;
	map<pair<int, int>, bool> treeMap;
	// 포인터 = 배열
	int* dx = nullptr;
	int* dy = nullptr;
	int blockSize = 4;
	// 현재 회전 상태의 오프셋 설정
	SetBlockOffsets(type, rotation, dx, dy);
	// 현재 블록 좌표 저장
	for (int i = 0; i < blockSize; i++) {
		int curX = startX + dx[i];
		int curY = startY + dy[i];
		if (!CheckBoundary(curY, curX)) {
			cerr << "Block이 유효범위를 벗어났음.\\n";
			exit(EXIT_FAILURE);
		}
		pair<int, int> curPosPair = make_pair(curY, curX);
		curBlocks.push_back(curPosPair);
		treeMap[curPosPair] = true;
	}
	// 다음 회전 상태 계산
	int nextRotation = (rotation + 1) % 4;
	int* next_dx = nullptr;
	int* next_dy = nullptr;
	SetBlockOffsets(type, nextRotation, next_dx, next_dy);
	// 다음 회전 좌표 계산 및 검사
	for (int i = 0; i < blockSize; i++) {
		int nextX = startX + next_dx[i];
		int nextY = startY + next_dy[i];
		nextBlocks.push_back({ nextY, nextX });
		// 경계 검사
		if (!CheckBoundary(nextY, nextX)) {
			return; // 회전 불가
		}
		// 겹침 검사 (자기 자신 제외)
		pair<int, int> nextPos = { nextY, nextX };
		if (treeMap.find(nextPos) == treeMap.end() && grid[nextY][nextX] == 1) {
			return; // 회전 불가
		}
	}
	// ---- 여기까지 오면 회전 가능 ---- //
	// 기존 위치 비우기
	for (auto& curBlock : curBlocks) {
		grid[curBlock.first][curBlock.second] = 0;
	}
	// 새로운 위치 채우기
	for (auto& nextBlock : nextBlocks) {
		grid[nextBlock.first][nextBlock.second] = 1;
	}
	// 회전 상태 갱신
	rotation = nextRotation;
}
void RotateCounterClockWiseBlock(BlockType type, int& rotation, int& startX, int& startY) {
	bool moveFlag = true;
	vector<pair<int, int>> nextBlocks;
	vector<pair<int, int>> curBlocks;
	map<pair<int, int>, bool> treeMap;
	// 포인터 = 배열
	int* dx = nullptr;
	int* dy = nullptr;
	int blockSize = 4;
	// 현재 회전 상태의 오프셋 설정
	SetBlockOffsets(type, rotation, dx, dy);
	// 현재 블록 좌표 저장
	for (int i = 0; i < blockSize; i++) {
		int curX = startX + dx[i];
		int curY = startY + dy[i];
		if (!CheckBoundary(curY, curX)) {
			cerr << "Block이 유효범위를 벗어났음.\\n";
			exit(EXIT_FAILURE);
		}
		pair<int, int> curPosPair = make_pair(curY, curX);
		curBlocks.push_back(curPosPair);
		treeMap[curPosPair] = true;
	}
	// 다음 회전 상태 계산
	int nextRotation = ((rotation - 1) + 4)) % 4;
	int* next_dx = nullptr;
	int* next_dy = nullptr;
	SetBlockOffsets(type, nextRotation, next_dx, next_dy);
	// 다음 회전 좌표 계산 및 검사
	for (int i = 0; i < blockSize; i++) {
		int nextX = startX + next_dx[i];
		int nextY = startY + next_dy[i];
		nextBlocks.push_back({ nextY, nextX });
		// 경계 검사
		if (!CheckBoundary(nextY, nextX)) {
			return; // 회전 불가
		}
		// 겹침 검사 (자기 자신 제외)
		pair<int, int> nextPos = { nextY, nextX };
		if (treeMap.find(nextPos) == treeMap.end() && grid[nextY][nextX] == 1) {
			return; // 회전 불가
		}
	}
	// ---- 여기까지 오면 회전 가능 ---- //
	// 기존 위치 비우기
	for (auto& curBlock : curBlocks) {
		grid[curBlock.first][curBlock.second] = 0;
	}
	// 새로운 위치 채우기
	for (auto& nextBlock : nextBlocks) {
		grid[nextBlock.first][nextBlock.second] = 1;
	}
	// 회전 상태 갱신
	rotation = nextRotation;
}
블록 회전 관련 키입력처리
- Q와 E를 통해 키입력 회전처리를 하도록한다.