diff --git a/index.html b/index.html index e4b78ea..c0d2368 100644 --- a/index.html +++ b/index.html @@ -1,10 +1,9 @@ - - + - Vite + React + TS + WORDLE(demo) | kakutory
diff --git a/public/maton.png b/public/maton.png new file mode 100644 index 0000000..ce637a6 Binary files /dev/null and b/public/maton.png differ diff --git a/src/App.css b/src/App.css deleted file mode 100644 index b9d355d..0000000 --- a/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/src/App.tsx b/src/App.tsx index afe48ac..bff786b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,35 +1,47 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' - -function App() { - const [count, setCount] = useState(0) - - return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ) -} +import { useState } from 'react'; + +import { Answer } from './components/answer'; +import { Keyboard } from './components/keyboard'; + +export const App = (): JSX.Element =>{ + // 6*5の配列の初期化 + const initAnswerList: string[][] = new Array(6); + for (let i=0; i<6; i++){ + initAnswerList[i] = new Array(5).fill(""); + } + + // 回答一覧 + // キーボードの文字入力により更新 + const [ answerList, setAnswerList ] = useState(initAnswerList); + + // 回答の判定を行うフラグ + // キーボードのEnter入力により更新 + const [ judge, setJudge ] = useState(false); -export default App + // 現在の状態 + // playing: ゲーム中 + // success: 成功 + // fail: 失敗 + const [ gameStatus, setGameStatus ] = useState("playing"); + + // 正解単語 + const [ answerWord ] = useState("MARIO"); + + return ( +
+ + + +
+ ); +} diff --git a/src/components/answer.tsx b/src/components/answer.tsx new file mode 100644 index 0000000..38a8dc3 --- /dev/null +++ b/src/components/answer.tsx @@ -0,0 +1,199 @@ +import React, { useState, useEffect } from 'react'; + +type Props = { + answerList: string[][]; + judge: boolean; + setJudge: React.Dispatch>; + answerWord: string; + gameStatus: string; + setGameStatus: React.Dispatch>; +} + +export const Answer = (props: Props) => { + + // 回答のCSSスタイル + const answerStyle: React.CSSProperties = { + borderSpacing: '6px 6px', + display: 'flex', + justifyContent: 'center', + marginBottom: '40px', + marginTop: '100px', + }; + + /* td要素のCSSスタイル */ + // Whiteスタイル + const whiteTdStyle: React.CSSProperties = { + border: '2px solid rgb(217, 217, 217)', + width: '60px', + height: '70px', + + fontSize: '30px', + fontWeight: 'bold', + textAlign: 'center', + lineHeight: '60px', + + // 文字色 + color: 'Black', // 背景色Whiteの時のみ + + // 背景色 + backgroundColor: 'White', + + }; + + // Blackスタイル + const blackTdStyle = {...whiteTdStyle}; + blackTdStyle['color'] = 'White'; + blackTdStyle['backgroundColor'] = '3a3a3c'; + + // Yellowスタイル + const yellowTdStyle = {...whiteTdStyle}; + yellowTdStyle['color'] = 'White'; + yellowTdStyle['backgroundColor'] = 'b59f3b'; + + // Greenスタイル + const greenTdStyle = {...whiteTdStyle}; + greenTdStyle['color'] = 'White'; + greenTdStyle['backgroundColor'] = '538d4e'; + + // ラウンド + const [ round, setRound ] = useState(0); + + // リストの初期化 + const initMatchList: string[][] = new Array(6); + for (let i=0; i<6; i++){ + initMatchList[i] = new Array(5).fill("White"); + } + + // 回答欄のCSSリスト + // White: 判定していない + // Black: 文字も位置も無一致 + // Yellow: 文字のみ一致 + // Green: 文字も位置も一致 + // const [ matchList, setMatchList ] = useState(initMatchList); + + // リストの初期化 + const initMatchStyleList: React.CSSProperties[][] = new Array(6); + for (let i=0; i<6; i++){ + initMatchStyleList[i] = new Array(5).fill(whiteTdStyle); + } + + // 回答欄のCSSリスト + // White: 判定していない + // Black: 文字も位置も無一致 + // Yellow: 文字のみ一致 + // Green: 文字も位置も一致 + const [ matchStyleList, setMatchStyleList ] = useState(initMatchStyleList); + + // 単語一致判定 + const wordMatchJudgement = () => { + // 一度ディープコピーする + const tmpMatchStyleList = Array.from(matchStyleList); + + // 1文字ずつ判定 + for (let i=0; i<5; i++){ + + // 文字が一致 + if (props.answerWord.indexOf(props.answerList[round-1][i]) !== -1){ + + // 位置も一致(Green) + if (props.answerList[round-1][i] === props.answerWord[i]){ + tmpMatchStyleList[round-1][i] = greenTdStyle; + } + + // 文字だけ一致(Yellow) + else { + tmpMatchStyleList[round-1][i] = yellowTdStyle; + } + } + + // 文字も位置も一致していない(Black) + else { + tmpMatchStyleList[round-1][i] = blackTdStyle; + } + } + + console.log(tmpMatchStyleList[round-1]); + return tmpMatchStyleList; + } + + // クリア判定 + const clearJudgement = () => { + + // ワードを抽出 + const wordList = []; + for (let j = 0; j < 5; j++) { + wordList.push(props.answerList[round-1][j]); + } + const submitWord = wordList.join(""); + console.log(submitWord); + + if (submitWord == props.answerWord){ + alert("clear!!"); + return "success"; + } + + else if (round == 6) { + alert("fail..."); + return "fail"; + } + + return "playing"; + } + + + // Appコンポーネントのjudgeが変化した時に呼ばれる + useEffect(() => { + + // Enterを押したら + if (props.judge === true){ + // 一度フラグをおろす + props.setJudge(false); + } + + // フラグをおろしてからここへ + else { + // コンポーネント初期化時にここを通る + if (round == 0){ + setRound(round+1); // ラウンドを1に + return; + } + + // ゲーム継続中なら + if (props.gameStatus == "playing"){ + // 単語一致判定 + const tmpMatchStyleList = wordMatchJudgement(); + + // クリア判定 + clearJudgement(); + + // スタイル更新 + setMatchStyleList(tmpMatchStyleList); + // ラウンド更新 + setRound(round+1); + + console.log(tmpMatchStyleList[0]); + } + } + + }, [props.judge]); + + + return ( + // mapにより回答table作成 +
+ + + + {props.answerList.map((answer, i) => ( + + {answer.map((letter, j) => ( + + ))} + + ))} + +
{letter}
+ +
+ ); +} \ No newline at end of file diff --git a/src/components/keyboard.tsx b/src/components/keyboard.tsx new file mode 100644 index 0000000..21e47f7 --- /dev/null +++ b/src/components/keyboard.tsx @@ -0,0 +1,144 @@ +import React, { useState } from "react"; + +type appProps = { + setAnswerList: React.Dispatch>; + setJudge: React.Dispatch>; +}; + +type Props = { + rowcnt: number; + setColumncnt: React.Dispatch>; + columncnt: number; + setRowcnt: React.Dispatch>; + setAnswerList: React.Dispatch>; + keyLayout: string[]; + setJudge: React.Dispatch>; +}; + +const KeyboardRow = (props: Props) => { + const updateAnswer = (prevState: string[][], letter: string, row: number, column: number) => { + const tmpList = Array.from(prevState); + tmpList[row][column] = letter; + + return tmpList; + } + + const handleClick = (event: React.MouseEvent) => { + const letter = event.currentTarget.value; + + // Enter入力 + if (letter == "Enter") { + // 文字数不足 + if (props.columncnt < 5){ + alert("文字数が足りません"); + } + + // 5文字入力した状態 + else { + // フラグ送信(正解判定の依頼) + props.setJudge(true); + + // 列数リセット + props.setColumncnt(0); + + // 次の行へ移行 + props.setRowcnt((prev) => prev+1); + } + } + + // Delete入力 + else if(letter == "Delete"){ + // 1文字以上入力 + if(props.columncnt > 0){ + props.setAnswerList((prevState) => updateAnswer(prevState, "", props.rowcnt, props.columncnt-1)); + props.setColumncnt((prev) => prev-1); + } + } + + // アルファベット入力 + else if (props.columncnt < 5) { + props.setAnswerList((prevState) => updateAnswer(prevState, letter, props.rowcnt, props.columncnt)); + props.setColumncnt((prev) => prev+1); + } + }; + + // キーボードのCSSスタイル + const keyboardStyle: React.CSSProperties = { + borderSpacing: "6px 6px", + display: "flex", + justifyContent: "center" + }; + + // ボタンのCSSスタイル + const buttonStyle: React.CSSProperties = { + backgroundColor: "rgb(217, 217, 217)", + borderRadius: "4px", + border: "none", + width: "45px", + height: "60px", + + fontSize: "13px", + fontWeight: "bold", + cursor: "pointer" + }; + + return ( + // mapによりキーボードtable作成 + + + + {props.keyLayout.map((key, i) => ( + + ))} + + +
+ {/* ボタン */} + +
+ ); +}; + + +export const Keyboard = (props: appProps) => { + const upKeyLayout: string[] = ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"]; + const middleKeyLayout: string[] = ["A", "S", "D", "F", "G", "H", "J", "K", "L"]; + const downKeyLayout: string[] = ["Enter", "Z", "X", "C", "V", "B", "N", "M", "Delete"]; + + const [rowcnt,setRowcnt] = useState(0); + const [columncnt,setColumncnt] = useState(0); + + return ( +
+ + + +
+ ); +}; \ No newline at end of file diff --git a/src/index.css b/src/index.css index 6119ad9..c798e28 100644 --- a/src/index.css +++ b/src/index.css @@ -1,4 +1,4 @@ -:root { +/* :root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; @@ -65,4 +65,4 @@ button:focus-visible { button { background-color: #f9f9f9; } -} +} */ diff --git a/src/main.tsx b/src/main.tsx index 3d7150d..9702424 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,6 +1,6 @@ import React from 'react' import ReactDOM from 'react-dom/client' -import App from './App.tsx' +import { App } from './App.tsx' import './index.css' ReactDOM.createRoot(document.getElementById('root')!).render(