내일배움캠프 Node.js 트랙 15일차
1. ZEP에서 이루어진 로그라이크 게임 만들기 프로젝트
게임에 사용된 주요 함수들
카드(배열) 무작위 정렬
let array = [1, 2, 3, 4, 5];
array.sort(() => Math.random() - 0.5);
console.log(array);
sort() 메서드에 별다른 compareFunction이 제공되지 않으면 요소를 문자열로 변환하고 두 요소를 비교한다. 기준은 유니 코드의 코드 포인트 값에 따라 정렬된다.
function compareNumbers(a, b) {
return a - b;
}
요소가 숫자인 경우, sort() 메서드는 콜백함수를 매개변수로 받는데, 매개변수가 음수를 반환하면 a가 b보다 먼저 정렬되고 양수를 반환하면 b가 a보다 먼저 정렬된다. 만약 Math.random() - 0.5를 콜백함수로 넣어준다면 어떻게 될까? 50퍼센트의 확률로 양수 혹은 음수(혹은 0)를 반환할 것이므로 정렬 또한 무작위로 수행된다.
Fisher-Yates 셔플 알고리즘
for (let i = this._hasCard.length - 1; i > 0; i--) {
// 0부터 i까지의 임의의 인덱스를 선택
const j = Math.floor(Math.random() * (i + 1));
// 배열의 i번째 요소와 j번째 요소를 교환
[this._hasCard[i], this._hasCard[j]] = [this._hasCard[j], this._hasCard[i]];
}
콘솔 로그에 한 줄씩 딜레이 주기(Promise+SetTimeout)
async function scenario () {
await new Promise((resolve) => {
setTimeout(() => {
console.log('스크립트 1번');
resolve(); // 비동기 작업 완료
}, 1000);
});
await new Promise((resolve) => {
setTimeout(() => {
console.log('스크립트 2번');
resolve();
}, 3000);
});
await new Promise((resolve) => {
setTimeout(() => {
console.log('스크립트 3번');
resolve();
}, 3000);
});
}
카드를 무작위로 생성하는 함수
function makeRandomCard(player) {
let randomCardInstance;
// 후보가 될 클래스들을 배열로 나열한다.
const cardClasses = [
NormalAttackCard,
NormalDefenseCard,
RareAttackCard,
RareDefenseCard,
EpicAttackCard,
EpicDefenseCard,
LegendaryAttackCard,
LegendaryDefenseCard,
];
// 만약 조건에 따라 다른 배열에서 생성하고 싶으면 그 또한 배열로 할당한다.
const eliteCardClasses = [
EpicAttackCard,
EpicDefenseCard,
LegendaryAttackCard,
LegendaryDefenseCard,
];
// 조건에 따라 배열의 요소를 무작위로 선별한다.
if (player.isEliteStage) {
randomCardInstance = eliteCardClasses[Math.floor(Math.random() * eliteCardClasses.length)];
} else {
randomCardInstance = cardClasses[Math.floor(Math.random() * cardClasses.length)];
}
// 선택된 클래스의 인스턴스를 반환해주자.
return new randomCardInstance();
}
→ 사용 예시
// 여관
let tavern = (player) => {
let tavernChoice;
// 랜덤한 카드를 생성해서 상점에서 나타나게 만들자.
let card1 = makeRandomCard(player);
let card2 = makeRandomCard(player);
let card3 = makeRandomCard(player);
// 생략...
카드 세 장을 하나의 카드로 업그레이드
// 같은 이름을 가진 카드를 세는 함수
let countCard = (player) => {
// 소유한 카드(객체)를 하나의 배열로 모으고 정렬
player.collectAllCard();
// 카드 개수 세기
let cardCounts = {};
// player.hasCard 배열을 순회하면서 동일한 카드 이름이 몇 장인지 세기
player.hasCard.forEach((card) => {
if (cardCounts[card.cardName]) {
cardCounts[card.cardName]++;
} else {
cardCounts[card.cardName] = 1;
}
});
// 카드의 이름을 프로퍼티로 가지고 장수를 밸류로 가진 객체 반환
return cardCounts;
};
let mergeCard = (player) => {
console.clear();
// 덱 리스트 보여주고
displayDeckList(player);
miniUI(player);
// 카드 개수 세기
let cardCounts = countCard(player);
// 합칠 수 있는 카드(3장 이상 소유한 카드)들을 배열로 만든다.
let canMerge = [];
// cardCounts 객체를 순회하면서 밸류가 3이상인 프로퍼티가 존재한다면 canMerge 배열에 추가한다.
for (let cardName in cardCounts) {
if (cardCounts[cardName] >= 3) {
canMerge.push(cardName);
}
}
// readlineSync.keyInSelect는 배열의 요소들을 선택지 형식으로 표현해준다. 그리고 입력값을 통해 해당 배열의 인덱스를 반환해준다.
if (canMerge.length > 0) {
let cardNameIndex = readlineSync.keyInSelect(
canMerge,
'세 장 이상 소유하고 있는 카드들입니다! 어떤 카드를 합치시겠습니까? ',
{ cancel: '취소하기' },
);
if (cardNameIndex === -1) {
console.log(colors.error('카드 합치기가 취소되었습니다.'));
}
// readlineSync.keyInSelect로 구한 인덱스 값으로 플레이어가 선택한 카드의 이름을 알아내기.
let cardName = canMerge[cardNameIndex];
// player.hasCard에서 선택한 카드 제거 및 변경
if (canMerge.includes(cardName)) {
let removedCards = [];
let potentialHandSizeAfterMerge = player.hasCard.length - 2; // 합친 후 예상되는 카드 수
// 예상 카드 수가 손패 크기보다 작으면 에러를 표시한다.
if (potentialHandSizeAfterMerge < player.handSize) {
console.log(
colors.error(
'카드의 개수는 손패 크기보다 작아질 수 없습니다. 카드 합치기가 취소되었습니다.',
),
);
readlineSync.keyInPause();
} else {
// 플레이어의 덱에서 플레이어가 선택한 카드와 이름이 같은 카드를 2장 제거
player.hasCard = player.hasCard.filter((card) => {
if (card.cardName === cardName && removedCards.length < 3) {
removedCards.push(card); // 제거한 카드를 저장
return false;
// 제거할 카드는 false로 반환(filter() 메서드는 콜백함수의 조건이 true가 될 때 해당 요소를 선별해준다.)
}
return true; // 유지할 카드는 true로 반환
});
if (removedCards.length === 3) {
// 첫 번째 카드를 기반으로 업그레이드된 카드 생성
// 전개 구문으로 removedCards배열의 첫번째 요소를 복사(얕은 복사)해준다.
let upgradedCard = { ...removedCards[0], cardName: cardName + '+' };
// 프로퍼티는 고유하므로 덮어쓰기가 된다!
// 카드이름에 '+'를 더한 값을 덮어쓰기 해주자.
upgradedCard.actProb += 20;
upgradedCard.attackDmg += upgradedCard.attackDmg;
upgradedCard.fireDmg += upgradedCard.fireDmg;
upgradedCard.restoreHp += upgradedCard.restoreHp;
upgradedCard.defense += upgradedCard.defense;
player.hasCard.push(upgradedCard);
console.log(colors.success(`${cardName} 카드가 업그레이드되었습니다!`));
readlineSync.keyInPause();
}
}
} else {
console.log(colors.error('유효하지 않은 카드입니다.'));
readlineSync.keyInPause();
}
} else {
console.log(colors.error('합칠 수 있는 카드가 없습니다.'));
readlineSync.keyInPause();
}
};
'JS > TIL(Today I Learned)' 카테고리의 다른 글
| 2024-11-19 (1) | 2024.11.19 |
|---|---|
| 2024-11-18 (3) | 2024.11.18 |
| 2024-11-14 (1) | 2024.11.14 |
| 2024-11-13 (2) | 2024.11.13 |
| 2024-11-12 (2) | 2024.11.12 |
댓글