NCurses 기반 Space Invader 게임을 웹으로 포팅하기

지난번 PacVim 포팅1http://andrwj.com/post/2019/09/porting-pacvim-with-emscripten/ 실패후에 찝찝함이 남아서 다른 패키지를 포팅해보았다.  흔해빠진 Invader 게임인데 ncurses 기반이다. 2http://ninvaders.sourceforge.net/ 둘다 ncurses v6.1 이지만 이번 nInvader 게임은 PacVim 보다 화면제어가 잘되고 있다. 그러나 역시나 어쩔 수 없는 점도 많이 보인다.

 

 

nInvaders 게임 코드 수정사항

1  pthread를 사용하지 않기 때문에 간단히 window.requestAnimationFrame()를 사용해서 브라우저가 뻗지 않도록 적절하게 업데이트하게 했다. iTerm2 에서 동작할 때와 별반 다른 점을 찾기 힘들정도로 부드럽게 움직였다. (뭐 당연한 얘기)

2  역시나 main()exit() 을 업애버리고 시작과 종료를 적절히 웹에서 조절하게 했다. 

3  장애물이 이상하게 표시되고 미사일 발사 주변이 말려올라가고 … 등등은 iTerm2에서 실행할 때는 전혀 문제없다. PacVim에서보다 훨씬 화면 제어가 잘되는 걸로 봐서 xterm.js에서 제대로 표시하려면 특정 ncurses API를 WebAssembly로 변환된 후의 상황을 고려해서 보정해줘야하는게 있다고 추측한다. 그게 뭔지 알려면 시간이 필요할 뿐이다. xterm.js 에는 문제가 없는 것 같다. (여전히 추측)

4  종료후 GPL 라이센스 출력을 하지 않게했다. 

5  ‘q’를 눌러도 exit() 하지 않고 다시 게임 대기화면으로 돌아가게 했다.

 

 

별 수정을 가하지 않아도 제대로 포팅되는 ncurses 기반 프로젝트를 발견할 때까지 쉬엄 쉬엄 포팅을 해볼 요량이다. 더불어 xterm.js 를 Client/Server 방식으로 돌려보며 문제해결채을 찾게되면 다시 업데이트 할 예정. 

References   [ + ]

커맨드라인 게임 PacVim을 Emscripten으로 웹으로 포팅하기

Vim 키맵을 사용하기 때문에 이런류의 게임1https://github.com/jmoon018/PacVim 좋아라 한다.  Emscripten을 사용한지도 오래됐고 WebAssembly Tool의 현재 상태도 알아보고 싶기도해서 겸사겸사 포팅을 시작했더랬다.  React Hooks를 씌워서 그럴싸한 UI까지 만드는게 목표였는데, ncurses + xterm.js 조합에 해결하기엔 시간이 너무 들어갈 것으로 보이는 문제 때문에 잠시 묵혀두기로 결정한다.

 

컴파일환경
  • Emscripten v1.38.41-upstream2https://emscripten.org/docs/getting_started/downloads.html, Target: WASM
  • Developing OS: Ubuntu
  • ncurses v6.13https://github.com/mirror/ncurses
  • xterm.js4https://xtermjs.org/ 
  • PacVim5https://github.com/jmoon018/PacVim 

 

중단원인

아래 두 그림에서 보듯 iTerm2 에서의 게임내 레벨 표시에 비해 xterm.js 에서 표시는 중간 부분이 완전히 사라져보이는데, 해당 원인은 Emscripten으로 컴파일된 ncurses에서 제어문자열이 도중에 많은 부분이 제거된채  xterm.js으로 보내지기 때문이다. (비교를 위해 이미지 높이를 동일하게 했더니 왼쪽이 좀 좁아 보인다)

Level 8 in iTerm2 Level 8 in xterm.js

ncurses 에서 문자하나를 현재 위치에 출력하는 addch() 함수호출 뒤에 refresh() 호출 여부가 JS 쪽 출력에 영향을 주는 것이 확인됐다. ncurses API에서 내놓는 escape sequence code를 xterm.js 쪽에서 해석을 하지 못하는 게 아닌가 의심스럽지만,  ncurses API쪽에서 적절한 단위로 코드를 나눠 전송하지 않는 문제일수도 있다.  문자열이 아닌 ASCII code 31 이하의 특수문자는  xterm.js 에서 제대로 해석하는 걸로 확인됐다. 따라서 현재 테스트해봐야할 부분은 ncurses API쪽에서 보낸 데이터와 브라우저 JS에서 받은 코드를 비교함으로써 데이터 누락 유무를 확인 하는 것이다.  제대로 전달되지 않는다면 Emscripten으로 컴파일된 ncurses 쪽 문제이고, 제대로 전달된다면  ncurses 쪽에서 내놓는 코드가 xterm.js 에서 제대로 해석되는지 확인해야 한다. 이래저래 시간 엄청 들어가므로 여기서 멈춘다. 

 

PacVim 코드 수정사항

1  게임내 Player 움직임을 제어하기 위해 ncurses getch() 로 입력받는 부분을 제거하고 브라우저 JS 쪽에서 입력받고 C++ API를 호출하는 방식으로 변경했다.

void EMSCRIPTEN_KEEPALIVE player_move(const char *key) {
        if(!READY) return;
        if(*key) {
                onKeystroke(player, *key);
                move(player.getY(), player.getX());
        }
        refresh();
}
 

 

Emscripten으로 포팅된 getch()는 동기함수라서 매번 입력받는 팝업이 게임 흐름을 정지시키므로 도저히 사용할 수가  없다. 앞으로 ncurses API로 데이터 입력받는 일은 없을 듯. 입력된 키 전달은  브라우저 JS에서  C++ API 호출로 전송한다. 

function onKeyUp(event) {
   if(event.key === 'Enter') {
      running = !running;
      Module._toggle_game_state(running);
      return;
   }
   Module._player_move(event.key);
}

output.addEventListener("keyup", onKeyUp, true);

 

2  game.cpp init() 함수에서 while()루프를 돌며 게임이 진행되지만  emscripten_set_main_loop()로 변환하지 않았다. Ghost는 while 루프와 무관하게 pthread로 동작하기 때문이다. pthread상의 Ghost는 READY 변수로 동작을 pause/resume 시킬 수 있다. 이 부분도 브라우저에서 조절하는 게 훨씬 조작성을 좋게한다.  pthreads 때문에  -s ALLOW_MEMORY_GROWTH=1 설정과 -u USE_PTHREADS=1 설정을 동시에 사용하면 귀찮은 메모리 설정 문제가 발생하므로  전체 메모리를 48MB, 스택을 16MB로 지정하고 컴파일했다.

3  게임속 player life 갯수가 0 이하가 될 때까지 루프를 돌게되어 있지만,  단순히 게임 한판을 종료하면 끝내게 해서  main()을 사용하지 않는다.  사용자입력 검증및 후처리를 ncurses API로 받아 하는 것보다 React Hooks 에서 조절하는게 훨씬 보기 좋기 때문이다. 

4  TERM 설정 값 외에는 다른 terminfo 파일이 필요없으므로 embed 방식으로 포함하였다:

   ↳  --embed-file /opt/emscriptened/share/terminfo/x/xterm@/home/web_user/.terminfo/x/xterm

5  PacVim 레벨 데이터 파일은 preload 로 포함시킨다:

    --preload-file maps@/usr/local/share/pacvim-maps

 

ncurses 기반 앱 활용가능성

SDL6https://www.libsdl.org/ 사용하면 이런 종류의 문제는 없겠지만 개인적으로 콘솔형 어플을 선호하기 때문에 가능한 ncurses + xterm.js 조합이 성공하길 기대하고 있다. ncurses를 사용하는 다른 오픈소스가운데 몇가지 Emscripten으로 컴파일해서 살펴보며 추후 수정여부를 새 글로 올릴 예정이다.

 

References   [ + ]

1. https://github.com/jmoon018/PacVim
2. https://emscripten.org/docs/getting_started/downloads.html
3. https://github.com/mirror/ncurses
4. https://xtermjs.org/
5. https://github.com/jmoon018/PacVim
6. https://www.libsdl.org/

자바스크립트 함수형 프로그래밍 – 루이스 아텐시오

이 글은 리뷰가 아니다.

 

2019년 4월 초에 구입했다. 가볍게 읽고 넘어간 부분과 함께 어떤 부분은 10번 이상, 어떤 부분은 수술하듯 단어를 분해하며 학습했다.  이 책이 다른 여타의 입문서나 Medium 글과 비교해 다른 건 체계적으로 함수형 프로그래밍을 어떻게 하는지 기술했다는 점이다. 소위 말하는 ‘초보자’를 위한 책은 아니다. 생각하고 고민하며 구현해가면서 봐야하는 책이었다. 분명하고 확실한 정의를 제공하지만 아는만큼 보이기 때문에 이해하지 못하는 부분은 결국 나중에서야 혀를 차게되는 경우가 제법 있었다.  

함수형 학습 초반에는 FxJS1https://github.com/marpple/FxJS 소스를 분석하고 같은 기능을 직접 구현하면서 ‘함수형이 무엇을 해결하려고 하는지’를 배워갔다면 실전에 가장 영향을 미쳤던 건 Either Monad 구현2https://github.com/andrwj/FPJS이었다. 과하게 Either를 적용하면서 문제가 생길 때마다 FolkTale3https://folktale.origamitower.com/ 구현 하나 하나를 적용하는 계기가 됐다. 그리고 6월 중순을 지나며 Lambda Calculus 구현4https://www.youtube.com/watch?v=3VQ382QG-y4을 볼 때 함수형 코딩론의 밑바닥에 손이 닿인 기분이 들었고 무엇을 어떻게 어떤 식으로 해야할지 완전히 이해했다.

 

.forEach(), .map() , .filter(), .reduce() … 이런 것에 촛점을 맞출 수 밖에 없게한 기존의 책과 Medium 글, 옆집 개가 짖으니 나도 짖는다 식의 블로그 글들,  함수형 코딩 무용론,..  OOP 방법론과의 비교글, ..  오래 전부터 함수형을 배우고 싶었으나 늘 그 언저리에서 머물 수 밖에 없었던 이유를 깨달았고 병신짓인지 모르고 읊어대는 교회 방언마냥 욕을 해대고 싶었으나, 비난의 대상이 너무 넓어 스스로에게 침뱉는 격이었다.

 

생각지 않았던 프로그래밍 언어에 대한 해방감인 찾아온 건 뜻밖의 수확. 라인 한 줄을 코딩하더라도 지침이 있기때문에 코딩하면 할수록 쌓이는 즐거움. 무엇이 잘못되고 무엇이 올바른 가에 대한 확고한 지침. 덤으로 찾아온 회춘.  나의 지난 시간들이 너무나 억울하다. 코딩의 세상에도 사회주의 공산주가 존재한다고 표현하면 너무 과하다 생각할지 모르겠지만, 내 옆에 있던 동료와 소위 열정을 불살랐던 스터디그룹과 한가닥 한다는 개발자의 선호도와 의견들, 무엇보다 개발자를 생각하지 못하게 강제한 회사와 사장의 요구들… 그 모든게 좀 처럼 생각을 자유롭게 적용하지 못하게 서로에게 영향을 끼쳤다.  설령 내가 억울하다 여기는 과거의 시간동안 함수형 코딩을 할 수 있었다해도 동료란 인간들과 무형의 팀원과 일에 치여 귀찮은 팀장과 돈이 궁한 사장의 압력을 버티지 못했을 것이라 생각한다. 그저 늦었지만 지금이라도 제대로 알게된 것에 힘입어 얼마 남지 않았을지도 모를 내 앞의 시간을 함수형 개발을 하며 기쁨을 계속 누려가고 싶다. 

 

함수형 코딩을 배우려는 개발자에게 몇 마디 하자면, 모든 결론은 함수형 코딩이 지켜야할 몇 안되는 이 규칙Rule에 귀결한다는 사실을 반지로 삼아 끼고 학습하면 너무 돌아가지 않고 신속히 적응할 수 있을 것이다:

 

  • Declarative
  • Purity
  • Transparency
  • Immutability

 

위의 네가지 규칙의 코딩by 그리고 규칙을 위해for , 또한 룰에 의해of 코딩하는 방법이 함수형 프로그래밍이다.  학습 초반에 너무 ‘정의’에 매달리지 말고 해당 ‘정의’가 어떤 상황에서 적용되는 것인가를 주의 깊게 보는게 더 낫다. 정의는 결국 이해한 뒤에야 인정하는 동감일 뿐이다. 그러자면 남들이 작성한 FP 스타일 코드를 무조건 많이 보는게 확실한 방법. 무수하게 사용하는 if..then..else 조차도 함수형 방법론에 의해 대체되면 인지적 자유와 해방감을 얻게된다. 곧 한번 코딩하면 그 부분은 논리 오류가 아닌 이상 문제가 없음을 확신하므로 더 이상 자신의 코드에 불안을 가지는 경우가 드물어간다.  작게 쌓아올리지만 결국엔 거대한 모습을 형성하게 됨을 볼 때, OOP는 후쿠시마산 농수산물과 같다는 생각이 들지도 모른다. 값 싸고 그럴듯해 보이는데 내부피폭을 유발하는 OOP.  그 내부 피폭에 해당하는게 OOP의 핵심이론이라니…ㅋ  아마도 깊은 빡침을 느낄지도 모른다.

 

FP를 반대하는 개발자는 OOP의 우수성과 비교하며 제발 OOP에 안주해주길 바란다. 적자생존 아니던가.. 사실 나도 FP를 OOP와 공존시키는 방법이 있는가 엄청 찾아보고 고민했었다.  원래 불가능한 것이며 하지 말아야 할 시도인데도 불구하고 그렇게 끈덕지게 달라붙었던 건, 아직까지 ‘현업’에서 OOP를 쓰기 때문이었다.  역시나 병신 짓을 반복했던 나. 그 ‘현업’이란게 나를 속여온 원흉 중 큰 부분인데 거기에 또 미련을 가진 이유가 뭐냐…  그러나 이제 OOP도 문제되지 않는다고하면 뭔 궤변같은 개소리냐 하겠지만, 사실 그렇다. 문제가 뭔지 알고 난 후에는 얼마든지 피해가는 방법이 보이기 때문이다.  쉽다고는 하지 않는다. 단지 비용이 들어갈 뿐이지. 그 비용은 회사 대표가 지출하는 것이고 문제삼지 않는다면 굳이 거기에 기를 쓰고 반대할 필요가 없다. 

 

시간… 시간이 필요하다.  함수형 코딩론은 이해가 된 시점부터 시간 싸움인 것 같다. 좀만 더 일찍 알았더면…  즐겁게 내가 만들고자 하는 것들을 구현하는 기쁨에 나이가 들던말던 개발자로서 살아가는 재미가 더 했을 텐데..

References   [ + ]

1. https://github.com/marpple/FxJS
2. https://github.com/andrwj/FPJS
3. https://folktale.origamitower.com/
4. https://www.youtube.com/watch?v=3VQ382QG-y4