Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2e5fad0
Update README.md
00306 Apr 30, 2024
e8d21bc
Update README.md
00306 Apr 30, 2024
9c4a546
Update README.md
00306 Apr 30, 2024
b22bb7b
Update README.md
00306 Apr 30, 2024
23ead2a
feat(UI): 게임 타이틀과 규칙 및 예시 UI 구현
00306 May 1, 2024
cdbd2fb
feat(UI): 텍스트 필드 및 버튼 UI 구현
00306 May 1, 2024
0b9e828
feat(UI): 결과 UI 구현
00306 May 1, 2024
4778682
Merge pull request #1 from 00306/#1/UI
00306 May 3, 2024
833b197
feat: Game 클래스 구현
00306 May 4, 2024
3b3164f
feat: String.prototype.isNotIncludes 메서드 구현
00306 May 4, 2024
4cb3afd
feat(Game): Game 클래스 구현
00306 May 4, 2024
ee99ae6
feat(Game): String.prototype.isNotIncludes 메서드 구현
00306 May 4, 2024
362a292
feat(Game): 태그 상호작용 및 게임 로직 적용
00306 May 4, 2024
617936a
Merge branch '#2/Game' of https://github.com/00306/Kakao_Tech_Campus_…
00306 May 4, 2024
13c07c3
feat(Game): 결과 텍스트 및 재시작 텍스트 스타일 적용
00306 May 4, 2024
d63684f
refactor(Game): 재시작 메시지 태그 이름 수정
00306 May 4, 2024
cd33713
fix(UI): 게임 컴포넌트 배치 수정
00306 May 4, 2024
9520236
Merge pull request #2 from 00306/#2/Game
00306 May 4, 2024
2981795
feat(UI): 게임 타이틀 및 로그 UI 구현
00306 May 5, 2024
bad827b
refactor(Module): DomManipulation 로직 분리
00306 May 5, 2024
71c4065
refactor(Constants): 게임 재시작 메시지, 오류 메시지, 결과 메시지 상수 추가
00306 May 5, 2024
59c417d
refactor(Elements): DOM 요소 상수 추가
00306 May 5, 2024
3beadaa
feat(Game): 게임 로직 및 DOM 조작 함수 구현
00306 May 5, 2024
9ec4a75
refactor: 모듈화로 인한 코드 이동
00306 May 5, 2024
023ff15
feat(Game): 로그 로직 구현 및 텍스트 수정
00306 May 5, 2024
c382cb7
Merge pull request #3 from 00306/#1/UI
00306 May 5, 2024
6f0b420
Update README.md
00306 May 5, 2024
732e751
feat(UI): favicon 및 title 추가
00306 May 5, 2024
92bb420
Merge branch 'main' of https://github.com/00306/Kakao_Tech_Campus_Fir…
00306 May 5, 2024
357a109
fix(UI): 결과 메시지 margin-bottom 값 수정
00306 May 5, 2024
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
48 changes: 47 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,47 @@
# javascript-baseball-precourse

<p align="center">
<img src="https://github.com/00306/Kakao_Tech_Campus_First_Assignment/assets/114010099/ce1c5cfd-d955-47fb-84ef-8bf813890624" style="width: 60%" alt="KAKAO"/>
</p>

# 1회차 미니 과제 - ⚾️ 숫자 야구 게임

> 카카오 테크 캠퍼스 2기 FE 1회차 미니 과제, 숫자 야구 게임 레포입니다.

<br>


## 기능 목록

### UI
* 게임 타이틀과 규칙 및 예시를 표시한다.
* 입력을 받을 텍스트 필드를 배치한다.
* 답 제출 버튼을 배치한다.
* 결과 섹션을 배치한다.

<br>

### 문제 출제
* 1부터 9까지 서로 다른 임의의 수로 이루어진 3자리의 수가 생성된다.

<br>

### 문제 풀기
* 사용자로부터 서로 다른 3자리의 수를 입력 받는다.
* 정상 입력된 경우 입력에 따른 결과 메시지를 표시한다.
* 같은 수가 같은 자리에 있으면 스트라이크
* 다른 자리에 있으면 볼
* 같은 수가 전혀 없으면 낫싱
* 게임을 종료한 후 게임을 다시 시작하거나 완전히 종료할 수 있다.
* 잘못 입력된 경우 alert()을 이용한 에러 메시지 출력 후 그 부분부터 다시 입력받는다.

<br>

### 로그
* 사용자가 입력한 값과 그 결과값을 표시한다.

<br>

### 정답 이후 처리
* 3개의 숫자를 모두 맞히면 게임이 종료되고 '🎉정답을 맞추셨습니다🎉' 메시지 출력과 함께 재시작 문구 및 버튼이 표시된다.

<br>
Binary file added images/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 49 additions & 5 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,56 @@
<!doctype html>
<html lang="en">
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<title>숫자 야구</title>
<link rel="icon" href="./images/favicon.png" />

<link rel="stylesheet" href="./src/main.css" />
<script defer type="module" src="./src/main.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
<div id="app">
<div class="titleScreen">
<div class="triangle-red"></div>
<div class="triangle-offblack"></div>
<div class="container hidden">
<div class="titleScreen__image">⚾️</div>
<div class="titleScreen__title">Bulls and Cows</div>
<div class="titleScreen__startBtn">Start</div>
</div>
</div>

<h1>⚾️ 숫자 야구 게임</h1>

<section class="rules">
<b>1~9까지의 수</b>를 중복없이 <b>3개</b> 입력해주세요.

<div>올바른 예) 139</div>
<div>틀린 예) 122</div>
</section>

<br />

<section class="input">
<input type="text" id="input__textfield" />
<input type="button" value="확인" id="input__btn" />
</section>

<section class="result">
<h3>📄 결과</h3>
<div class="result__text"></div>
<div class="result__restartMessage"></div>
<input type="hidden" value="게임 재시작" class="result__btn--restart" />
<input type="hidden" value="게임 종료" class="result__btn--finish" />
</section>

<h3>📋 기록</h3>
<section class="log">
<ul class="loglist"></ul>
</section>

<script type="module" src="/src/main.js"></script>
</div>
</body>
</html>
48 changes: 48 additions & 0 deletions src/components/domManipulation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
inputTextFieldEl,
inputBtnEl,
resultTxtEl,
restartMsgEl,
logsEl,
restartBtnEl,
finishBtnEl,
} from "./elements.js";

export function showElement(element) {
element.classList.remove("hidden");
}

export function hideElement(element) {
element.classList.add("hidden");
}

export function clearElements() {
resultTxtEl.innerText = "";
restartMsgEl.innerText = "";
logsEl.innerHTML = "";
restartBtnEl.type = "hidden";
finishBtnEl.type = "hidden";
enableBtn(inputBtnEl);
}

export function enableInputTextField() {
inputTextFieldEl.disabled = false;
}

export function disableInputTextField() {
inputTextFieldEl.disabled = true;
}

export function enableBtn(btnEl) {
btnEl.disabled = false;
}

export function disableBtn(btnEl) {
btnEl.disabled = true;
}

export function appendLog(log) {
const li = document.createElement("li");
li.textContent = log;
logsEl.appendChild(li);
}
10 changes: 10 additions & 0 deletions src/components/elements.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const inputTextFieldEl = document.querySelector(".input #input__textfield");
export const inputBtnEl = document.querySelector(".input #input__btn");
export const resultTxtEl = document.querySelector(".result .result__text");
export const restartBtnEl = document.querySelector(".result .result__btn--restart");
export const finishBtnEl = document.querySelector(".result .result__btn--finish");
export const restartMsgEl = document.querySelector(".result .result__restartMessage");
export const titleStartBtnEl = document.querySelector(".titleScreen .titleScreen__startBtn");
export const titleScreenEl = document.querySelector(".titleScreen");
export const titleContainersEl = document.querySelector(".titleScreen .container");
export const logsEl = document.querySelector(".loglist");
79 changes: 79 additions & 0 deletions src/games/eventHandlers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {
inputTextFieldEl,
inputBtnEl,
restartBtnEl,
resultTxtEl,
restartMsgEl,
finishBtnEl,
titleScreenEl,
titleContainersEl,
} from "../components/elements.js";

import {
showElement,
hideElement,
clearElements,
enableInputTextField,
disableInputTextField,
enableBtn,
disableBtn,
appendLog,
} from "../components/domManipulation.js";

import { RESTART_MESSAGE } from "../utils/constants.js";

let game;

export function initGameInstance(gameInstance) {
game = gameInstance;
}

export function handleInputTextFieldKeyPress(event) {
if (event.key === "Enter") {
inputBtnEl.click();
}
}

export function handleInputBtnClick() {
if (game.compareResult(inputTextFieldEl)) {
resultTxtEl.innerText = game.getResult();
inputTextFieldEl.value = "";
inputTextFieldEl.focus();

if (game.isGameSet) {
disableInputTextField();
disableBtn(inputBtnEl);
restartMsgEl.innerText = RESTART_MESSAGE;
restartBtnEl.type = "button";
finishBtnEl.type = "button";
}

appendLog(game.getLastLog());
}
}

export function handleRestartBtnClick() {
game.restart(() => {
enableInputTextField();
enableBtn(inputBtnEl);
clearElements();
inputTextFieldEl.focus();
});
}

export function handleTitleStartBtnClick() {
enableInputTextField();
hideElement(titleScreenEl);
inputTextFieldEl.focus();
game.generateRandomNumber();
}

export function handleDOMContentLoaded() {
showElement(titleContainersEl);
}

export function handleFinishBtnClick() {
showElement(titleScreenEl);
clearElements();
game.isGameSet = false;
}
126 changes: 126 additions & 0 deletions src/games/game.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import "../utils/isNotIncludes.js";
import { ERROR_MESSAGE, RESULT_NOTHING, RESULT_CORRECT } from "../utils/constants.js";

export class Game {
#answer;

constructor() {
this.#answer = undefined;
this.result = "";
this.isGameSet = false;
this.logs = [];
}

generateRandomNumber() {
let number = "";

while (number.length < 3) {
const randomNumber = Math.floor(Math.random() * 9) + 1;
if (number.isNotIncludes(randomNumber)) {
number += randomNumber;
}
}
console.log(number);
this.#answer = number;
}

compareResult(inputEl) {
let inputValue = inputEl.value;
if (!this.#isValid(inputValue)) {
this.#alertHandler(inputEl);
return false;
}
const { strikeCount, ballCount, nothingCount } = this.#countHits(inputValue);
this.#setResult(strikeCount, ballCount, nothingCount, () => {
this.logs.push(`${inputValue}: ${this.result}`);
});

return true;
}

#countHits(inputValue) {
let nothingCount = 0;
let strikeCount = 0;
let ballCount = 0;
for (let i = 0; i < this.#answer.length; i++) {
if (inputValue[i] === this.#answer[i]) {
strikeCount += 1;
continue;
}

if (inputValue.isNotIncludes(this.#answer[i])) {
nothingCount += 1;
} else {
ballCount += 1;
}
}

return { strikeCount, ballCount, nothingCount };
}

#alertHandler(inputEl) {
alert(ERROR_MESSAGE);
this.#resetInputEl(inputEl);
}

#resetInputEl(inputEl) {
inputEl.value = "";
inputEl.focus();
}

#setResult(strikeCount, ballCount, nothingCount, callback) {
if (nothingCount === 3) {
this.result = RESULT_NOTHING;
} else if (strikeCount === 3) {
this.result = RESULT_CORRECT;
this.isGameSet = true;
} else {
if (strikeCount === 0) {
this.result = `${ballCount}볼`;
} else if (ballCount === 0) {
this.result = `${strikeCount}스트라이크`;
} else {
this.result = `${ballCount}볼 ${strikeCount}스트라이크`;
}
}

callback();
}

getResult() {
return this.result;
}

getLastLog() {
return this.logs[this.logs.length - 1];
}

addLog(logsEl) {
const li = document.createElement("li");
li.textContent = game.getLastLog();
logsEl.appendChild(li);
}

#isValid(input) {
let numPattern = /^[1-9]+$/;

if (!numPattern.test(input)) {
return false;
}
if (input.length !== 3) {
return false;
}
if (new Set(input).size !== input.length) {
return false;
}

return true;
}

restart(complete) {
this.generateRandomNumber();
this.result = "";
this.isGameSet = false;
complete();
}
}
Loading