Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# 숫자 야구 게임 기능 구현 미션

## 기능 구현 목록

### MVC 패턴으로 분류

- 게임 흐름 제어 (BaseballController.js)
- 확인 버튼 클릭 시 유효성 검사 및 판정 로직 실행
- 재시작 버튼 클릭 시 모델 과 뷰 초기화 후, 새 게임 시작

- 게임 데이터 관리 (BaseballModel.js)
- 컴퓨터의 서로 다른 임의의 수 3개 생성
- 사용자의 입력값과 컴퓨터의 숫자를 비교 후 스트라이크/볼 판정
- 게임 재시작 시 새로운 정답 숫자 생성 후 데이터 초기화

- 유효성 검사 (isValid.js)
- 3자리 숫자가 아니거나, 중복된 숫자가 있거나 0이 포함된 경우 alert 메시지 띄움

- 사용자 인터페이스 (BaseballView.js)
- 사용자가 입력한 숫자에 대한 힌트를 화면에 출력
- 정답을 맞췄을 경우 축하 메시지와 재시작 버튼 표시
- 재시작 버튼 클릭 시 입력창과 결과창 초기화
39 changes: 39 additions & 0 deletions src/controllers/BaseballController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import BaseballModel from '../models/BaseballModel.js';
import BaseballView from '../views/BaseballView.js';
import { isValid } from '../models/isValid.js';

export default class BaseballControllor {
constructor(){
this.model=new BaseballModel();
this.view=new BaseballView();
this.initEventListeners();
}
initEventListeners(){
// HTML 태그 안에 직접 <button onclick="play()"> 라고 적는 방법도 있음
this.view.submit.addEventListener('click', (e) => {

// 브라우저의 자동 새로고침 본능을 억제
e.preventDefault();

// 사용자가 입력창에 쓴 값을 가져옴
const userValue = this.view.userInput.value;

// 유효성 검사 모듈(isValid)을 호출해 통과 여부를 확인
if (!isValid(userValue)) return;

// play 로직을 실행해 결과를 받아옴
const resultText = this.model.play(userValue);

if (resultText === "3스트라이크"){
this.view.displaySuccess();
}else{
this.view.displayHint(resultText);
}
});

this.view.restart.addEventListener('click', ()=>{
this.model.prepareNewGame()
this.view.RestartUI();
});
}
}
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import BaseballControllor from "./controllers/BaseballController.js";

//게임 실행
new BaseballControllor();
64 changes: 64 additions & 0 deletions src/models/BaseballModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//데이터의 생성, 저장, 처리를 전담

export default class BaseballModel {
constructor(){
this.computerInputNumbers=this.generateComputerNumbers();
}

// 컴퓨터 숫자 생성 (중복 없는 난수 3개)
generateComputerNumbers() {
const numbers = [];
while (numbers.length < 3) {
const number = MissionUtils.Random.pickNumberInRange(1, 9);
if (!numbers.includes(number)) numbers.push(number);
}
return numbers;
}

play(userInputNumbers){
// 스트라이크/볼 판정
let strike = 0;
let ball = 0;

for(let i=0;i<3;i++){
const userDigit=Number(userInputNumbers[i]);
//userInput.value로 가져온 값은 무조건 글자 상태로 들어오기 때문에
// Number()을 써서 숫자로 변환
if(userDigit===this.computerInputNumbers[i]){
strike++;
}else if (this.computerInputNumbers.includes(userDigit)){
ball++;
}
}
// 조건에 맞춰 문자열로 출력
if (strike === 0 && ball === 0){
return "낫싱";
}
if(strike === 3){
return "3스트라이크"
}
// string으로 출력하는 방식
// 1. 템플릿 리터럴: 백틱(` `)과 ${} 사용 (대부분 템플릿 리터럴 사용함)
// 2. 문자열 연결 연산자: (+) 기호 사용
// 3. 배열 합치기(join): result.join(" ") 사용
// 볼과 스트라이크가 있을수도 없을수도 있으니 join 사용함
const result = [];

if(ball > 0){
result.push(`${ball}볼`);
}

if(strike > 0){
result.push(`${strike}스트라이크`);
}

return result.join(" ");

}

prepareNewGame(){
// 컴퓨터 숫자를 생성하는 로직은 게임의 핵심 데이터라 생각해서
// view로 전부 이사시키지 않고 Model로 분류함
this.computerInputNumbers = this.generateComputerNumbers();
}
}
29 changes: 29 additions & 0 deletions src/models/isValid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export function isValid(userValue){
// 유효성 검사
if(isNaN(userValue)){
alert('숫자만 입력해주세요!');
return;
}
// 숫자인지 확인

if(userValue.length !== 3){
alert('반드시 3자리 숫자를 입력해주세요!');
return;
}
// 3자리인지 확인

if(new Set(userValue).size !== 3){
alert('중복된 숫자가 있습니다!');
return;
}
// 중복을 허용하지 않는 Set 자료형의 특성을 활용

if(userValue.includes('0')){
alert('0은 포함될 수 없습니다. 1~9 사이의 숫자를 입력해주세요.');
return;
}
// 입력값에 '0'이 있는지 확인 -> 있으면 alert

return true;

}
38 changes: 38 additions & 0 deletions src/views/BaseballView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export default class BaseballView {
constructor(){
// 1. 부품(DOM) 가져오기: HTML 요소를 클래스 내부 변수(this)에 저장
this.userInput = document.querySelector('#user-input');
this.submit = document.querySelector('#submit');
this.result = document.querySelector('#result');
this.restart = document.querySelector('#game-restart-button');

// 재시작 버튼 숨기기
this.restart.style.display = 'none';
}

displayHint(text){
this.result.textContent=text;
// 평소엔 숨기기
this.restart.style.display='none';
}

displaySuccess(){
// 정답 시 원래 있던 결과창 글자를 싹
// 지우고 축하 문구 및 재시작 버튼 활성화
this.result.innerHTML = `
<div>🎉 정답을 맞추셨습니다 🎉</div>
<br>게임을 새로 시작하시겠습니까?
`;
// textContent (변수만 섞인 글자를 보여줄 때)
// testContent로 `<b>${strike}스트라이크</b>` 할당하면 그대로 출력됨

// innerHTML (버튼이나 문단 같은 HTML 구조를 통째로 만들어서 끼워 넣고 싶을 때 사용)
// 백틱 사이에 <button id="game-restart-button">재시작</button> 이렇게 적어도 구현 가능
this.restart.style.display = 'block';
}

restartUI(){
this.result.innerHTML = '';
this.restart.style.display = 'none';
}
}