테트리스 개발일지 ver. Linux - 1

나묘쿠·2023년 11월 26일
0

테트리스

목록 보기
8/8

사실 1주일차 라고 하는게 맞겠다..
교수님께서 c++ 수업의 과제로 프로그램 하나를 만들어오라고 했는데 21년 이맘때쯤 만들었던 테트리스를 리메이크 해보려고 마음먹었다.

그것도 리눅스로!

이유는 별거 없다.
지금 개발머신으로 사용중인 dell xps 13 9300에 리눅스를 깔아 사용한지 두 달째 되어가는데 리눅스 환경에서 c프로그래밍은 어떤 느낌일까 궁금해서 시작해보았다.


그리고 이 개발일지는 발표 ppt를 만드는데 참고하고 발표 대본으로 쓸 예정이다. 우려먹고 또 우려먹을 것이다.


가장 먼저 예전 tetris 코드를 꺼내보자!

https://github.com/scienceNH/tetris

단일 파일에 1832줄 c++ 코드... 쉽지 않다.

객체지향이나 파일분리, 코드 스타일 등 아무것도 모를 때 만든 코드니 뭐...

어쨌든 코드를 클론해준다.
리눅스에 ssh 공개키와 비밀키를 만들어두고 github를 사용하고 있는데 이게 꽤나 편하다.

컴파일러는 CLion을 사용할 예정이다.

프로젝트 제작부터 난관이다.. 이 cmake라는 것은 무엇인가.

일단 프로젝트를 만들면서 알게 된게 윈도우에서 vs를 사용할 때에는 여러 모듈 파일을 알아서 관리해주지만 리눅스에서는 아니더라. 그래서 프로젝트에서 의존관계로 있는 모듈 하나가 바뀔 때마다 일일이 컴파일을 해줘야 하는데, 이를 디버깅 한 번으로 해결할 수 있게 해주는 것이 makefile 이다. 그런데 이 makefile 마저 지속적으로 관리를 해주지 않으면 빌드가 난리가 나면서 꼬이는데 이를 반 자동화..? 하여 의존관계로 얽힌 여러 모듈들의 디버깅을 편하게 해주는 도구인 cmake를 사용하여 프로젝트를 하는 것이 일반적이라는 것을 알았다.

리눅스..쉽지 않군!

어쨌든 프로젝트를 만들어 예전에 만든 코드를 불러와보았다.

코드의 모든 부분에서 오류가 나고있다.

가장 난감했던 오류가 gotoxy와 kbhit, getch 였다.
이 세 함수가 없으면 기존 코드의 구조를 가져올 수 없다.
리눅스에서는 windows.h 헤더와 conio.h 헤더파일을 사용할 수 없는게 가장 큰 문제였다. 알아보니 conio.h는 tc가 도스에서 사용자들의 편의를 위해 추가한 헤더파일이어서 gcc 컴파일러에서는 지원하지 않는다고 한다.

그래서 가장 먼저 알아 본 것이 kbhit, getch의 리눅스에서의 구현과 gotoxy였다.

kbhit과 getch는 구글링을 하니 금방 나왔다.
https://eastskykang.wordpress.com/2015/01/28/c-c-kbhit-function-in-linux/

그리고 gotoxy를 검색해보았다.
gotoxy를 ncurses 라이브러리의 wmove를 통해 사용할 수 있다는 글이 있던데 CLion을 처음 사용하는지라 ncurses를 링크하는데 어려움을 겪어 제대로 작동하지 않아 printf로 하드코딩된 코드를 사용했다.

	printf("\033[%d;%df", y, x);
    //이것도 사용 가능 printf("%c[%d;%df", 0x1B, y, x);
    fflush(stdout);

두 printf 구문을 모두 사용할 수 있던데 둘의 차이가 무슨 차인지는 다음에 알아봐야겠다. 현 시점 기준으로 제출기한이 일주일도 남지 않았기 때문에...

그리고 다음은 리눅스 콘솔에서 커서를 숨기는 함수이다.

printf("\e[?25l"); //숨기는 코드
printf("\e[?25h"); //보이게 하는 코드

This should work ! It is taken from ANSI codesheet where the characters are not just what they are seen. They act like some form of commands.
- 스택 오버플로우의 어느 유저...

이 또한 원리는 나중에 알아봐야겠다.

필요한 코드는 얼추 갖추어졌으니 일단 메인 화면부터 만들어보자.
기존 코드는 단일 파일에 모두 다 때려박아 가독성도 떨어지고 유지보수도 힘들다.
라고 하지만 성능은 아마 제일 좋을텐데
근데 나는 이왕 만드는거 깔끔하게 만들고 싶기 때문에 클래스 분리나 코드 스타일을 설정해 만들것이다.

코드 스타일은 ClangFormat을 사용했다. intelliJ에서는 xml을 통해 구글이나 우아한형제들의 코드 스타일을 쉽게 구할 수 있었는데 CLion의 코드는 마땅한 xml 파일을 찾을 수 없었다. 그래서 알아본게 clangformat이었다. CLion의 설정 에서 기본 옵션으로 제공해 설정도 쉬웠다.

https://clang.llvm.org/docs/ClangFormat.html

궁금하신 분들은 들어가 확인해보시길.

메인 화면에 타이틀을 출력하려고 한다.
일단 터미널에서 없애야 할 것이 두 가지 있는데, 터미널 탭 바와 커서이다.

터미널 탭 바는 clear 명령어를 통해 지워지기 때문에
system("clear");로 지워지고,
커서는 아까 만들어둔 함수를 통해 지울 것이다.
커서를 지우는 함수는 TetrisTerminal.h에 #define hide()로 선언해두었다.

커서와 탭 바를 없앴으니 다음은 타이틀 출력이다.
터미널에 색을 바꿔 출력하는 것도 윈도우와 다른 방식으로 할 수 있다.

TetrisColor.h 헤더를 만들어주고 다음 코드를 넣어주자

//
// Created by namaek_2 on 11/21/23.
//

#ifndef TETRIS_TETRISCOLOR_H
#define TETRIS_TETRISCOLOR_H

#include <ostream>

namespace Color {
enum Code {
  FG_RED = 31,
  FG_GREEN = 32,
  FG_YELLOW = 33,
  FG_BLUE = 34,
  FG_MAGENTA = 35,
  FG_CYAN = 36,
  FG_WHITE = 37,
};
class Modifier {
  Code code;

public:
  Modifier(Code pCode) : code(pCode) {}
  friend std::ostream &operator<<(std::ostream &os, const Modifier &mod) {
    return os << "\033[" << mod.code << "m";
  }
};
} // namespace Color

#endif // TETRIS_TETRISCOLOR_H

그리고 색을 바꿔 출력하는 함수가 있는 소스파일로 가 헤더를 선언해주고 다음과 같이 정의를 해주면 색을 사용할 수 있다.

Color::Modifier RED(Color::FG_RED);
Color::Modifier MAGENTA(Color::FG_MAGENTA);
Color::Modifier YELLOW(Color::FG_YELLOW);
Color::Modifier BLUE(Color::FG_BLUE);
Color::Modifier CYAN(Color::FG_CYAN);
Color::Modifier GREEN(Color::FG_GREEN);
Color::Modifier WHITE(Color::FG_WHITE);
//TetrisSTart::PrintTitle

void TetrisStart::PrintTitle() {
  cout << RED;
  PrintT(39, 6);
  cout << MAGENTA;
  PrintE(53, 6);
  cout << YELLOW;
  PrintT(67, 6);
  cout << GREEN;
  PrintR(81, 6);
  cout << CYAN;
  PrintI(95, 6);
  cout << BLUE;
  PrintS(109, 6);
  cout << WHITE;

  kb.gotoxy(42, 36);
  cout << "회전 : S, D, 방향키 상 / 이동 : 방향키 좌, 우 ,하 / 하드드랍 : "
          "스페이스 바";
  kb.gotoxy(70, 40);
  cout << "made by namaek";
}

이렇게 하면 다음과 같이 출력할 수 있다.
자세한 코드는 글의 마지막에 있는 깃허브 링크에서 확인하길 바란다.

터미널의 탭 바는 프로그램이 종료되어 그런 것이다. namaek뒤의 %도 아마 버퍼에 남아있던게 아닐까 싶다. 이것도 나중에 알아봐야겠다

다음은 메뉴를 출력하고 화살표 위, 아래 키로 순회할 수 있도록 만들 것이다.

여기서 충격받았던게 화살표 입력 매커니즘이 윈도우랑 달랐다..!
윈도우에서는 버퍼에 숫자 224(esc)가 먼저 들어가고, 그 다음에 다음과 같은 숫자들이 들어간다.

리눅스에서는 달랐다. 가장 먼저 224가 들어가고, 91이 한 번 더 들어간다. 그리고 그 다음에 65, 66이 각각 위 아래로 들어간다. 이게 운영체제가 달라서 그런건지 아니면 아키텍쳐나 다른 요소 때문인지도 나중에 찾아봐야겠다.

기존에 있던 코드에 수정들을 해주니 제대로 작동했다!

이제 다음과 같은 화면이 뜨고 아래 >> 커서가 깜빡이면서, 화살표 키를 누르면 위 아래로 움직이며 클릭하면 클릭된 메뉴가 깜빡인다.

시작 화면은 이렇게 구현이 완료되었다.

uml처럼 간단히 설계도를 그려보았다.
#include가 중복이 되지 않게 하기 위해 어느 헤더에 어떤 헤더가 include됐는지 파악하려고 한다.

자세한 코드는 여기서 확인해보길 바란다.

코드 깃허브 링크 :
https://github.com/scienceNH/tetris_linux

profile
사고의 흐름을 따라가 보자.

0개의 댓글