역시 프로그램을 작성하기에 앞서 순서도를 생각해보는 과정이 가장 중요하다.
로또 추첨기는 다른 게임 예제들에 비해서 간략한 순서를 가진다.
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>타이머사용하기-로또추첨기</title>
<style>
.ball {
display: inline-block;
border: 1px solid black;
border-radius: 20px;
width: 40px;
height: 40px;
line-height: 40px;
font-size: 20px;
text-align: center;
margin-right: 20px;
}
</style>
</head>
<body>
<div id="result">추첨 결과는?</div>
<div id="bonus">보너스:</div>
<script>
const candidate = Array(45).fill().map((v, i) => i + 1);
//피셔=예이츠셔플
const shuffle = [];
while (candidate.length > 0) {
const random = Math.floor(Math.random() * candidate.length); //무작위 인덱스 뽑기
const spliceArray = candidate.splice(random, 1); //뽑은 값은 배열에 들어있음
const value = spliceArray[0]; //배열에 들어 있는 값을 꺼내어
shuffle.push(value); //shuffle 배열에 넣기
}
const winBalls = shuffle.slice(0, 6).sort((a, b) => a - b);
//slice 원본은 건드리지않고 자르기 가능 splice는 원본을 수정함
//정렬메서드 sort 원본을 수정
//a-b = 오름차순 / b-a =내림차순
const bonus = shuffle[6];
console.log(winBalls, bonus);
const $result = document.querySelector('#result');
const $bonus = document.querySelector('#bonus');
//중복되는 부분은 함수로 빼주고 변하는 부분은 매개변수로 빼줌.
//중복제거 후 똑같이 돌아가는지 확인하기!
const showBall = (number, $target) => {
const $ball = document.createElement('div');
$ball.className = 'ball';
colorize(number, $ball); //색칠해주는 함수
$ball.textContent = number;
$target.appendChild($ball);
}
for (let i = 0; i < winBalls.length; i++) {
setTimeout(() => {
showBall(winBalls[i], $result);
}, (i + 1) * 1000);
}
setTimeout(() => {
showBall(bonus, $bonus);
}, 7000);
</script>
</body>
로또 번호를 무작위로 섞기위해 사용된 알고리즘.
피셔-예이츠 셔플(Fisher-Yates shuffle)은 유한 수열의 무작위 순열을 생성하기 위한 알고리즘이다.
45개의 수를 map 메서드를 이용해 저장하고 피셔-예이츠 셔플 알고리즘을 이용해 섞어준다.
const candidate = Array(45).fill().map((v, i) => i + 1);
//피셔=예이츠셔플
const shuffle = [];
while (candidate.length > 0) {
const random = Math.floor(Math.random() * candidate.length); //무작위 인덱스 뽑기
const spliceArray = candidate.splice(random, 1); //뽑은 값은 배열에 들어있음
const value = spliceArray[0]; //배열에 들어 있는 값을 꺼내어
shuffle.push(value); //shuffle 배열에 넣기
}
- slice는 원본을 건드리지 않고 자르기가 가능해서 편리하게 쓰인다.
slice로 생성된 배열과 원래 배열은 같지 않다.
splice는 원본을 수정하기 때문에 불편한 부분이 생길 수 있다.- 정렬메서드 sort는 원본을 수정한다.
sort((a,b)=>a-b); 와 같은 형식으로 쓰이며 a-b는 오름차순, b-a는 내림차순 정렬이다.
const winBalls = shuffle.slice(0, 6).sort((a, b) => a - b);
for (let i = 0; i < winBalls.length; i++) {
setTimeout(() => {
showBall(winBalls[i], $result);
}, (i + 1) * 1000);
}
위 코드를 작성할 때 let 대신 var 변수를 넣으면 공 안의 숫자가 나타나지않는다.
바로 var가 함수 스코프이기때문.
var를 넣어주면 i가 먼저 실행되어서 undefined, 6 이라는 값을 연속적으로 나타내게 된다.
따라서 블록스코프인 let을 사용해 블록안의 함수를 먼저 실행할 수 있도록 차단시켜줘야한다.
비동기처리방식, var가 만나면 클로저 문제가 발생한다.
당첨 공의 숫자를 기준으로 색을 칠해준다.
function colorize(number, $tag) {
if (number < 10) {
$tag.style.backgroundColor = 'red';
$tag.style.color = 'white';
} else if (number < 20) {
$tag.style.backgroundColor = 'orange';
} else if (number < 30) {
$tag.style.backgroundColor = 'yellow';
} else if (number < 40) {
$tag.style.backgroundColor = 'blue';
$tag.style.color = 'white';
} else {
$tag.style.backgroundColor = 'green';
$tag.style.color = 'white';
}
}
그리고 위의 함수를 const showBall부분에 넣어준다.
colorize(number, $ball); 추가