Javascript

미니프로젝트 - 캘린더, 베이스볼게임

big whale 2021. 6. 4. 19:25

1. 캘린더


 

코드 실행시 오늘의 요일, 날짜가 출력되어야 하고 색상은 빨간색이다.

화살표로 이동시 해당 월의 달력이 출력되어야 하고 표시되는 날은 1일이다.

일을 클릭했을 때 그 일의 요일과 숫자를 출력해야 한다.

 

1. html 구성 

테이블구조를 사용했고 한달에 최대 6주가 있을 수 있기 때문에 tr을 6개 생성하였다. 

<body>
<p class="week-day"></p>
      <h2 class="month-year"></h2>
      <div class="chalender-box">
        <button type="button" id="left-arrow">‹</button>
          <table>
            <tr>
              <th>SUN</th>
              <th>MON</th>
              <th>TUE</th>
              <th>WED</th>
              <th>THU</th>
              <th>FRI</th>
              <th>SAT</th>
            </tr>
            <tr id="tr1"></tr>
            <tr id="tr2"></tr>
            <tr id="tr3"></tr>
            <tr id="tr4"></tr>
            <tr id="tr5"></tr>
            <tr id="tr6"></tr>
          </table>
          <button type="button" id="right-arrow">›</button>
      </div>
</body>

 

2. CSS 구성 

깔끔한 UI를 위해 버튼의 경계와 클릭시 아웃라인을 삭제했고 display:flex로 가로 정렬 및 아이템 중앙 정렬을 했다.

th {
  font-weight:bolder;
  font-size: 20px;
}
td {
  text-align: center;
}

button {
  border: none;
  background: none;
  font-size: 20px;
}

button:active {
  outline: none;
  border: none;
  }

button:focus {outline:0;}

#left-arrow {
  font-size: 50px;
}

#right-arrow {
  font-size: 50px;
}

.chalender-box {
  display: flex;
  justify-content: center;
  align-items: center;
}

.month-year {
  text-align: center;
}
.week-day {
  text-align: center;
  font-size: 50px;
}

 

3. Javascript 구성 

 

첫번째 접근법

setDate(number)을 이용해서 화살표로 이동할 시에 number을 더하거나 빼서 달력을 그리려고 했다. 하지만 끝도없이 더해야 한다는 조잡한 방법이라는 생각과 2월달의 총 일수가 년마다 바뀌기 때문에 오류가 많이 발생해서 좋은 방법이 아니었다.

 

예시

const date = new Date();
date.setDate(31);
const todayDate = date.toDateString();
const year = todayDate.substring(11,15);
const week = todayDate.substring(0,3).toUpperCase();
const mon = todayDate.substring(4,7).toUpperCase();
const day = todayDate.substring(8,10);

     

두번째 접근법

화살표 버튼을 클릭했을 때 해당 달의 총 일수와 첫번째 날의 요일을 이용해서 달력을 그리는 방식 , 2월은 28일로 한다.

: 2월의 일수가 다른 실제 달력에 영향을 받지 않기 때문에 무한대로 그릴 수 있다는 장점이 있다.  

 

이전 달의 첫번째 날의 요일을 구하는 법

- daysinMonth 배열은 각 월별 일수를 가지고 있다.

- monthCount 변수는 값이 0이면 1월, 11이면 12월이다.

- weekCount 변수를 만들고 값이 0이면 일요일, 6이면 토요일으로 한다.

- 현재 달의 첫번째 날의 weekCount-1일을 하면 이전 달의 마지막 주의 day 개수가 되고, 이전 달의 days에서 마지막 주의 day 개수를 뺀 후, 7일씩 1주씩 빼가면서 이 값이 7보다 같거나 작아질때, 이 값을 7에서 빼면 이전달의 첫번째 day의 weekCount를 구할 수 있다.   

 

이 아이디어로 코드를 작성하면,

function getPreviousFirstDay() {
    resetChalender();
    let weekCountOflastDay;
    if (weekCount === 0) {
        weekCountOflastDay = 6;
    } else {
        weekCountOflastDay = weekCount-1;
    }
    console.log(weekCountOflastDay);
    if (monthCount === 0) {
        monthCount = 11;
        year--;
    } else {
        monthCount--;
    }
    month = months[monthCount];
    for (var i=0;i<6;i++) {
        let firstweekDays = daysinMonth[monthCount]-weekCountOflastDay-1-7*i;
        if (firstweekDays<=7) {
            weekCount = 7-firstweekDays;
            week = weeks[weekCount];
            console.log(firstweekDays);
            break;
        }
    }
    console.log(weekCount);
    weekday.innerText = `${weeks[weekCount]} \n 1`
    drawChal();
}

이런식으로 적을 수 있고 전체 코드는 깃헙에 올려두었다.

첫번째 방법으로 문제풀이를 하다가도 두번째 방법으로 하고싶다는 생각이 계속 들었지만 구현을 다시 시작해야 해서 힘들겠다는 생각에 빠른 노선변경을 하지 못했는데 막상 바꾸니까 뚝딱 마무리돼서 좋았다. 안될때는 다른생각을 하기.

 

2. 야구게임


설계 요구 사항

 

  • 게임 시작 버튼 만들기
  • 게임 시작 버튼을 클릭 했을때, 랜덤한 세 자리 숫자 만들기 (사용자에게 보여주진 않습니다.)
  • 숫자 입력칸 만들기
  • 사용자가 엔터키를 클릭 했을때, 입력값이 세자리 숫자가 아닌 경우 경고창 띄워주기
  • 사용자가 엔터키를 클릭 했을때, 2단계에서 생성한 숫자와 사용자의 입력값 비교하기
  • 각 자리 별로 비교하고, 같은 자리에 같은 숫자가 몇개 있는지 판별합니다. (스트라이크 갯수)
  • 각 자리 별로 비교하고, 다른 자리에 같은 숫자가 몇개 있는지 판별합니다. (볼 갯수)
  • 화면에 스트라이크와 볼의 갯수를 표기합니다.
  • 사용자가 10회까지 시도할 수 있도록 제한합니다.
  • 게임 재시작 버튼을 만들고, 재시작 할 수 있도록 합니다.

 

Javascript 코드

const startBtn = document.querySelector('#game-start-btn');
const userNumberBox = document.querySelector('.user-number-box');
const strikeBall = document.querySelector('.strike-ball');
const input = document.querySelector('input');
const restartBtn = document.querySelector('#restart-btn');
let randomNumber = 0;
let strike = 0;
let ball = 0;
let chance = 10;
function setRandomNumber() {
    let i = 0;
    while(i<1) {
        let tempNumber = Math.floor(Math.random()*1000);
        if (tempNumber>=100) {
            randomNumber = tempNumber;
            i++;
        }
    }
    console.log(randomNumber);
    input.classList.remove('noshowing');
    input.classList.add('showing');
    userNumberBox.addEventListener('submit',submituserNumber);
}

function compareNumber() {
    const ranThird = Math.floor(randomNumber/100);
    const ranSecond = Math.floor((randomNumber-100*ranThird)/10);
    const ranFirst = (randomNumber-100*ranThird-10*ranSecond);
    const randomNumberArr = [ranThird,ranSecond,ranFirst];
    const userThird = Math.floor(input.value/100);
    const userSecond = Math.floor((input.value-100*userThird)/10);
    const userFirst = (input.value-100*userThird-10*userSecond);
    const userNumberArr = [userThird,userSecond,userFirst];
    console.log(randomNumberArr,userNumberArr);
    for (let i = 0;i<3;i++) {
        if (randomNumberArr[i]===userNumberArr[i]) {
            strike++;
        }
    }
    for (let i=0;i<3;i++) {
        for (let j=0;j<3;j++) {
            if (userNumberArr[i] === randomNumberArr[j] && i!==j) {
                    ball++;
            }
        }
    }
    input.value='';
    paintScore();

}

function paintScore() {
    strikeBall.innerText =`strike: ${strike} ball: ${ball}`;
    strike = 0;
    ball = 0;
}


function restart() {
    chance=10;
    strikeBall.innerText = '';
    strike = 0;
    ball = 0;
    input.value='';
}

function submituserNumber(event) {
    event.preventDefault();
    if (chance>0) {
        if (input.value>=100 && input.value<1000) {
            compareNumber();
            chance--;
        } else {
            alert('세자리 숫자를 입력해 주세요.');
            input.value='';
        }
    }   else {
        alert('기회를 다 사용했습니다.');
    }
}

function init() {
    startBtn.addEventListener('click',setRandomNumber);
    restartBtn.addEventListener('click',restart);
}

init();

1. init함수: 게임시작 버튼을 누르기를 기다리는 이벤트 리스너와 재시작 버튼을 누르기를 기다리는 이벤트 리스너가 있다.

2. setRandomNumber함수: 게임시작 버튼을 누르면 호출되며, 랜덤한 세자리수를 만들고 사용자가 텍스트를 입력하는 칸을 클래스 교체 방법을 통해 표시해준다. 그리고 사용자가 숫자를 submit 하는 순간 submituserNumber함수를 호출한다.

3. submituserNumber함수: 생성된 랜덤수와 사용자가 입력한 수를 비교하는 함수이고, 실제 비교는 compareNumber()함수에서 이뤄지며, 비교가 끝나면 기회를 하나 깎는다.

4. compareNumber함수: 랜덤수,사용자 입력수 각각의 자릿값을 비교하는 함수로, 자릿수와 값이 같으면 strike에 1을 더하고 값은 같지만 자릿수가 다르면 ball에 1을 더한다. 마지막으로 paintScore함수를 호출한다.

5. paintScore함수: 화면에 스트라이크,볼을 표시해주는 함수로, 표시가 끝난 후에는 strike,ball값을 모두 0으로 초기화해줘서 사용자가 새로 입력한 값의 strike,ball값을 받는다.

 

요구사항 순서대로 작은것부터 해결하다보니 어느새 하나의 숫자야구 게임이 만들어져 있는게 신기하고 전체 코드를 보니 여러개의 작은 부분으로 나눠서 코드를 짜는게 처음 코드 짤때의 막막함을 덜어주는데에 정말 좋은 방법인 것 같다는 생각이 들었다.