본문 바로가기
💻CODING/react. vue

[react] 리액트 끝말잇기 게임 만들기(ft. 컴포넌트 구조)

by 코딩하는 갓디노 2021. 3. 24.

숫자야구

 

리액트 class/hooks로 구현한
숫자야구 게임 입니다.

 

예제는 인프런의 제로초, "조현영"님의 강의를 들으면서 공부한 내용입니다.

 

구현 순서

순서 특이사항
컴퓨터가 랜덤 4자리 숫자 문제 제출 예시) 4385, 조건)겹치지 않는 4개
사용자가 답을 입력  
입력한 답의 결과 발표

결과 발표: 홈런/스트라이크/볼
홈런) 숫자가 모두 일치할 경우
스트라이크) 자리수가 맞을 경우
볼) 자리수는 맞이 않으나, 숫자가 맞을 경우

답이 맞을때까지 10회 제한을 둠



파일 구조

client.jsx >NumberBaseball.jsx >Try.jsx

 

client.jsx

import React from 'react';
import ReactDom from 'react-dom';
import NumberBaseball from './NumberBaseball';

ReactDom.render(<NumberBaseball />, document.querySelector('#root'));

 

NumberBaseball.jsx - class 방식

import React, { createRef } from 'react';
import Try from './Try';

function getNumbers() { //숫자4개를 겹치지 않게 뽑는 함수
    const candidates = [1,2,3,4,5,6,7,8,9];
    const chosenNum = [];
    //splice()가 index를 하나씩 빼주기 때문에 뽑을 숫자가 하나씩 줄음 
    //안했을 경우, 랜덤 숫자가 undefined가 나옴 -> 9-i로 index 수 반영
    for (let i=0; i<4; i++) {
        const mixedNum = candidates.splice(Math.floor(Math.random() * (9-i)), 1);
        chosenNum.push(mixedNum[0]);
    }
    console.log('정답:' + chosenNum.join(''));
    return chosenNum;   
} 

class NumberBaseball extends React.Component {   
    state = {
        result: '',
        value: '',
        answer: getNumbers(),
        tries: [], //입력한 데이터 기록, push로 데이터 저장하면 안됨
    };

    onSubmit =(e) => { //property 생성
        e.preventDefault();
        //숫자야구 로직
        if(this.state.value === this.state.answer.join('')) { //정답일 경우,
            this.setState ((prevState) => {
                return {
                    result: '홈런!',
                    value: 'this.state.answer',
                    tries: [...prevState.tries, {try: this.state.value, result: '홈런'}],
                }        
            })
            alert('게임을 다시 시작합니다.'); //초기화
            this.setState({ //초기화
                value: '',
                answer: getNumbers(),
                tries: [],
            });
            this.input.current.focus;
        }else{ //틀렸을 경우, strike, ball 안내
            //입력한 value값을 to array(split()) map으로 요소 하나씩 확인
            //string이므로 parseInt로 number 변환
           const answerArray = this.state.value.split('').map(item => parseInt(item));
           let strike  = 0;
           let ball = 0; 
           if (this.state.tries.length >= 9) { //10번이상 실패시
            this.setState({
                result: `10번 넘게 틀려서 실패하였습니다. 정답은 ${this.state.answer} 입니다.`
            });
            alert('게임을 다시 시작합니다.');
            this.setState({ //초기화
                value: '',
                answer: getNumbers(),
                tries: [],
            });
            this.input.current.focus;
           } else { //10번 미만 실패시
            //몇 스크라이크, 몇 볼인지 확인하는 코드
            for(let i=0; i<4; i+=1) { //strike 체크
                if(answerArray[i] === this.state.answer[i]) {
                    strike +=1;
                }else if (this.state.answer.includes(answerArray[i])) { //ball 체크
                    ball+=1;
                }
            }
            this.setState((prevState) => {
                return {
                    tries: [...prevState.tries, {
                        try: this.state.value, result: `${strike} 스트라이크 ${ball} 볼입니다.`
                    }],
                    value :'',
                }   
            })
           }
           console.log(answerArray);
           this.setState({
               result: '땡',
               value: '',
           }); 
        }
    }   

    onChange = (e) => { //property 생성
        this.setState({value : e.target.value})
    }

    inputRef = createRef(); //this.inputRef(=property) 생성

    render(){
        return (
            <>
            <h1>{this.state.result}</h1>
            <form onSubmit={this.onSubmit}>
                <input type="number" maxLength={4} ref={this.inputRef} value={this.state.value} 
                onChange={this.onChange} />
                <button type='submit'>입력</button>
            </form>
            
            <div>시도횟수: {this.state.tries.length}</div>
            <ul>
                {this.state.tries.map((item, i) => {
                    return (
                        <Try key={`${i+1}차 시도 :`} value={item.try} result={item.result} 
                        index={i} />
                    );
                })}     
            </ul>   
            </>
        )
    }
}   

export default NumberBaseball;

 

NumberBaseball.jsx - hooks 방식

import React, { useState, useRef }from 'react';
import Try from './Try';

const getNumbers = () => { //숫자4개를 겹치지 않게 뽑는 함수
    const candidates = [1,2,3,4,5,6,7,8,9];
    const chosenNum = [];
    //splice()가 index를 하나씩 빼주기 때문에 뽑을 숫자가 하나씩 줄음 
    //안했을 경우, 랜덤 숫자가 undefined가 나옴 -> 9-i로 index 수 반영
    for (let i=0; i<4; i++) {
        const mixedNum = candidates.splice(Math.floor(Math.random() * (9-i)), 1);
        chosenNum.push(mixedNum[0]);
    }
    console.log('정답:' + chosenNum.join(''));
    return chosenNum;   

} 

const NumberBaseball = () => {
    //hooks로 state선언
    const [result, setResult] = useState('');
    const [value, setValue] = useState('');
    const [answer, setAnswer] = useState(getNumbers());
    const [tries, setTries] = useState([]);
    const onRefInput = useRef(null);

    const onSubmit =(e) => {
        e.preventDefault();
        //숫자야구 로직
        if(value === answer.join('')) { //정답일 경우,
            setResult('홈런!');
            setTries((prevTries)=> { //에전 데이터로 새로운 것을 만들때는 함수형
                return  [...prevTries, {try: value, result: '홈런'}]
            });
            
            alert('게임을 다시 시작합니다.'); //초기화
            setValue('');
            setAnswer(getNumbers());
            setTries([]);

        }else{ //틀렸을 경우, strike, ball 안내
            //입력한 value값을 to array(split()) map으로 요소 하나씩 확인
            //string이므로 parseInt로 number 변환
           const answerArray = value.split('').map(item => parseInt(item));
           let strike  = 0;
           let ball = 0; 
           if (tries.length >= 9) { //10번이상 실패시
            setResult( `10번 넘게 틀려서 실패하였습니다. 정답은 ${answer} 입니다.`);
            alert('게임을 다시 시작합니다.');
            setValue('');
            setAnswer(getNumbers());
            setTries([]);
           } else { //10번 미만 실패시
            //몇 스크라이크, 몇 볼인지 확인하는 코드
            for(let i=0; i<4; i+=1) { //strike 체크
                if(answerArray[i] === answer[i]) {
                    strike +=1;
                }else if (answer.includes(answerArray[i])) { //ball 체크
                    ball+=1;
                }
            }
            setTries((prevTries) => {return [...prevTries, 
                {try: value, result: `${strike} 스트라이크 ${ball} 볼입니다.`}]}
            )

           }
           setResult('땡');
           setValue('');
           onRefInput.current.focus();
        }
    }   

    const onChange = (e) => {
        setValue(e.target.value);
        //console.log(e.target.value);
    }

    return (
        <>
        <h1>{result}</h1>
        <form onSubmit={onSubmit}>
            <input type="number" maxLength={4} value={value} onChange={onChange} ref={onRefInput}/>
            <button type='submit'>입력</button>
        </form>
        
        <div>시도횟수: {tries.length}</div>
        <ul>
            {tries.map((item, i) => {
                return (
                    <Try key={`${i+1}차 시도 :`} value={item.try} result={item.result} 
                    index={i} />
                );
            })}     
        </ul>   
        </>
    );
};  

export default NumberBaseball;

 

Try.jsx

import React, { Component } from 'react';

const Try = ({value, result, index}) => {
    return (
        <li>
            내가 낸 숫자: {value} / 결과:  { result} / {index + 1} 번째
        </li>
    )
}

export default Try;

 

반응형

댓글