react web game : react router

This commit is contained in:
haerong22
2021-03-25 21:52:18 +09:00
parent 8447297449
commit 230eff9498
17 changed files with 448 additions and 7 deletions

View File

@@ -0,0 +1,5 @@
import React from "react";
import ReactDom from "react-dom";
import MineSearch from "./MineSearch";
ReactDom.render(<MineSearch />, document.querySelector("#root"));

View File

@@ -0,0 +1,21 @@
<html>
<head>
<meta charset="utf-8" />
<title>지뢰찾기</title>
<style>
table {
border-collapse: collapse;
}
td {
border: 1px solid black;
width: 40px;
height: 40px;
text-align: center;
}
</style>
</head>
<body>
<div id="root"></div>
<script src="./dist/app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,20 @@
import React from "react";
import { BrowserRouter, HashRouter, Route } from "react-router-dom";
import NumberBaseBall from "./games/NumberBaseball";
import RSP from "./games/RSP";
import Lotto from "./games/Lotto"
const Games = () => {
return (
<BrowserRouter>
<div>
<Route path="/number-baseball" component={NumberBaseBall} />
<Route path="/rock-scissors-paper" component={RSP} />
<Route path="/lotto-generator" component={Lotto} />
</div>
</BrowserRouter>
);
};
export default Games;

View File

@@ -1,5 +1,5 @@
import React from "react";
import ReactDom from "react-dom";
import MineSearch from "./MineSearch";
import Games from "./Games";
ReactDom.render(<MineSearch />, document.querySelector("#root"));
ReactDom.render(<Games />, document.querySelector("#root"));

View File

@@ -0,0 +1,23 @@
import React, { memo } from "react";
const Ball = memo(({ number }) => {
let background;
if (number <= 10) {
background = "red";
} else if (number <= 20) {
background = "orange";
} else if (number <= 30) {
background = "yellow";
} else if (number <= 40) {
background = "blue";
} else {
background = "green";
}
return (
<div className="ball" style={{ background }}>
{number}
</div>
);
});
export default Ball;

View File

@@ -0,0 +1,78 @@
import React, {
useState,
useRef,
useEffect,
useMemo,
useCallback,
} from "react";
import Ball from "./Ball";
function getWinNumbers() {
console.log("getWinNumbers");
const candidate = Array(45)
.fill()
.map((v, i) => i + 1);
const shuffle = [];
while (candidate.length > 0) {
shuffle.push(
candidate.splice(Math.floor(Math.random() * candidate.length), 1)[0]
);
}
const bonusNumber = shuffle[shuffle.length - 1];
const winNumbers = shuffle.slice(0, 6).sort((p, c) => p - c);
return [...winNumbers, bonusNumber];
}
const Lotto = () => {
const lottoNumbers = useMemo(() => getWinNumbers(), []);
const [winNumbers, setWinNumbers] = useState(lottoNumbers);
const [winBalls, setWinBalls] = useState([]);
const [bonus, setBonus] = useState(null);
const [redo, setRedo] = useState(false);
const timeouts = useRef([]);
useEffect(() => {
runTimeouts();
return () => {
timeouts.current.forEach((v) => {
clearTimeout(v);
});
};
}, [timeouts.current]);
const runTimeouts = () => {
for (let i = 0; i < winNumbers.length - 1; i++) {
timeouts.current[i] = setTimeout(() => {
setWinBalls((prevBalls) => [...prevBalls, winNumbers[i]]);
}, (i + 1) * 1000);
}
timeouts.current[6] = setTimeout(() => {
setBonus(winNumbers[6]);
setRedo(true);
}, 7000);
};
const onClickRedo = useCallback(() => {
setWinNumbers(getWinNumbers());
setWinBalls([]);
setBonus(null);
setRedo(false);
timeouts.current = [];
}, [winNumbers]);
return (
<>
<div>당첨 숫자</div>
<div id="결과창">
{winBalls.map((v) => (
<Ball key={v} number={v} />
))}
</div>
<div>보너스</div>
{bonus && <Ball number={bonus} />}
{redo && <button onClick={onClickRedo}> !</button>}
</>
);
};
export default Lotto;

View File

@@ -0,0 +1,83 @@
import React, { useState, useRef, memo } from 'react';
import Try from './Try';
// 숫자 4개를 겹치지않고 랜덤하고 뽑는 함수
function getNumbers() {
const candidate = [1,2,3,4,5,6,7,8,9];
const array = [];
for (let i = 0; i < 4; i++) {
const chosen = candidate.splice(Math.floor(Math.random() * (9 - i)), 1)[0];
array.push(chosen);
}
return array;
}
const NumberBaseball = memo(() => {
const [result, setresult] = useState('');
const [value, setvalue] = useState('');
const [answer, setanswer] = useState(getNumbers());
const [tries, settries] = useState([]);
const inputRef = useRef(null);
const onSubmitForm = (e) => {
e.preventDefault();
if(value === answer.join('')) { // 정답
setresult('홈런!');
settries((prevTries) => {
return [...prevTries, { try: value, result: '홈런!'}]
})
alert('게임을 다시 시작합니다.');
setvalue('');
setanswer(getNumbers());
settries([]);
inputRef.current.focus();
} else {
const answerArray = value.split('').map((v) => parseInt(v));
let strike = 0;
let ball = 0 ;
if(tries.length >= 9) { // 10번이상 틀렸을 때
setresult(`실패! 답은 ${answer.join(',')} 였습니다!`);
alert('게임을 다시 시작합니다.');
setvalue('');
setanswer(getNumbers());
settries([]);
} else {
for (let i = 0; i < 4; i++) {
if(answerArray[i] === answer[i]) {
strike++;
} else if (answer.includes(answerArray[i])) {
ball++;
}
}
settries((prevTries) => {
return [...tries, {try: value, result: `${strike} 스트라이크, ${ball} 볼 입니다.`}]
})
setvalue('');
}
inputRef.current.focus();
}
}
const onChangeInput = (e) => {
setvalue(e.target.value);
}
return (
<>
<h1>{result}</h1>
<form onSubmit={onSubmitForm}>
<input ref={inputRef} maxLength={4} value={value} onChange={onChangeInput}/>
<button>입력!</button>
</form>
<div>시도: {tries.length}</div>
<ul>
{tries.map((v, i) => {
return (
<Try key={`${i + 1}차 시도 :`} tryInfo={v} index={i} />
)
})}
</ul>
</>
)
});
export default NumberBaseball; // import NumberBaseball

View File

@@ -0,0 +1,89 @@
import React, { useState, useRef, useEffect } from "react";
const rspCoords = {
바위: "0",
가위: "-142px",
: "-284px",
};
const scores = {
가위: 1,
바위: 0,
: -1,
};
const computerChoice = (imgCoord) => {
return Object.entries(rspCoords).find(function (v) {
return v[1] === imgCoord;
})[0];
};
const RSP = () => {
const [result, setResult] = useState("");
const [imgCoord, setImgCoord] = useState(rspCoords.바위);
const [score, setScore] = useState(0);
const interval = useRef();
useEffect(() => {
// componentDidMount, componentDidUpdate 역할
// setTimeout(changeHand, 100);
interval.current = setInterval(changeHand, 100);
return () => {
// componentWillUnMount 역할
clearInterval(interval.current);
};
}, [imgCoord]);
const changeHand = () => {
if (imgCoord === rspCoords.바위) {
setImgCoord(rspCoords.가위);
} else if (imgCoord === rspCoords.가위) {
setImgCoord(rspCoords.);
} else if (imgCoord === rspCoords.) {
setImgCoord(rspCoords.바위);
}
};
const onClickBtn = (choice) => () => {
clearInterval(interval.current);
const myScore = scores[choice];
const cpuScore = scores[computerChoice(imgCoord)];
const diff = myScore - cpuScore;
if (diff === 0) {
setResult("비겼습니다!");
} else if ([-1, 2].includes(diff)) {
setResult("이겼습니다!");
setScore((prevScore) => prevScore + 1);
} else {
setResult("졌습니다ㅠㅜ");
setScore((prevScore) => prevScore - 1);
}
setTimeout(changeHand, 2000);
};
return (
<>
<div
id="computer"
style={{
background: `url(https://en.pimg.jp/023/182/267/1/23182267.jpg) ${imgCoord} 0`,
}}
/>
<div>
<button id="rock" className="btn" onClick={onClickBtn("바위")}>
바위
</button>
<button id="scissor" className="btn" onClick={onClickBtn("가위")}>
가위
</button>
<button id="paper" className="btn" onClick={onClickBtn("보")}>
</button>
</div>
<div>{result}</div>
<div>현재 {score}</div>
</>
);
};
export default RSP;

View File

@@ -0,0 +1,11 @@
import React, { memo } from 'react';
const Try = memo(({ tryInfo }) => {
return (
<li>
<div>{tryInfo.try}</div>
<div>{tryInfo.result}</div>
</li>
)
});
export default Try;

View File

@@ -1,7 +1,7 @@
<html>
<head>
<meta charset="utf-8" />
<title>지뢰찾기</title>
<title>Games</title>
<style>
table {
border-collapse: collapse;

View File

@@ -1026,7 +1026,6 @@
"version": "7.13.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz",
"integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
@@ -3045,6 +3044,27 @@
}
}
},
"history": {
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
"requires": {
"@babel/runtime": "^7.1.2",
"loose-envify": "^1.2.0",
"resolve-pathname": "^3.0.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0",
"value-equal": "^1.0.1"
}
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"requires": {
"react-is": "^16.7.0"
}
},
"hpack.js": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
@@ -3671,6 +3691,15 @@
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
},
"mini-create-react-context": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
"integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==",
"requires": {
"@babel/runtime": "^7.12.1",
"tiny-warning": "^1.0.3"
}
},
"minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@@ -4126,6 +4155,16 @@
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"dev": true
},
"prop-types": {
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
}
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
@@ -4230,12 +4269,63 @@
"scheduler": "^0.20.1"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"react-refresh": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz",
"integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==",
"dev": true
},
"react-router": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
"integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==",
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
"hoist-non-react-statics": "^3.1.0",
"loose-envify": "^1.3.1",
"mini-create-react-context": "^0.4.0",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.2",
"react-is": "^16.6.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"path-to-regexp": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
"requires": {
"isarray": "0.0.1"
}
}
}
},
"react-router-dom": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz",
"integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==",
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-router": "5.2.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
}
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
@@ -4289,8 +4379,7 @@
"regenerator-runtime": {
"version": "0.13.7",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
"dev": true
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
},
"regenerator-transform": {
"version": "0.14.5",
@@ -4419,6 +4508,11 @@
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"dev": true
},
"resolve-pathname": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
},
"resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@@ -5125,6 +5219,16 @@
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
"dev": true
},
"tiny-invariant": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
"integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
},
"tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -5366,6 +5470,11 @@
"integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==",
"dev": true
},
"value-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View File

@@ -10,7 +10,9 @@
"license": "MIT",
"dependencies": {
"react": "^17.0.1",
"react-dom": "^17.0.1"
"react-dom": "^17.0.1",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0"
},
"devDependencies": {
"@babel/core": "^7.12.10",