HIKING- 1000M 는... 서비스 종료다.

 

오늘부터는 알고리즘 풀이에 좀 더 집중하고,

다음 주 부터 진행 될 CH3 과제를 준비하는 시간을 가... 가...

 

가 아니었다...

 

무려 100명이 넘는 수강생 중, 우수 제출자 2명을 선발하여 발표하는데,

그 2명 중 한명이 내가 된 것이다 !!!

 

 

              실화?

 

기왕 이렇게 된 김에,

오늘의 TIL은 발표를 했던 내용을 기반으로

과제의 결과물을 리뷰하는 방향으로 TIL을 작성하는것으로 방향을 틀었다.

 

 

그런데....

 

나의 발표 순서는 두 번째였는데,

앞서 발표한 수강생의 능력치가 심상치가 않았다.

 

 

??????

https://velog.io/@artbiit/NodeJS-%EA%B0%84%EB%8B%A8%ED%95%9C-%EB%A1%9C%EA%B7%B8%EB%9D%BC%EC%9D%B4%ED%81%AC-%EA%B2%8C%EC%9E%84-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EA%B8%B0-%EC%B5%9C%EC%A2%85-%EC%84%A4%EB%AA%85

 

NodeJS 간단한 로그라이크 게임 만들어보기 - 최종 설명

여태까지 간단한 기능들의 모음을 먼저 만들고 게임 시나리오를 구현하는 식으로 소개를 했었습니다.이번에는 스크립트 흐름을 통해서 어떻게 구현했는지 완전히 작성해보려 합니다.이전글에

velog.io

 

위는 심상치 않은 발표자의 블로그 이며, 디렉토리 구성만 보더라도 어나더 레벨이라는 것을 알 수 있다....

 

코딩이 곧 생활 방식이신듯 합니다

 

열심히 만든 내 코드가 부스러기로 보이는 순간이었다..

 

 

아무튼... 내 발표는, 

1. 기획

2. 게임 시연

3. 주요 코드 리뷰

 

순서로 진행이 되었다.

 

 

1. 기획

 

기획에서 컨셉을 제외한 나머지 부분은 사실상 과제에서 fix가 되었기 때문에

readme 에 작성했던 컨셉 위주로 설명을 진행했다.

 

 

2. 게임 시연

 

따로 녹화를 한것이 아니기 때문에 영상은 남아있지 않지만, 

게임을 직접 구동하여 주요 기능을 보여주는 식으로 진행을 했다.

목록은 다음과 같다.

 

 1) 메인화면 등장 연출

2) 전투 진행

 

 

3) 지름길 스킬 연출

 

4) 게임 오버 출력

 

 

 

3. 주요 코드 리뷰

내 코드중에 그나마 쓸만한 것을 몇개 골라서 리뷰를 진행했다.

 

 1) 연출? 텍스트

varcolor ...?? 는 넘어가자

이전 TIL 에서 언급했던 !

오프닝, 엔딩, 게임오버, 스킬성공 연출에 사용된 그 함수이다.

 

위의 함수는 역순 나열로 오프닝에 사용된 함수이고, 

나머지는 정방향으로 나열하는 함수이다.

 

함수 내용을 해석하면 매우 간단하다.

 

첫 번째 매개변수 text에 받아온 임의의 문자열

ex " HIKING-1000M" 를

console.clear();  를 통해 모든 내용을 전부 지우고

첫번째 문자 M 출력 (역순)

다시 console.clear(); 

0M -> 클리어 -> 00M -> 클리어 -> 000M -> 클리어 ........ -> IKING -1000M -> 클리어 -> HIKING-1000M

를 출력하는 단순한 반복문이라고 할 수 있다.

 

특이 사항이라면, 반복문 끝에 await delay(setDelay); 인자를 받아서 

반복출력 간격을 조절할 수 있다. (스킬 성공 연출 출력속도와 게임오버 출력 속도가 다른 이유)

 

이제 여기서 튜터님의 도움을 받아 color 매개변수에 chalk.컬러명 을 인자로 받는 것으로

로그로 출력할 텍스트에 color 를 조절 할 수 있게 되었다. 

 

해당 부분은 

https://p-lani.tistory.com/43

 

8.23일 TIL 에서 기술한 바 있다.

 

 

 

2) 새로운 로그를 딜레이 출력

 

 

전투로그를 출력하는 부분에 대한 내용으로, 8.28  TIL 뒷부분에 작성한 내용이다.

 

oldLogs 의 내용은 즉시 전부 출력. -> 

이전 배틀 과정에서 추가된 logs 는 딜레이를 주어 출력하고, oldLogs 로 복사->

해당함수 실행 후 logs를 초기화 하는것으로 완성한 코드이다.

 

맨 아랫줄인 if문은 logs 가 비어있지 않은 경우, 딜레이를 만들어서

이후 내용인 행동 선택지를 띄우는 시간 사이에도 틈을 만들었다.

 

 

다른 코드들은 발표 시간 문제로, 또 그렇게 특별한 코드는 없기 때문에 스킵을 했고

 

그렇게 발표는 마무리 되었다 !

 

 

 

 

 

해당 과제를 진행하며 마주하게된 문제점과 해결 과정은 매일 TIL에 기록하였기 때문에

따로 회고가 필요하진 않을것 같다.

 

여기서 

 

찐찐찐으로 RogLike  과제는 마무리가 되었다.

 

많은걸 배우고, 또 직접 적용해보기도 하고

수많은 문제점을 마주하며 해결해보는 과정이 모두 재밌었고 의미있는 과제였다 !

 

다음주 부터 시작될 CH3의 과제도 잘 되길 바라는 마음이다.

'내일배움캠프' 카테고리의 다른 글

24.09.02 TIL : Node.js 입문주차  (0) 2024.09.02
24.08.30 TIL : Node.js 입문주차  (0) 2024.08.30
[내일배움캠프] 24.08.28 TIL  (1) 2024.08.28
[내일배움캠프] 24.08.27 TIL  (1) 2024.08.27
[내일배움캠프] 24.08.23 TIL  (0) 2024.08.23

OSI 7계층 이란?

OSI(Open System Interconnection) 7계층은 국제 표준화 기구인

ISO(International Standardization Organization)에서 개발한 

컴퓨터 네트워크 프로토콜 디자인과 통신을 계층으로 나누어

설명한 개방형 시스템 상호 연결 모델이다.

 

 

OSI 7계층 레이어의 특징

1. 물리 계층 (physical Layer)

물리계층은 1계층으로써, 두 시스템간의 데이터 전송을 위해 링크를 활성화 하고 관리하기 위한 기계적, 전기적, 기능적, 절차적 특성 등을 정의하며 허브나 라우터, 네트워크카드, 케이블 등의 전송매체를 통해 비트(bit)들을 전송하는 계층입니다. 물리계층의 데이터 단위는 '비트(bit)' 입니다. 송신측에서는 데이터 링크 계층에서 과 1로 구성된 비트열의 데이터를 받아 전기적 신호로변환 후 전송 매체를 통하여 수신측에 보내게 됩니다. 받는 쪽인 수신측은 송신측으로부터 받은 전기 신호를 0과 1로 구성된 비트열로 복원하여 수신측의데이터링크 계층에 전송하게 됩니다. OSI 모델에서 최하위 계층에 속하며 상위계층에서 전송된 데이터를 물리매체를 통하여 다른 시스템에 전기적 신호를 전송한다, 랜카드, 케이블, 허브, 라우터와 같은 물리적인 것과데이터 전송을 위해 사용하는 전압도 물리계층에 속하게 됩니다.

2. 데이터링크 계층(Data Link Layer) 

2계층인 데이터링크 계층은 물리적 링크를 통해 데이터를 신뢰성 있게전송하는 계층을 말합니다. 하위계층에 속하며 물리계층 바로 위에 위치합니다. 비트들을 프레임(Frame)이라는 논리적 단위로 구성하여 전송합니다. 시스템 간에 오류 없는 데이터 전송을위하여 네트워크 계층에서 받은 데이터 단위(패킷)를 프레임으로 구성하여 물리계층으로 전송합니다. 데이터링크 계층 데이터 단위를 프레임(Frame)' 이라 부릅니다. 즉 프레임은 데이터+ 헤더 + 트레일러로 구성이 되어 있습니다. 송신자의 프레임을 수신자 측에서 받는다면 헤더와 트레일러를 제거한 후에 네트워크 계층으로 전송합니다.

3. 네트워크 계층(Network Layer) 

3계층인 네트워크 계층에서는 패킷을 송신측으로부터 수신측으로 전송하는 구간이며 상위계층에 연결하는데 필요한 데이터 전송과 경로를 선택하는 기능을 제공합니다. 데이터가 전송될 수신측의 주소를 찾고 수신된 데이터의 주소를 확인하여 자신의 것이면 상위 계층인 전송계층으로 전송합니다. 라우팅 프로토콜을 사용하여 최적의 경로를 선택하는 계층구간이기도 합니다. 데이터를 패킷(Packet)' 단위로 분할하여 전송한 후 재결합하는 방식을 쓰고, 데이터 링크 계층이 인접하는 두 노드간에 전송을 담당한다고 하면 네트워크 계층은 각 패킷이 송신지에서 수신지까지 정확하게 전송되도록 경로를 책임지는 구간이다. 네트워크 계층 데이터 단위는 패킷(Packet)' 이라 합니다.

4. 전송계층(Transport Layer)

4계층인 전송계층은 시스템 종단간에 투명한 데이터 전송을 양방향으로 행하는 계층이며 두 시스템간의 신뢰성있는 데이터 전송을 보장합니다. 프로토콜은 TCP, UDP, SPX 등과 관련있습니다. 오류복구와 흐름제어 기능을 담당하는 계층이기도 합니다. 데이터 헤더에는 포트주소 또는 소켓(socket) 주소를 포함하고 있고 순서(squence) 또는 세그먼트 번호가 포함되어 있습니다. 이는 세션계층으로부터 온 데이터를 수신할 때 데이터를 전송할 수 있는 세그먼트로 나누고 수신측에서 수신자가 재조립할 수 있도록 순서를 헤더에 표시하게 됩니다. 네트워크 계층은 전송해야 하는 시스템에게 각 패킷을 전송하는 일을 하고 전송계층은 해당 시스템의 응용 프로그램에게 모든 데이터를 전송하는 역할을 합니다. 전송계층에 있어서 데이터 단위는 '세그먼트(segment)' 라 합니다.

5. 세션계층(Session Layer)

세션계층은 5계층으로써, 응용 프로그램 계층간의 통신에 대한 제어 구조를 제공하기 위해 응용 프로그램 사이의 접속을 설정, 유지, 종료시켜주는 역할을 하는 계층입니다. 통신 장치들 간의 설정을 유지하며 동기화하고, 데이터 단위를 전송계층으로 전송할 순서를 결정하고 데이터에 대한 점검 및 복구를 위해 동기를 위한 위치를 제공합니다. 또한 세션을 종료할 필요가 있을 때 적절한 시간을 수신자에게 알려 줍니다. 세션계층의 데이터 단위는 'n메시지(message)' 입니다.

6. 표현계층(Presentation Layer) 

6계층인 표현계층은 데이터 표현차이를 해결하기 위하여 서로 다형식을 변환해 주거나 공통형식을 제공해 주는 계층을 말합니다. 송신측과 수신측 사이에 표준화된 데이터 형식에 대해 규정합니다. 예를 들면 이미지가 bmp인지, jpg 인지, 압축이 14었는지 등의 표현과 관련된 구분을 지을 수 있습니다. 이는 두 시스템간에 서로 다르게 사용하는 문자 및 그래픽 문자 등을 위해 번역을 수행하며 전송데이터를 서로 이해할 수 있도록 하는 것이 목적입니다. 보안을 위하여 송신측에서 암화하고 수신측에서 복호화하며 전송률을 높이기 위하여 압축을 합니다. 표현계층에서 데이터 단위는 메시지(message)' 입니다.

7. 응용계층(Application Layer) 

OSI 모델에서 마지막모델인 응용계층은 가장 상위계층에 속하며 실제 통신의 최종 목표에 해당하는 가장 중요한 계층입니다. 사용자로부터 정보를 입력받아 하위계층으로 전달하고 하위계층에서 전송한 데이터를 사용자에게 전달하는 기능을 합니다. 응용프로세스(사용자, 응용프로그램)가 네트워크에 접근하는 수단을 제공하여 서로 간에 데이터를 교환할 수 있는 창구 역할을 합니다. 파일 전송, DB, 원격접속, 메일전송 등 응용서비스를 네트워크에 접속시키는 역할을 담당하는 계층입니다. FTP, SMTP, POP3, HTTP와 같은 프로토콜이 관련있고 응용계층의 데이터 단위는 메시지(message)' 입니다.

 

 

 

 

기존의 코드에서 수정한 부분이 있다. 

monster 를 생성하는 과정에서

class 를 적절히 활용하고 있다는 느낌이 들지 않았다.

      const monster = new Monster(RandomName[RandomNameChoice], 70, 5);

      //10층 막보
      if ( stage % 10 === 0 && stage !== 0 ) {
      monster.name = "최종BOSS : 정상으로 향하는 길"
      monster.hp = 2999
      monster.attack = 180
      }
      //5층 중보
      else if ( stage % 5 === 0 && stage !== 0 ) {
      monster.name = "중간BOSS : 계단지옥"
      monster.hp = 777
      monster.attack = 77
      }
      //일반 층
      else {
        monster.hp += stage*80+stage**3
        monster.attack += stage*1+stage**2

 

뭔가 마음에 들지 않는 구성이다.

일단 대충 때려만든 다음에 수치를 조작하는 형태라고 느껴짐...

 

특히, 채점 기준에서 벗어날 수도 있다는 생각이 들어서 코드를 수정해보기로 했다.

 

 

1차 시도

원하는 그림은

new Monster 생성 과정에서 new Monster( 정보 ) 인자를 넘겨서 

그것을 class 내에서 처리하게 만드는 것이다.

 

//10층 막보
    if (stage % 10 === 0 && stage !== 0) {
      const monster = new Monster("최종BOSS : 정상으로 향하는 길", 2999, 180)
    }
    //5층 중보
    else if (stage % 5 === 0 && stage !== 0) {
      const monster = new Monster("중간BOSS : 계단지옥", 777, 77)
    }
    //일반 층
    else {
      const monster = new Monster(RandomName[RandomNameChoice], stage * 80 + stage ** 3 + 80, stage * 2 + stage ** 2 + 8)
    }

 

위 처럼 작성을 하고 보니,

 

 

monster 를 찾을 수 없다는 결과가 나왔다.

 

if문을 지나간다면 어느 경우라도 const monster 를 실행하게 되겠지만, 

if 문 내 에서만 정의가 된 변수는 참조가 불가능한 듯 보인다...

 

아니 if문 해보면 무조건 const 된다니까? << 그건 니 생각이고 !!

 

추측하자면, if문이 실행되기 전에 선언된 함수, 변수 (인자 포함)  정보를 호이스팅 하는데

if 문이 아직 실행되지 않았기 때문에 정의 자체가 되지 않은것으로 보인다.

 

2차 시도...라고 하긴 애매하지만

if 문이 실행되기 전에 초기화만 해주면 일단 monster = undifined 상태로 실행이 진행될것이고, 

if 문을 들어가며 원하는 값으로 대입이 될 것이다.

 

하지만 보란듯이 밑줄이 그어졌다.

const 는 선언과 동시에 초기화가 이루어져야 한다...

사실 재선언, 재할당 모두 불가능한 const에 값을 안주고 선언만 하는건 말이 안되는 일이었다.

 

 

3차 시도 

왜 인지...

const 로 선언하지 않으면 안될거같은 느낌이 들지만....

다양한 시도를 해봐도, 원하는 형태로 코드를 짜려면 let 을 써야할 듯 하다..

let 괜찮은거겠지..?

let monster;
    //10층 막보
    if (stage % 10 === 0 && stage !== 0) {
     monster = new Monster("최종BOSS : 정상으로 향하는 길", 2999, 180)
    }
    //5층 중보
    else if (stage % 5 === 0 && stage !== 0) {
     monster = new Monster("중간BOSS : 계단지옥", 777, 77)
    }
    //일반 층
    else {
     monster = new Monster(RandomName[RandomNameChoice], stage * 80 + stage ** 3 + 80, stage * 2 + stage ** 2 + 8)
    }

 

 

결국 위와 같은 형태로 마무리 하기로 했다.

 

class Monster {
  constructor(name, hp, attack) {
    this.name = name
    this.hp = hp
    this.attack = attack
  }
  ........

class 부분은 위와 같다.

 

 

 

그리고 바로 이 시점에서,  제출한 과제에 대한 피드백을 받았다.

피드백의 내용을 요약하면 다음과 같다.

 

우선 참신한 컨셉에 놀랐고,

필수기능과 도전 기능에 해당하는 부분을 모두 성공적으로 구현해내었음.

도박성 기술인 지름길 스킬에 실패 할 경우, 주사위를 표시하여

직관적으로 확인할 수 있는 부분이 좋았음. 

사실 별 생각 없이 만든 코드

 

아쉬운 점으로, 반복되는 코드가 많다는 점 !

 

또, 깃 활용 시 커밋 내용을 확실하고 직관적으로 알 수 있게 작성하여

히스토리로써도 관리되게끔 할 수 있으면 좋겠음

 

 

 

전체적으로 과제 결과물이 괜찮았다는 내용이었다 !

어흐흘

 

헛되지 않았다......

 

즉각적으로 피드백을 적용해보자면,

제출했던 스테이지 클리어 보상 함수 내용이다.

확실히 초 하드코딩으로 반복되어 지저분해 보이는 부분이다.

 

반복문을 통해 간소화를 해볼까?

// 불필요한 반복 간소화
  await delay(0.2)
  console.log(chalk.cyanBright.bold(`${stage === 6 ? `${monster.name} 돌파 !! 보상을 5회 획득합니다.`:` 랜덤 보상을 3회 획득합니다.  `} \n`));
    for(let i = 0; i < (stage ===6 ? 5 : 3); i++) {
      RandomReward(player, stage);
      await delay(0.2)
    }

 

for문을 통해 간소화 하고, 추가로 로그 부분은 삼항 연산자를 통해 출력하도록 개선했다.

 

 

문제는 다음인데, 

일이 커질것 같은 예감이 든다...

 

이미 코드를 짤 때에도, 이게 최선일까 고민했던 부분이다.

 

저렇게 코드를 만들었던 이유는,

전투 로그 출력에 딜레이를 주고싶었던 작은 소망에서 시작된 것인데.. 

 

그건 결코 간단한 일은 아니었다.

전투 로그는 화면을 초기화 하고 logs.forEach ~~ 가 실행 될 때에 갱신이 되기 때문에,

전투 도중에 logs에 push 한다고 해서 즉각적으로 보여지는게 아니다.

 

따라서 내가 선택한 방법은 (현재의 내 머리에서 나올 수 있는 유일한 방법..흑흑)

 

위 처럼 매번 전투 logs가  push 되는 상황마다

console.clear 와 상태창 불러오고, 전투로그를 불러오는

이른바 짜치는 코드를 짜버렸던 것이다.

 

당연히 해당 코드를 간소할 방법 또한 생각 해 낼 수 없었다.

그렇기 때문에 저 상태로 제출한 것이니까 !!

 

 

결국, 그대로 다시 튜터님에게

진실의 방으로

 

 

내가 생각하지 못하고 있었던 부분을 바로 체크해 주셨다.

어....음...... 이 부분을 말로 설명하려니까 굉장히 어려운데,

 

결국 그렇게 보이기만 하면 된다??

 

기존에 작성한 방법은 logs 가 push 될 때마다 초기화 해서 그때 그때 출력하는 방법이고,

 

튜너님은 결국 그런 (짜치는) 과정 필요 없이 기존에 있던 메서드 logs.forEach ~~에서

새로 추가된 logs 에 해당하는 log만 딜레이로 출력되게 하면 보이는 건 똑같다는 말을 해주셨다.

 

      헉 !!!!

 

사실, 생각지도 못한 부분이었기 때문에, 처음 설명을 들었을땐 (???????????) 상태였다...

 

 

결국

이 녀석이 로그를 뱉어낼 때, 새로 추가된 log를 뱉을 때만 딜레이를 주게 하면 된다

이 말입니다 !!

 

그렇게 하면 초기 셋팅 처럼, 전투로그를 턴마다 한번 만 쏟아내게 되지만

눈에 보이게 되는건

로그 초기화(순식간이라 안보임)-> 기존 로그 한번에 쫙( 이전 log와 그대로인 것 처럼 보임)->

 추가된 log 는 딜레이되어 출력 (새로 등장하는것 처럼 보임)

 

이렇게 되는것이다.

 

왜 이 생각을 못했을까?

 

바로 시작해보자.

 

// 화면 초기화
function displayReset(stage, player, monster, logs) {
  console.clear();
  displayStatus(stage, player, monster);
  while (logs.length > 14) { logs.shift(); }
  logs.forEach((log) => console.log(log));

}

이런 흉물은 더 이상 필요 없게 되었다. 

 

 

시작은

 

솔직히, 뭐라고 하는지 잘 모르겠는 위의 친구를 평범한 함수로 바꾸는것 부터 시작이다.

 

 

// **NEW**  전투로그 출력 함수
async function addLogs (logs, oldLogs) {
  for (let log of oldLogs) {
    console.log(log)
  }
  for (let log of logs) {
    await delay(0.2)
    console.log(log)
    oldLogs.push(log)
  }
  await delay(0.3);
}

 

oldLogs 는 전부 즉시 출력, 

logs 는 딜레이로 출력하고, 

logs 에 있던 내용을 전부 oldLogs 에 복사한다 .

해당 함수 실행 후 logs 를 비워주면

 

완성 !!

 

 

이제, 새로 logs 에 push 한 문자열은 알아서 딜레이로 출력되게 된 것이다.

 

 

이랬던 코드가  (한 화면에 담기지도 않음)

 

 

 

 

이렇게 바뀌었다 !! 장족의 발전

 

이 영광을 튜터님께......

 

 

피드백 받은 내용을 바로 내용에 담으려다 보니 

TIL 이 좀 길어진 감이 있는데,

 

아무튼 이번 로그라이크 과제는 여기서 마무리가 되었다 !

추가로 쓰고싶은 내용이 생각나면 한번 더 다룰지도?

 

 

 

 

'내일배움캠프' 카테고리의 다른 글

24.08.30 TIL : Node.js 입문주차  (0) 2024.08.30
[내일배움캠프] 24.08.29 TIL  (0) 2024.08.29
[내일배움캠프] 24.08.27 TIL  (1) 2024.08.27
[내일배움캠프] 24.08.23 TIL  (0) 2024.08.23
[내일배움캠프] 24.08.22 TIL  (0) 2024.08.22

 

 

 

과제 진행 상황

구현하고 싶었던 기능은 전부 구현했고, 이쯤에서 이번 과제를 마무리 하기로 했다.

 

https://github.com/P-lani/sparta_RogLike

 

깃 허브에 최종merge 가 완료된 상태이다. 원한다면 플레이가 가능하다 !

아래는 readme 에 쓴 내용이다.

 

## **개발 프로세스**

**도전 기능 부분**
0. 찾기 쉽도록 위쪽에 쓰겠습니다

1. 확률 로직
 1) 플레이어의 공격력과 최대배율에 따른 난수 데미지 / 몬스터의 공격력 또한 1.4배의 난수를 가짐
 2) 축지법 ! 스킬은 일종의 카운터 스킬로, 성공확률이 존재
 3) 필살! 암벽등반! 스킬은 연속공격 스킬로, 5~10회 랜덤횟수를 공격
 4) 지름길 개척 스킬은 도망 스킬로, 10%의 확률(고정)로 성공함
 4) 스테이지 클리어 보상 5가지중 3개를 랜덤 획득하며 (중복 가능)
    각각의 보상은  0.8배~2.0배의 난수를 가짐
 5) 몬스터의 name은, 따로 설정한 배열에서 랜덤으로 선택함
 6) 몬스터의 패턴(체력 소모)는 확률에 따라 상이하게 발생 (빗나감, 약화된 공격, 평범한 공격, 강력한 공격, 아주 강력한 공격)

2. 복잡한 행동 패턴
 1) 필살! 암벽등반 ! 기술은 확률로직 외에도, 충전을 3개를 소모해야 사용할 수 있으며,
    충전은 stage clear 랜덤 보상으로만 획득할 수 있습니다. (원활한 Test를 위해 3충전을 가지고 시작합니다.)
    추가로, 확률 로직과 delay 텍스트 출력 등등 약간의 연출이 추가되어 있습니다.

 2) 지름길 개척 기술은, 성공시 연출효과...를 출력합니다.

 3) 위의 연출 효과를 응용하여 함수로 만들었고, 시작 시(오프닝), 엔딩, 패배 화면에 적용했습니다.

###  게임 기획 분석**


**컨셉 잡기**

1. 등산을 컨셉으로 제작한 게임입니다.
플레이어 : 등산가
플레이어의 체력 : 체력
플레이어의 공격력 : 등산력(...)

몬스터 : 등산로
몬스터의 체력 : 남은 거리
몬스터의 공격력 : 체력 소모량

2. 컨셉만 약간 특이할 뿐, 구현내용은 크게 다르지 않습니다.

다만 !
체력이 0이 된 몬스터는 공격을 못하겠지만,
남은거리가 0이되는 등반은 체력을 소모합니다.
때문에, 마지막 일격에도 반격을 받는 느낌이 되었고 의도된 구현입니다. (더블 KO 는 패배 처리)



**전투 기획**

1. 플레이어= 등산가 의 행동 선택지

    1. 등반하기 : 100% 확률로 등산력에 해당하는 거리를 등반. 이후 체력 소모(상대 턴) 발생합니다.

    2. 축지법 ! : 기본35% 확률로 등산력에 해당하는 거리를 등반. 성공시 체력을 소모하지 않습니다. 실패시 체력소모만 발생합니다.
        35% 확률로 굉장히 저조한 성능이지만, 이후 랜덤 보상을 통해 확률이 증가할 수 있습니다.

    3. 필살! 암벽등반! : 최대 충전3, 초기 충전3, 사용시 충전 3 소모.
        등산력에 해당하는 거리를 5~10회 랜덤횟수 등반합니다.  충전은 랜덤 보상에서 획득 가능합니다.

    4. 지름길 개척 : 도망에 해당하는 기술로, 낮은 확률(10%) 로 성공하여 스테이지 클리어로 판정하고 보상 또한 정상적으로 획득합니다.
        => 이는 사실상 즉사기에 해당하기 때문에, 성공시 등산로의 남은 거리를 0으로 만들고 체력 소모 턴이 발생하지 않습니다.
        실패시 체력 소모만 발생.


2. 플레이어의 능력치
    1. 체력 : 0이 되면 게임오버가 되며, 스테이지 클리어시 확정적으로 일정량을 회복합니다.
        추가적으로 랜덤 보상에서도 획득할 수 있습니다.

    2. 등산력 : 공격력에 해당하며, 등산력 ~ 등산력 x (1+최대배율/100) 의 난수를 가집니다.
        확정 증가는 없으며, 랜덤 보상을 통해 상승할 수 있습니다.

    3. 최대배율 : 최대 공격력 배율에 해당하며, 계산은 위와 같습니다. 기본 20의 최대배율로 시작합니다. (1.2배)
        확정 증가는 없으며, 랜덤 보상을 통해 상승할 수 있습니다.

    4. 축지법 확률 : 축지법 ! 스킬의 성공확률이며, 35%로 시작합니다.
        확정 증가는 없으며, 랜덤 보상을 통해 상승할 수 있습니다.
        최대 75% 의 상한을 가지고 있습니다.

2. 몬스터 = 등산로의 패턴

    1. 엄밀히 따지면, 몬스터가 완전한 턴을 받는 형식의 구현은 아니고,
        플레이어의 행동에 따라 몬스터의 공격 이벤트가 발생하는 방식입니다.

    2. 체력을 소모할 때, 등산로(monster) 의 공격력을 참조하며, 등산로의 공격력은 1 배 ~ 1.4배 난수를 갖습니다.

    3. 추가적으로 확률에 따라 체력 소모가 0.6배 , 1배, 1.3배, 1.6배, 2배, 체력소모 0 이 되는 이벤트가 발생합니다.
        확률은 각각 상이합니다. (낮은 확률로 컨셉이 파괴되는 텍스트가 출력됩니다. )
       
    4. 5층에서는 중간 BOSS 를, 10층에서는 최종BOSS 를 조우합니다.
        BOSS 는 전용 name 을 가지고, 별도의 체력과 공격력을 가집니다.

    5. BOSS 이외의 몬스터는 지정된 값(배열) 중 랜덤한 name을 갖습니다.
        (name중 일부는 컨셉이 파괴되는것도 있습니다. )

    6. 등산로(monster)는 stage 마다 증가한 체력과 공격력을 갖습니다.

    7. 등산로(monster)의 남은거리(hp)가 0 이 될 경우, stage가 클리어 되며,
        랜덤 보상 3가지를 획득하고 다음 stage 로 넘어갑니다.

    8. 중간 BOSS 클리어시엔 랜덤 5가지 보상을 획득하며,
        최종 BOSS 클리어시엔 엔딩이 출력됩니다.


3. 랜덤 보상

    1. stage 를 클리어하여 랜덤한 보상 3개를 획득합니다. (중간BOSS 클리어 시 5개)

    2. 체력 회복 / 등산력 / 최대 배율 / 필살 충전 / 축지법 확률
         종류는 총 5가지 이며 각각 동일한 확률을 가집니다.

    3. 체력 회복, 등산력, 최대배율은 stage 에 따라 기본 보상량이 증가합니다.  
        또한 0.8배~ 2.0배의 난수가 적용됩니다.

    4. 축지법 확률은 stage 에 상관없이 고정된 보상량을 갖지만,
        마찬가지로 0.8배~ 2.0배 난수가 적용됩니다.

    5. 모든 보상은 현재 수치 -> 증가된 수치 , 증가량을 표기합니다.





**추가적인 요소**

1. 전투로그가 14줄을 넘어갈 경우 먼저 출력되었던 로그가 제거되어 14줄이 유지됩니다.

2. 지름길 개척  스킬 성공 시, 연출 효과를 추가했습니다.
    반복문을 통해, 콘솔 클리어와 문자열 출력을 반복합니다.

3. 위의 연출을 함수화 시켜서 GAME-OVER, GAME-CLAER 에도 적용 했습니다.
   
4. setTimeout 을 사용하는 delay 함수를 사용하여, 로그 출력간에 delay를 적용했습니다.


 

readme 에는 기획과 구현기능에 관한 내용을 담아보았다.

 

 

과제 마무리에 관한 내용은 과제 제출날짜인 내일 TIL로 작성할 계획이다.

 

오늘은?

오랜만에 알고리즘 풀이 시간을 가져 보았다. 

물론, 과제 진행중에도 알고리즘 풀이시간을 갖긴 했지만,

TIL로 작성하기엔 시간도 양도 부족했었다.

 

그럼 시작해볼까?

 

 

뭐지 언어 영역인가...

 

시작부터 문제 설명만 10줄이 넘어가는 알고리즘 문제를 만났다.

 

쉽게 설명하면....

[1,3,4,6] 에서

index 0 = 1 개

index 1 = 3 개

index 2 = 4 개

index 3 = 6 개 

의 숫자가 존재하며 이를 풀어서 나열하면 0 111 2222 333333 이 되고, 

이 목록을 0을 기준으로  반으로 쪼개서 1223330333221 을 출력해야하는 상황이다.

이 때, 1은 3개 이기 때문에, 반으로 쪼개고 남은 1개는 버려지게 된다.

 

가장 쉽고 직관적인 방법으로 접근해보자.

1.

index 1~끝까지, 해당 index의 value의 절반만큼(소수점 버림) index를 빈 문자열 answer에 추가

 answer = "122333" 

2.

index 0 을 value 만큼 추가. ( 문제에서 0은 1개로 고정이라고 했기 때문에 arr += 0 을 해도 같다)

3.

answer = "1223330"

그 후, 1에 사용한 방법을 역순으로 적용

index의 끝에서~1 까지, 해당 index의 value의 절반만큼 어쩌구 저쩌구

answer = "1223330333221" 

 

벌써부터 O(n^2 *2 )  급의 코드가 만들어질것 같은 예감이 든다.

당장 해보자 !

 

function solution(food) {
    var answer = '';
    for(let i = 1; i < food.length; i++) {
        for(let j = 0; j < food[i]/2; j++ ){
            answer += i
        }
    }
    return answer;
}

console.log(solution([1,3,4,6]))  // "1122333"  실패 !
// 홀수value 를 가진 경우 +1회가 더 출력되는 모습

계획중  1에 해당하는 부분 이다.

위 처럼 코드를 짰더니, food[i] /2 에 홀수가 들어갈 경우 절반 +1 회 출력이 되어버린다.

 

function solution(food) {
    var answer = '';
    for(let i = 1; i < food.length; i++) {
        for(let j = 1; j <= food[i]/2; j++ ){
            answer += i
        }
    }
    return answer;
}

console.log(solution([1,3,4,6]))  // "122333"

소수점을 고려하지 않도록 위처럼 수정해 주었다.

 

그 후, 0을 추가하고 다시 뒤에서 부터 반복 !

 

function solution(food) {
    var answer = '';
    for(let i = 1; i < food.length; i++) {
        for(let j = 1; j <= food[i]/2; j++ ){
            answer += i
        }
    }
    answer += 0
    for(let i = food.length; i > 0 ; i--) {
       for( j = 1; j <= food[i]/2; j++) {
           answer +=i
       }
    }
    return answer;
}

계획한 그대로 코드가 되었다...

잘 작동하는 모습...

 

여기서 끝낸다면 취업의 길은 O=(food.length-1 ^2 *2) 만큼 멀어졌다고 볼 수 있다.

취업과 가까워 지기 위해 검색을 해본 결과, repeat() 함수를 사용하는것이 좋아보였다.

str.repeat(n) 메서드는, str 을 n번 만큼 반복 출력하는 메서드로, 

이 알고리즘 문제에 특화된 메서드라고 볼 수 있다.

심지어 소수점을 과감하게 버리는 대담함 까지 가지고있다.

 

이를 이용해서 2중 반복문을 1중 반복문으로 간소화 할 수 있다.

 

function solution(food) {
    var answer = '';
    for(let i = 1; i < food.length; i++) {
        answer += i.repeat(food[i]/2);
    }
    answer += 0
    for(let i = food.length; i > 0 ; i--) {
        answer += i.repeat(food[i]/2);
    }
    return answer;
}

 

에러 발생 

i.repeat() is not function 

 

앗... i는 문자열이 아니었다. 어차피 참고용 i이기 때문에 문자열로 전환해주자.

 

function solution(food) {
    var answer = '';
    for(let i = 1; i < food.length; i++) {
        answer += i.toString().repeat(food[i]/2);
    }
    answer += 0
    for(let i = food.length; i > 0 ; i--) {
        answer += i.toString().repeat(food[i]/2);
    }
    return answer;
}

보다 깔끔하고, 2중 for문을 사용하지 않게되어 효율적이게 되었다.

작동도 잘 된다.

 

function solution(food) {
    var answer = [];
    for(let i = 1; i < food.length; i++) {
        answer.push(i.toString().repeat(food[i]/2));
    }
    return answer.join("")+"0"+answer.reverse().join("")
}

 

억지로 줄인다면 위처럼 배열로 제작한 뒤 조립하여 return 할 수도 있다.

코드가 줄어든 것 같지만 메서드도 많이 쓰고 배열이 문자열 보다 많은 메모리를 쓰기 때문에

효율적인 코드는 아니라고 할 수 있다.

 

 

다른 사람이 작성한 코드

 

헉... 위에 작성한 나의 코드와 굉장히 유사한 형태의 코드가 눈에 띄었다.

다만, 문자열 방식을 그대로 사용하고

리턴 과정에서  [...res].reverse().join('') 이라는 엄청난 기술로 

즉각적으로 문자열을 뒤집어서 출력하는 방법을 사용했다.

 

주목할 점은

[...res] 인데, 아주 간단한 방법으로 문자열 쪼개서 배열로 만드는 최신 기술(?) 이 존재했다.

난 왜 이걸 모르고 있었던 걸까???

 

let arr = [1,2,3]

...arr = 1 2 3

... 을 통해서 배열을 분해하여 나열할 수 있는것은 알고 있었지만

거기에  [...arr] 을 한다고 다시 배열이 될 거란 생각은 못했다.

당연히 arr = [1 2 3] 이라는 이상한 녀석이 될 거라고 생각했었기 때문이다.

 

이상하다.. 구조 분해 할당 강의를 분명히 들었는데

머리속에서 undefined를 출력하고있다. (버그발생)

 

남는 시간을 활용해서 해당 부분을 다시 복습해야겠다.

 

 

 

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.29 TIL  (0) 2024.08.29
[내일배움캠프] 24.08.28 TIL  (1) 2024.08.28
[내일배움캠프] 24.08.23 TIL  (0) 2024.08.23
[내일배움캠프] 24.08.22 TIL  (0) 2024.08.22
[내일배움캠프] 24.08.21 TIL  (0) 2024.08.21

1. 컴퓨터 세계에서 서버와 클라이언트는 무엇인가?

2. 웹 어플리케이션 서버와 게임 서버의 공통점과 차이점은 무엇인가?

 

 

 

 

1. 서버와 클라이언트

서버란?

특정한 서비스를 제공하는 '서비스 제공자'의 역할로, 클라이언트로부터 요청받은 내용을 처리하고,

결과를 다시 클라이언트에게 전달해주는 역할을 합니다.

 

흔히 생각하는 서버의 모습

서버는, 수많은 서버용 컴퓨터로 이루어진 집합체가 될 수도 있고,

그저 하나의 컴퓨터로 이루어 질 수도 있습니다.

심지어, 가정용 컴퓨터 또한 서버가 될 수도 있습니다.

 

 

 

클라이언트?

클라이언트는 말 그대로 '사용자' 에 입장에 있는 역할로, 서버에 데이터를 요청하여 데이터를 받아오거나,

서버에 데이터를 저장하거나 하는 데이터의 사용자 입니다.

 

이 둘의 관계는 대표적으로 음식점으로 설명하는 경우가 많은데,

저는 도서관으로 설명되는게 좀 더 좋을것이라 생각됩니다.

 

도서관에는 수많은 책이 있고, 그걸 관리하는 직원(사서) 그리고 책을 빌리러 온 사람이 있을겁니다.

 

일반적으로 봤을 때,

책을 빌리러 온 사람 = 클라이언트

도서관의 수많은 책 = 서버

그리고 직원 = 네트워크 라고 할 수 있습니다.

 

책을 빌리러 온 사람은, 직원을 통해 자신이 필요한 책(정보)를 요청하게 되고,

네트워크는 그 요청받은 책을 도서관 내에서 찾아올 것입니다.

여기서 네트워크는, 클라이언트와 서버간에 필요한 정보를 전달해주는 수단이 됩니다.

 

 

 

2. 웹 어플리케이션 서버와 게임 서버

 

웹 어플리케이션 서버란, WAS (Web Application Server) 라고도 하며,

서버내에 있는 다양한 알고리즘, 비즈니스 로직, DB 조회 등 클라이언트 요청에 따라

동적인 컨텐츠를 제공하는 서버를 말합니다.

 

웹 서버와 웹 어플리케이션 서버?

WS(Web Server) 는, HTTP 요청을 받아 Static contents 

즉, 정적인 컨텐츠를 WAS를 거치지 않고 즉시 제공하는 서버입니다. 동적인 Contents 는 WAS 에게 넘기고,

정적 콘텐츠를 가능한 빠르게 제공하는 역할을 합니다.

WS 와 WAS 빠른 정보 처리를 위해, 비 동기적으로 정보를 처리하기 위해 분리되었다고 할 수 있습니다.

 

 

게임 서버도 위에서 설명한 것처럼, 클라이언트(들) 과 정보를 주고받는 역할을 하는 서버입니다.

웹 서버도, 게임 서버도 클라이언트가 요청한 정보를 서버가 처리하며 원하는 데이터를 돌려주는

역할을 하는 것입니다.

 

그렇다면 웹 서버와 게임 서버는 어떤점이 다를까?

 

웹서버와 게임서버의 가장 큰 차이점은,

실시간 ! 한마디로 표현 할 수 있을것 같습니다.

 

일반적으로 웹 서버는, 클라이언트가 정보를 요청한 것을 건네주고 다면, 다음 요청이 있을 때 까지

따로 클라이언트와 소통하는 것이 없습니다... 대부분은 말이죠.

 

실제로, 웹 서핑을 하는 도중에 인터넷이 끊겨버리더라도, 현재 보여지는 페이지 (이미 받아온 데이터) 는 

다른 요청을 하기 전 까지는 별 문제없이 작동하는것 처럼 보입니다.

 

하지만, 게임서버는 웹 서버와 다르게 많은 데이터들이 실시간으로 반영되어야 하기 때문에,

클라이언트가 요청을 지시하지 않아도 수많은 데이터를 서버로부터 받아오고, 또 보내줘야 합니다.

 

이 부분 또한 게임 서버와 웹 서버의 대표적인 차이점이라고 생각합니다.

클라이언트가 직접 요청하지 않아도 클라이언트와 서버간에는 수많은 데이터가 계속적으로 오가야 합니다.

 

정리하자면,

 

공통점

웹 서버와 게임 서버 모두 클라이언트의 요청을 서버가 받아 데이터를 저장하거나, 

보내줘야할 데이터를 클라이언트에게 보내주는 역할!

 

차이점

1. 웹 서버는 데이터의 생산성과 확장성에 중점에 둔다.

2. 게임 서버는 데이터의 변화량과 응답속도를 중점에 둔다.

3. 게임 서버는 클라이언트가 전송한 데이터 (패킷)에 서버가 반드시 응답을 보낼 필요는 없다.

4. 좀 더 쉽게 말하자면, 웹 서버는 말그대로 웹 페이지를 위한 서버로 발전되었고,

    게임 서버는 게임을 위한 서버로 발전한 것이라고 할 수 있다 !

 

 

특이사항

자료를 조사하며 알게된 내용 중, 게임서버에 웹 서버를 사용할 수 있다는것을 알게됐습니다.

즉, 게임 내에서 실시간, 동적인 움직임이 필요하지 않는 게임의 일부분, 혹은 전체가

웹서버로 구성될 수 있다는 사실입니다. 예를 들면 하루에 한번 갱신되는 유저 랭킹 이라던가,

뭐 길드 순위 같은 데이터에는 웹 서버를 사용한다던가,

또는 실시간으로 진행되지 않은 게임 (특히 모바일)이 웹 서버를 사용한다고 합니다.

 

웹 서버가 가지는 강점과 게임(MMORPG의) 서버의 강점을 잘 알고, 

목적에 맞게 필요한 서버를 사용하는것이 중요한 것 같습니다.

 

 

 

 

 

오늘은 구현하고 싶었던 기능을 하나 구현 해보고,

지저분한 코드를 깔끔하게 정리하는 시간을 가져보았다.

 

 

상당히(?) 개선된 메인 화면

어제 예상보다 오래걸렸던 setTimeout.. 이녀석을 좀 더 활용해 보기로 했다.

 

 

4. 지름길 개척에 성공할 경우 출력되는 화면이다.

figlet 모듈을 사용하여 간단하게 문자열을 표현하는 코드인데,

 

시작 화면에서 출력되던 HIKING 의 그것이다

 

이 figlet 출력에 딜레이를 줘 보자.

일종의 연출효과를 얻을 수 있고, 코드에 대한 학습도 될테니 일석 이조 !

 

크게 어려운 부분은 없었다.

delay 함수는 이미 전역에 뿌려두었고, 어제 했던것 처럼, 호출부에 await 를 걸어주고

async를 함수선언 앞에 달아준 뒤 await delay를 적당히 써 주면 된다.

 

문자 출력 부분은, for 문을 통해 구현했고,

주어진 문자열을 0인덱스부터 하나씩 추가하며

console.clear 와 출력을 반복하면 된다 . 

 

 

 

만들고 보니 꽤 그럴싸한듯 !

 

 

그래서, 패배시 game-over 의 표현이나,

엔딩에도 적용하면 좋을것 같다는 생각이 들었다.

 

하드코딩 결과물을 다시 함수로.. 만들어보자.

변동이 필요한 부분만 인자로 받게 만들면 끝이겠죠? 

 

 

// TEST 문자 출력
async function TestText (text, setDelay, color) {
  let TestTextMes = ""
  for (let i = 0; i < text.length ; i++){
    console.clear()
    TestTextMes += text[i]
    console.log(chalk.red(('='.repeat(75))));
    console.log(chalk.red(figlet.textSync(TestTextMes)));
    console.log(chalk.red(('='.repeat(75))));

await delay(setDelay);
}
await delay(0.5);
}

 

뭔가... 뭔가 부족하다 !

 

위에 보이는 것 처럼, color 또한 임의로 지정할 수 있었으면 좋겠는데...

 

불이 꺼지는 매개변수 color .. 

매개변수 : 아 되겠냐고 ㅋㅋ

 

어떻게 이해해야 하는 부분일까?

 

쉬울줄 알았는데...

콜백함수 쓰면..? 아니면..메서드를 통쨰로? 아니면.. 

되긴 해?

 

 

결국 검색과 고민 끝에, 튜터님에게 질문하기로 했다 !

 

chalk.color 를 통째로 인자로 받아오면 가능하다는 것이다 !!

 

저 위의 되겠냐고의 방법은,

변수chalk와 실행() 사이에 매개변수가 들어가서 뭐 어쩌라는건지 모르겠는 상황이 되지만, 

 

chalk.color 를 받아넣으면 함수() 와 같은 방식으로 실행처리가 가능해지기 때문에, 

정상적으로 기능을 한다는 것이었다 !!

 

 

이것은 고차 함수의 작동에 해당하는 방법으로 

내 머리로 이해할 수 있는 부분은

매개변수가 함수명을 인자로 받아오면

본문에서 매개변수() 를 통해  그 함수를 실행시킬 수 있다고 이해가 되어진다.

 

 

맞..나?

 

 

결론

 

JS의 펑션.. 당신은 대체...

이해가 다 안되는데도 놀라운 힘이야 !

(솔직히 이것도 의도한 기능인지 의심됨)

 

 

추가로, 조심해야 할 부분이 있다면,

모듈의 함수마다 바인딩하는 대상이 다를 수 있다고 하셨는데, 

그럴 땐 바인드까지 해버려서 인자로 넣어버리면 green이 집을 찾는것을 도와줄 수 있다고 하셨다 !

 

위 과정으로 만든 함수로, 게임 오버 출력과, 게임 클리어 출력까지 해먹으면 될 듯 하다.

 

음.. 초기 시작 화면 (오프닝) 까지 날로 먹으면 좋을것 같다 !

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.28 TIL  (1) 2024.08.28
[내일배움캠프] 24.08.27 TIL  (1) 2024.08.27
[내일배움캠프] 24.08.22 TIL  (0) 2024.08.22
[내일배움캠프] 24.08.21 TIL  (0) 2024.08.21
[내일배움캠프] 24.08.20 TIL  (0) 2024.08.20

 

 

 

 

어제에 이어서, 과제를 계속 진행했다.

 

사실상, 목표로 했던 부분은 다 완성을 했다고 볼 수 있다 !

 

 

시작 화면은 어제와 같지만 !

 

 

1. 등산 시작을 통해 첫 스테이지로 진입 할 수 있다.

보이는 기능들은 전부 구현이 된 상태이다.

 

1. 등반하기  (평타) 를 통해, 스테이지를 클리어한 모습

 

 

스테이지를 클리어 할 시, 랜덤보상이 3개 주어진다 !

 

// 랜덤 뽑기 보상 ~~ !
function RandomReward(player, monster, stage) {
  
  // Math.random()*100 // 80이상~ 60이상~ 40이상~ 20이상~ 0이상~
  let Reward = Math.random()*100+1
  // 일부 보상에 0.8 ~ 2.0 배율 적용
  let RewardMulty = ((Math.random()*1.2)+0.8).toFixed(1);
         
      // 보상 종류 1 : 회복
  if ( Reward >= 80) {
    let playerHpRestoration = Math.floor(RewardMulty*(stage*12+70))
    player.hp += playerHpRestoration
    console.log(chalk.yellowBright(`산바람이 시원하다. 체력이 [${playerHpRestoration}] 회복되었다 !`));
    
    //보상 종류 2 : 공격력
  } else if ( Reward >= 60) {
    let playerPowerInc = Math.floor(RewardMulty*(stage*2+5))
    player.power += playerPowerInc
    console.log(chalk.yellowBright(`근육이 늘었다. 등산력이 [${playerPowerInc}] 증가했다 !`));

    //보상 종류 3 : 공격 배율
  } else if ( Reward >= 40) {
    let playerPowerMulty = Math.floor(RewardMulty*(stage*2+7))
    player.powerMore += playerPowerMulty
    console.log(chalk.yellowBright(`산에 버려진 무공비법을 찾았다. 최대배율이 [${playerPowerMulty}] 증가했다 !`));

        //보상 종류 4 : 필살 충전
  } else if ( Reward >= 20) {
    if( player.specialMovePoint < 3) {
      player.specialMovePoint++
      console.log(chalk.yellowBright(`필살 ! 암벽등반 ! 충전이 1회 증가했다 ! 현재 충전[${player.specialMovePoint}]`));
    } else { console.log(chalk.yellowBright(`필살 ! 암벽등반 충전 ! 하지만, 이미 최대 충전 상태다...  `)); }

    //보상 종류 5 : 꽝으로 마무리?-> 축지법 성공률로 대체했다.
  } else {
    player.skillChance += Math.floor(4*RewardMulty)
    console.log(chalk.yellowBright(`보법이 달라졌다. 축지법 성공률이 ${Math.floor(4*RewardMulty)}% 증가했다 !`));
  }
};

 

랜덤 뽑기 함수가 현재 구현한 함수중에 길이가 가장 길다...

 

 

심지어 호출부, 인터페이스 부분은 따로있음

 

 

2. 축지법 스킬은 기본 20%확률 밖에 안되기 때문에

보상을 통해 확률이 늘어난게 아니라면 개 손해인 선택지이다.

 

1. 오늘의 첫번째 문제 발생

 

전투 진행상황을 알려주는 초록색 로그 부분이 길어지면, 상태창이 밀려 올라가게 된다.

현재 상태를 알기가 어렵게 되고,

스크롤을 올려서 위를 보면 

뭔가 의도되지 않는 로그가 보이는 등, 문제가 발생한다.

콘솔창을 최대로 키운상태로 진행하면 덜 할 수 있지만, 밀려올라가는건 마찬가지 !

 

해결 방법

로그를 출력하는 battle 함수에서

로그 출력 전 while 문을 달아서 로그가 너무 많을 경우,

0번 index 를 날려버리는 shift() 메서드를 반복하게 했다.

 

 

로그가 일정량 쌓이면, 먼저 들어갔던 로그부터 제거된다.

해결!

 

 

2.  두번째 문제 !

 

이건 간단한 문제로 빠르게 해결했다.

오늘의 목표였던 

 

충전식 필살 스킬 !

충전을 모아서 사용해야 하는데, 이 충전 변수를 어디어 두어야 할지 모르겠다.

함수 안에서 선언하면 어느 함수에서? 호출도 힘들텐데?

전역에 둘까..

전역에 두자니, 허허 벌판에서 혼자 떨고있는 변수가 안쓰럽기도 하고

"코딩 그렇게 하는거 아닌데"  하는 목소리가 계속 들려온다.

 

생각보다 답은 간단했다.

어차피 여기저기 불려다녀야하는 player의 형성자에  넣어주면 되는것이었다.

 

위에서 본 랜덤보상에서 해당 포인트를 얻을 수 있고, 

3포인트를 모아서 사용하면 

 

잘 작동하는 모습이다..

사실 이 장면에 오늘의 가장 큰 문제가 된 사항이 포함되어있다.

 

그건 바로..

3.  필살 이후 나오는 문구에 딜레이를 줘 볼까?

5~10회나 공격하는 기술인데, 텍스트가 한번에 출력되는 것보다

두두두두두두 하고 딜레이에 맞춰서 드드등장하면?

완전 개 간지  ! 

당장 해보자.

 

강의 시간에 배운 setTimeout 이라던가..  async  promise await ?? 같은걸 쓰면

간단한 문제라고 생각했다...

 

시간이 이렇게 되기 전까지는 !

그런 생각으로인해,  TIL을 이 시간에 작성하게 되었다

 

 

 setTimeout 을 아무리 써봐도,

promise ...await 를 이쁘게 넣어봐도

코드가 작동이 되지 않는다.

error 가 발생하는것도 아니고, 해당 함수에서 delay 부분 코드만 실행이 안되고 넘어가버린다.

 

이건 분명 기다려주지 않는것이다... !!!

기다려준다고 promise 했자나요!!

 

 

이번에도 답은 간단했다.......

해당 함수에 async 달고~ setTimeout 세팅해준다고 끝나는 것이 아니라,

 

해당 함수를 호출하는 친구또한 await를 달아줘야 한다.

즉, 필살기 끝나기 전까지 비-동기 진행하지마 ! 를 입구부터 걸어놓고 와야한다.

playerSpecialMove 내부 코드도 동기 처리가 되어야겠지만,

playerSpecialMove 밖에서는 안에서 동기처리가 되든말든 알 바가 아니기 때문에, 진행해버리기 때문이다.

허무해 !!  내 시간 !!

 

 

잘 작동한다 !

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.27 TIL  (1) 2024.08.27
[내일배움캠프] 24.08.23 TIL  (0) 2024.08.23
[내일배움캠프] 24.08.21 TIL  (0) 2024.08.21
[내일배움캠프] 24.08.20 TIL  (0) 2024.08.20
[내일배움캠프] 24.08.19 TIL  (0) 2024.08.19

 

 

 

텍스트로 이루어진 간단한(?) 로그라이크 게임을 만드는 과제가 주어졌다.

 

 

완성 예시 )

이런걸..?

 

 

이제 막 문법을 배웠는데 게임을 만들라구요?!

 

 

 

????

 

 

물론, 스켈레톤 코드가 제공된다고 해도 분명 쉬운 과제는 아니다.

실행이 가능할 정도의 뼈대만 구성이 되어 있었고, 나머지는 전부 직접 만들어야한다.

 

 

 

기본적으로, 위와 같은 템플릿이 주어지지만,

적정선 내에서는 일부 기능을 포기한다거나, 새로운 기능을 추가할 수 있다.

 

계획 !

계획만 그럴싸 하면 어떻게든 진행될 것이다..

 

1. 컨셉

우선 컨셉을 잡아보기로 했다.  (코딩에 바로 들어가기가 두렵다)

원본 컨셉   <===>    나의 컨셉

게임                             등산

stage                          현재 높이

monster                      등산로

공격, 공격력          등반, 등산력...?!

 

나름 그럴싸한, 식상하지 않은 컨셉이라고 생각한다.

 

 

 

2. 목표

본인은 현재 도전 ! 코스로 올려치기가 되어있는 상태로,

일반 코스와 비교해서 추가적인 기능을 구현해야 하는 상태이다.

 

 

 cmd로 실행하면, 왜 인지 폰트가 상당히 낡아지는

 

오늘은 간단하게(?) 텍스트를 정리하고,

가장 핵심 목표인 추가 스킬에 대해 구상을 해 보았다.

추가로 오류가 날 확률이 적다고 판단되어지는 부분은 우선적으로 구현을 해 보았다.

 

1. 등반하기 -> 일반 공격이다.   간단하게 공격력 만큼의 피해를 주는것에 더해서..

공격력에 배율을 곱해서 최대 데미지 개념을 만들려고 했는데 생각보다 쉽지 않았다 !!

잘 모르겠지만, 일단 의도대로 작동하고 있다 !

 

2. 축지법 !   ->  회피와 공격을 동시에

성공시, 체력소모 없이 공격이 가능한 스킬이다. 실패한다면 피해를 못주고 맞기만 한다.

일단은 고정확률인 60% (59%인가....?으음...) 로 마무리할 예정이기 때문에

대충 구현을 마친 상태다.

 

 

3. 필살 ! 암벽등반 ! -> 필살 스킬로, 아직은 미구현이다.

아무튼 강한 피해를 입히는데, 중요한건 충전식으로 충전스택이 2일 경우에만 사용이

가능하게 해볼 예정이다. 충전 스택은 스테이지 클리어 보상으로 랜덤하게 얻을수 있고,

최대 2까지만 쌓을 수 있고, 사용시 2를 소모한다.

 

4. 지름길 개척 :  도망가기 스킬이다.

마찬가지 아직은 미구현으로,

낮은 확률로 성공하여 보상을 모두 얻을지,

그럴싸한 확률로 성공하지만, 클리어 보상이 적어지게 할지 아직 정하진 않았다.

구현..가능 하겠지?

 

 

아직, 초기 단계 이지만, 

 

코딩

좀.. 재미있을지도...

 

 

 

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.23 TIL  (0) 2024.08.23
[내일배움캠프] 24.08.22 TIL  (0) 2024.08.22
[내일배움캠프] 24.08.20 TIL  (0) 2024.08.20
[내일배움캠프] 24.08.19 TIL  (0) 2024.08.19
[내일배움캠프] 24.08.16 TIL  (0) 2024.08.16

 

 

5주차의 내용은 크게 

 

1. DOM

 

2. 클래스

 

3. 클로저 

 

로 나누어진다.

 

 

 

1. DOM = document object modeling

브라우저에 기본적으로 내장되어있는 API 중 하나이다.

JS 는 기본적으로 브라우저 환경에서 작동하기 위해 만들어 졌으므로, 

HTML 과의 상호작용이 당연히 가능해야한다.

 

Dom Tree 는   html구조를 JS가 이해 할 수 있도록 내용을 해석한 것이다.

어떤 형태로?  Object 형태로 !

 

1-1 DOM 의 node 

 

 

1-2 DOM 요소의 속성과 메서드 구분 

document는 대상 객체 ( document 는 항상 dom tree 최상단의 노드 !)

getElementByid() 는.. 인자와 같은 id 를 가진 요소 하나를 반환하라는 메서드 

innerHTML 은 그 속성  !

 

 

 

2. Class 

 

ES6 에서 도입된 문법으로, 

다른 언어에서 많이 사용되던 클래스 기반의 프로그래밍 개발 기법 의 유행(?)으로 인해

자바스크립트에서도 억클( 억지 class 문법...) 을 하려다 보니 니즈를 반영하여 만들었다고 하더라 ~

 

기본적으로 알아둬야 할 사항으로,

Class 와 Instance 가 있다.

 

그럼 JS 에서는 뭘 만드는 설계도 인가?

바로, 객체를 만드는 설계도 이다.

그리고 설계한 대로 만들어진 객체는 instance 가 되겠다.

 

동작을 찍어내는 function, 객체를 찍어내는 class

묘하게 비슷한 감이...?

 

class Person {
	// constructor는 이름을 변경할 수 없어요.
  constructor(name, age) {
		// 이름(name)과 나이(age)가 없으면 사람이 아니죠?
		// new라는 키워드를 이용해서 인스턴스를 만들 때, 기본적으로
		// 넣어야 하는 값들을 의미해요! :)
		// 여기서 말하는 this는 만들어질 인스턴스를 의미한다고 생각해주세요!
    this.name = name;
    this.age = age;
  }

	// 다양한 메소드를 아래와 같이 정의할 수 있어요.
	// 여기서 this.name으로 내부 값을 접근해야 함을 잊지 마세요! :)
  sayHello() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
  }
}

const person1 = new Person("Alice", 30);
const person2 = new Person("Bob", 25);

// 만든 객체를 토대로 메서드 호출해보기
person1.sayHello(); // 출력: "Hello, my name is Alice and I am 30 years old."
person2.sayHello(); // 출력: "Hello, my name is Bob and I am 25 years old."

 

 

person 이라는 class는,  name 과 age 만 받는다면 새로운 person 객체를 마구마구 생성할 수 있는 설계도이다.

 

추가로, person class는 함수 또한 내장이 가능하고, 생성된 객체도 해당 함수를 가져간다.

 

또...중간에 못보던 녀석이 있다 !

Constructor() ; 

 

 

 

 

Getter 와 Setter 

get 을 통해 class 속성에 접근하여, 원하는 값을 반환하게 할 수 있다 !

set 을 통해 class 속성에 접근하여, 원하는 값으로 설정할 수 있다 !

 

 

상 속  Inheritance

 

객체를 복사하는 설계도는 class 

class를 복사하는 설계도는 상속 이라고 보면 될 듯 하다.

Animal class 로 부터 상속받는 Dog class 를 생성

상속 과정에서 필요한 부분을 입맛에 맞게 수정할 수 있다는게 핵심이다.

 

 

Static Method

 

 

3. 클로저

dom 과 class 가 디저트 였다면,

클로저가 이번 챕터의 메인 보스 이다.

 

 

 

클로저를 생각할 때 기억해야할 것  !!

중첩 함수 , 종료된 변수 , 여전히 참조

 

콜스택 적 으로 표현한다면,

이미읽어서 실행이 종료된 함수의 스코프는 외부 스코프로 나가면서 지워지게 된다.

원래대로 라면, 더 이상 해당 스코프의 내용은 참조할 수가 없게된다.

 

그러나, 클로저(중첩함수) 가 존재하는 함수라면, 실행 컨텍스트에서 제거되지만,

해당 함수의 렉시컬 환경까지 소멸하는 것이 아니다.

 

왜?

더 이상 쓰일 일이 없는 데이터를 제거하는 프로세스인

가비지 컬렉터 << 이 유능한 친구가 참조할 데이터가 남아있기 때문에 렉시컬 환경은 남겨두는 것이다.

 

 

왜 클로저를 써야할까? 

 

즉, 클로저로 인해 일어나는 현상에서 이득이 많기 때문에 쓴다는 말인데,

 

가장큰 장점은,

함수 내부로 변수를 은닉하여 의도되지 않은 변경으로 부터의 보호가 가능

이라는 것이다 !

 

( 아무튼 의도적으로 만들어진 기능은 아닌듯 함 )

 

 

this의 대상이 중구난방 이었던과 비슷하게,

 

closer 또한 closer 로 구성된 코드인지, 아닌지를 구분해야 하는 능력을 키워야 할 것 같다 !

 

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.22 TIL  (0) 2024.08.22
[내일배움캠프] 24.08.21 TIL  (0) 2024.08.21
[내일배움캠프] 24.08.19 TIL  (0) 2024.08.19
[내일배움캠프] 24.08.16 TIL  (0) 2024.08.16
[내일배움캠프] 24.08.14 TIL  (0) 2024.08.14

 

 

 

주요 내용은 다음과 같다.

 

  1. 자바스크립트의 콜백함수의 개념과 예시에 대해 학습
  2. 어떤 항목에서 콜백함수를 전달받은 함수에게 제어권이 이양되는지를 이해하고 그 예시를 테스트
  3. 비동기적인 코드 작성에서 발생할 수 있는 콜백지옥을 예방하고 비동기제어를 할 수 있는 Promise, async/await 등의 기술을 이해하고 활용하는 능력 배양

 

 

 

1. 콜백 함수 

콜백 함수는 이전 부터 계속 등장했던 함수이다.

함수 또는 메서드의 인자 부분에 함수가 들어가는것이 가능한데,  ( 일급 객체 )

그렇게 인자로써 매개변수로 넘겨지는 함수를 콜백 함수라고 한다.

 

이번 주차에서는 추가로 제어권에 관한 학습을 진행한다.

 

제어권 ??

 

쉽게 말하면,

cbFunc(); 일반 함수실행의 경우, 사용자가 해당 함수를 실행하도록 만든것이고

setInterval(cbFunc, 300);  의 경우, setInterval 메서드가 (cbFunc, 300) 를 실행하는 것이다.

 

 

콜백함수도 함수다

var obj = {
	vals: [1, 2, 3],
	logValues: function(v, i) {
		console.log(this, v, i);
	}
};

//method로써 호출
obj.logValues(1, 2);

//callback => obj를 this로 하는 메서드를 그대로 전달한게 아니에요
//단지, obj.logValues가 가리키는 함수만 전달한거에요(obj 객체와는 연관이 없습니다)
[4, 5, 6].forEach(obj.logValues);

 

위 예시는 콜백함수의 함정에 가까운 부분으로, 

언뜻 보면 ~forEach(obj.logvalues)   obj 객체를 대상으로하는 logvalues 메서드 이기 때문에 

obj 에 this가 바인딩 될것 같지만,

사실 저 부분은, obj의 logvalues가 인자를 인질로 가지고 있다.. 정도 밖에 안된다

어디까지나, 인자이기 때문

 

obj 가 아닌 global 이 우수수

 

 

 

 

콜백 지옥

 

 

 

대체 어느 부분이 어느 함수의 내용인지 알기가 어렵게 되어버린다.

코드를 짜다 보니 저렇게 될 수도 있다는 것인데,

수정과 유지보수 또한 hell 이기 때문에 최대한 지양해야한다.

 

 

동기와 비동기

 

 

동기란, 1-2-3-4-5 작업이 있을 때, 1번 작업이 완료되지 않는다면, 2-3-4-5 작업이 영원히 실행조차 안되는걸 말한다.

 

비 동기란, 동기의 반대말 !!

1번 작업이 완료되지 않더라도 상황에 따라 다음 순서의 작업이 실행되고, 완료 될 수 있다는 것을 말한다.

 

 

예시를 들자면...........으음

 

사격장에서 사격 훈련을 한다고 했을때,

 

동기 => 

1번 사수 사격- > 완료 -> 2번 사수 사격 -> 완료 -> 3번 사수 사격 -> 완료

 

비 동기 =>

준비된 사수로부터 사격 개시 => 완료 순서도 제각각

 

라고 할 수 있겠다.

 

 

콜백 지옥 해결법? 

setTimeout(
  function (name) {
    var coffeeList = name;
    console.log(coffeeList);

    setTimeout(
      function (name) {
        coffeeList += ", " + name;
        console.log(coffeeList);

        setTimeout(
          function (name) {
            coffeeList += ", " + name;
            console.log(coffeeList);

            setTimeout(
              function (name) {
                coffeeList += ", " + name;
                console.log(coffeeList);
              },
              500,
              "카페라떼"
            );
          },
          500,
          "카페모카"
        );
      },
      500,
      "아메리카노"
    );
  },
  500,
  "에스프레소"
);

 

보기만 해도 현기증이 나는 평범한 콜백 지옥이다.

 

강의에서는 

 

1.  기명함수로 변환  

2. 비 동기 작업의 동기적 표현

 2-1 ) promise

 2-2 ) Generator

 2-3 ) Promise + Async/await 

 

을 소개해주었다.

 

여기서 좀 더 중요한 녀석은 비 동기 작업을 동기적으로 표현한다는 내용인데, 

 

왜 굳이 비 동기 작업을 동기적으로 바꾸려고 할까?  라고 생각이 든다면, 

 

일에는 순서가 있는 법이야 를  떠올려 보자.

물론 가독성과 유지보수의 문제도 있겠다만,

 

우리는 음식을 주문받는 일과, 요리가 만들어지는 과정, 요리가 서빙되는 과정이

동시에 시작될 수 없다는 것을 잘 알고있다 .

 

주문을 받고-> 요리가 만들어지고-> 서빙이 되어야 하기 때문이다....

???

 

말하고 보니까 굉장히 이상한 느낌이 든다..

 

애초에 한번에 한 가지의 동작만 수행하도록 되어있는 동기식 언어인 JS에서, 

효율을 위해 Queue , call stack 등을 이용해 비 동기식으로 처리될 수 있게끔 만들었고,

그렇게 만들어진 비 동기 방식을 우리는 다시 동기적으로 표현할 수 있어야 한다.

 

 

 

 

 

'내일배움캠프' 카테고리의 다른 글

[내일배움캠프] 24.08.21 TIL  (0) 2024.08.21
[내일배움캠프] 24.08.20 TIL  (0) 2024.08.20
[내일배움캠프] 24.08.16 TIL  (0) 2024.08.16
[내일배움캠프] 24.08.14 TIL  (0) 2024.08.14
[내일배움캠프] 24.08.13 TIL  (0) 2024.08.13

+ Recent posts