From 23c72ac8f1935eef91656b15de06ff468c9509df Mon Sep 17 00:00:00 2001 From: Suke-H Date: Sun, 21 Apr 2024 17:24:13 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20#23=20=E3=82=B2=E3=83=BC=E3=83=A0?= =?UTF-8?q?=E3=82=AF=E3=83=AA=E3=82=A2/=E3=82=AA=E3=83=BC=E3=83=90?= =?UTF-8?q?=E3=83=BC=E6=99=82=E3=81=AB=E6=AD=A3=E8=A7=A3=E5=88=A4=E5=AE=9A?= =?UTF-8?q?=E3=82=92=E3=81=97=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gameState(Playing/GameClear/GameOver)を再度追加 - GameClear/GameOver時はEnterによる判定を無効化 --- src/App.tsx | 10 ++++++++-- src/components/keyboard.tsx | 8 ++++++-- src/game_logics/checkClear.ts | 12 ++++++++++-- src/game_logics/pushedEnterProcess.ts | 9 +++++++-- src/interfaces/GameState.ts | 2 ++ src/utils/limitDiaryPlay.ts | 18 ++++++++++++++++++ 6 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 src/interfaces/GameState.ts create mode 100644 src/utils/limitDiaryPlay.ts diff --git a/src/App.tsx b/src/App.tsx index 1752108..8245e08 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,6 +6,7 @@ import { Notes } from "./components/notes"; import { ShareResultButton } from "./components/ShareResultButton"; import { AlphabetMatch } from "./interfaces/AlphabetMatch"; +import { GameState } from "./interfaces/GameState"; import { pushedEnterProcess } from "./game_logics/pushedEnterProcess"; import { getTodaysWord } from "./utils/getTodaysWord"; @@ -35,7 +36,9 @@ export const App = (): JSX.Element => { /* State */ const [answerList, setAnswerList] = useState(initAnswerList); // 回答欄の文字列 - const [ matchList, setMatchList ] = useState(initMatchList); // 回答欄のマッチ状況(White/Black/Yellow/Grren) + const [matchList, setMatchList] = useState(initMatchList); // 回答欄のマッチ状況(White/Black/Yellow/Grren) + // クリア判定のための変数 + const [gameState, setGameState] = useState("Playing"); // ゲームの状態(Playing/GameClear/GameOver) const [judge, setJudge] = useState(false); // Enterを押したか const [correctAnswer, setCorrectAnswer] = useState(""); // 今日の単語 const [todaysNo, setTodaysNo] = useState(0); // 今日が何回目か @@ -53,8 +56,10 @@ export const App = (): JSX.Element => { // Enterを押した際 useEffect(() => { if (!judge) return; + if (gameState !== "Playing") return; - pushedEnterProcess(correctAnswer, answerList, matchList, round, setMatchList, setAlphabetMatch) + pushedEnterProcess(correctAnswer, answerList, matchList, round, + setMatchList, setAlphabetMatch, setGameState) .then((isValid) => { /* 単語が妥当でない場合 */ if (!isValid) { @@ -95,6 +100,7 @@ export const App = (): JSX.Element => { setAnswerList={setAnswerList} setJudge={setJudge} alphabetMatch={alphabetMatch} + gameState={gameState} /> >; setJudge: React.Dispatch>; alphabetMatch: AlphabetMatch; + gameState: GameState; }; type Props = { @@ -22,6 +24,7 @@ type Props = { keyLayout: string[]; setJudge: React.Dispatch>; alphabetMatch: AlphabetMatch; + gameState: GameState; }; @@ -194,8 +197,6 @@ const KeyboardRow = (props: Props) => { "Delete", ]; - - return (
{ keyLayout={upKeyLayout} setJudge={props.setJudge} alphabetMatch={props.alphabetMatch} + gameState={props.gameState} /> { keyLayout={middleKeyLayout} setJudge={props.setJudge} alphabetMatch={props.alphabetMatch} + gameState={props.gameState} /> { keyLayout={downKeyLayout} setJudge={props.setJudge} alphabetMatch={props.alphabetMatch} + gameState={props.gameState} />
); diff --git a/src/game_logics/checkClear.ts b/src/game_logics/checkClear.ts index ea500ca..7dd328b 100644 --- a/src/game_logics/checkClear.ts +++ b/src/game_logics/checkClear.ts @@ -1,13 +1,16 @@ +import { GameState } from "../interfaces/GameState"; + // クリア判定 export const checkClear = ( correctAnswer: string, answerList: string[][], round: number - ) => { + ): GameState => { + // 正解が空文字だった場合はサーバーエラー if (correctAnswer === "") { alert("Server Error: Please reload the page."); - return; + return "Playing"; } // ワードを抽出 @@ -20,9 +23,14 @@ export const checkClear = ( // クリア if (submitWord == correctAnswer) { alert("clear!!"); + return "GameClear"; } // 6回目で不正解だった場合 else if (round == 6) { alert(correctAnswer); + return "GameOver"; } + + // ゲーム続行 + return "Playing"; }; diff --git a/src/game_logics/pushedEnterProcess.ts b/src/game_logics/pushedEnterProcess.ts index 1232333..7ceb8ba 100644 --- a/src/game_logics/pushedEnterProcess.ts +++ b/src/game_logics/pushedEnterProcess.ts @@ -1,3 +1,5 @@ +import { GameState } from "../interfaces/GameState"; + import { checkWordValidity } from "./checkWordValidity"; import { checkWordMatch } from "./checkWordMatch"; import { checkClear } from "./checkClear"; @@ -10,7 +12,8 @@ export const pushedEnterProcess = async ( matchList: string[][], round: number, setMatchList: React.Dispatch>, - setAlphabetMatch: React.Dispatch> + setAlphabetMatch: React.Dispatch>, + setGameState: React.Dispatch> ) : Promise => { // 単語の妥当性判定 @@ -20,7 +23,9 @@ export const pushedEnterProcess = async ( // 単語一致判定 const tmpMatchList = checkWordMatch(correctAnswer, answerList, matchList, round); // クリア判定 - checkClear(correctAnswer, answerList, round); + const _gameState = checkClear(correctAnswer, answerList, round); + setGameState(_gameState); + // スタイル更新 setMatchList(tmpMatchList); diff --git a/src/interfaces/GameState.ts b/src/interfaces/GameState.ts new file mode 100644 index 0000000..261f4d8 --- /dev/null +++ b/src/interfaces/GameState.ts @@ -0,0 +1,2 @@ +// ゲームの状態 +export type GameState = 'Playing' | 'GameClear' | 'GameOver'; \ No newline at end of file diff --git a/src/utils/limitDiaryPlay.ts b/src/utils/limitDiaryPlay.ts new file mode 100644 index 0000000..96d0a40 --- /dev/null +++ b/src/utils/limitDiaryPlay.ts @@ -0,0 +1,18 @@ +// export const saveGameData = (gameData: ) => { +// // gameDataオブジェクトには、推測した単語や試行回数などが含まれる +// const data = JSON.stringify(gameData); +// localStorage.setItem('wordleGameData', data); +// } + +// export const loadGameData() { +// const data = localStorage.getItem('wordleGameData'); +// if (data) { +// return JSON.parse(data); +// } else { +// return null; // データが存在しない場合はnullを返す +// } +// } + +// function clearGameData() { +// localStorage.removeItem('wordleGameData'); +// } From 5a8a2f36a607c77b6b243af7a200611d53cbc462 Mon Sep 17 00:00:00 2001 From: Suke-H Date: Sun, 21 Apr 2024 17:29:56 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20#23=20=E3=82=B2=E3=83=BC=E3=83=A0?= =?UTF-8?q?=E3=82=AF=E3=83=AA=E3=82=A2/=E3=82=AA=E3=83=BC=E3=83=90?= =?UTF-8?q?=E3=83=BC=E6=99=82=E3=81=AB=E3=82=AD=E3=83=BC=E3=83=9C=E3=83=BC?= =?UTF-8?q?=E3=83=89=E5=85=A5=E5=8A=9B=E3=81=95=E3=81=9B=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GameClear/GameOver時にキーボード入力を無効化 --- src/components/keyboard.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/keyboard.tsx b/src/components/keyboard.tsx index 951e4cf..3c9f010 100644 --- a/src/components/keyboard.tsx +++ b/src/components/keyboard.tsx @@ -56,6 +56,9 @@ const KeyboardRow = (props: Props) => { ) => { const letter = event.currentTarget.value; + // ゲームが終了している場合は何もしない + if (props.gameState !== "Playing") return; + // Enter入力 if (letter == "Enter") { // 文字数不足 From bcc48e1616391e98f63875a34ba8f66ed6a54bb9 Mon Sep 17 00:00:00 2001 From: Suke-H Date: Sun, 21 Apr 2024 19:01:54 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20#23=20=E3=83=95=E3=82=A9=E3=83=AB?= =?UTF-8?q?=E3=83=80=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 174 ++++----- src/components/keyboard.tsx | 466 +++++++++++++------------ src/game_logics/checkClear.ts | 57 ++- src/game_logics/pushedEnterProcess.ts | 79 +++-- src/interfaces/GameData.ts | 4 + src/{utils => load}/limitDiaryPlay.ts | 0 src/{interfaces => types}/GameState.ts | 0 src/utils/saveAndLoadInLocalStorage.ts | 18 + 8 files changed, 416 insertions(+), 382 deletions(-) create mode 100644 src/interfaces/GameData.ts rename src/{utils => load}/limitDiaryPlay.ts (100%) rename src/{interfaces => types}/GameState.ts (100%) create mode 100644 src/utils/saveAndLoadInLocalStorage.ts diff --git a/src/App.tsx b/src/App.tsx index 8245e08..819f761 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,112 +6,116 @@ import { Notes } from "./components/notes"; import { ShareResultButton } from "./components/ShareResultButton"; import { AlphabetMatch } from "./interfaces/AlphabetMatch"; -import { GameState } from "./interfaces/GameState"; +import { GameState } from "./types/GameState"; import { pushedEnterProcess } from "./game_logics/pushedEnterProcess"; import { getTodaysWord } from "./utils/getTodaysWord"; import { makeGameResultText } from "./utils/makeGameResultText"; export const App = (): JSX.Element => { - // (リスト初期化) - const initAnswerList: string[][] = new Array(6); - for (let i = 0; i < 6; i++) { - initAnswerList[i] = new Array(5).fill(""); - } - const initMatchList: string[][] = new Array(6); - for (let i = 0; i < 6; i++) { - initMatchList[i] = new Array(5).fill("White"); - } - // 全アルファベットを'NoUse'で初期化する関数 - const initializeAlphabetMatch = (): AlphabetMatch => { - const result: AlphabetMatch = {}; - for (let charCode = 65; charCode <= 90; charCode++) { - const letter = String.fromCharCode(charCode); - result[letter] = 'NoUse'; - } - return result; - }; - // アルファベットの判定リストを初期化 - const initAlphabetMatch = initializeAlphabetMatch(); + // (リスト初期化) + const initAnswerList: string[][] = new Array(6); + for (let i = 0; i < 6; i++) { + initAnswerList[i] = new Array(5).fill(""); + } + const initMatchList: string[][] = new Array(6); + for (let i = 0; i < 6; i++) { + initMatchList[i] = new Array(5).fill("White"); + } + // 全アルファベットを'NoUse'で初期化する関数 + const initializeAlphabetMatch = (): AlphabetMatch => { + const result: AlphabetMatch = {}; + for (let charCode = 65; charCode <= 90; charCode++) { + const letter = String.fromCharCode(charCode); + result[letter] = "NoUse"; + } + return result; + }; + // アルファベットの判定リストを初期化 + const initAlphabetMatch = initializeAlphabetMatch(); - /* State */ - const [answerList, setAnswerList] = useState(initAnswerList); // 回答欄の文字列 - const [matchList, setMatchList] = useState(initMatchList); // 回答欄のマッチ状況(White/Black/Yellow/Grren) - // クリア判定のための変数 - const [gameState, setGameState] = useState("Playing"); // ゲームの状態(Playing/GameClear/GameOver) - const [judge, setJudge] = useState(false); // Enterを押したか - const [correctAnswer, setCorrectAnswer] = useState(""); // 今日の単語 - const [todaysNo, setTodaysNo] = useState(0); // 今日が何回目か - const [round, setRound] = useState(0); // ラウンド(現在の行番号+1) - const [columncnt, setColumncnt] = useState(0); // 現在の列番号 - const [alphabetMatch, setAlphabetMatch] = useState(initAlphabetMatch); // アルファベットの判定リスト + /* State */ + // セーブが必要なState + const [todaysNo, setTodaysNo] = useState(0); // 本日のお題番号 + const [answerList, setAnswerList] = useState(initAnswerList); // 回答欄の文字列 - // 初回レンダリング時 - useEffect(() => { - // 今日の単語を取得 - getTodaysWord(setCorrectAnswer, setTodaysNo); - setRound(round + 1); // ラウンドを1にセット - }, []); + // 上記を元にロードするState + const [matchList, setMatchList] = useState(initMatchList); // 回答欄のマッチ状況(White/Black/Yellow/Grren) + const [gameState, setGameState] = useState("Playing"); // ゲームの状態(Playing/GameClear/GameOver) + const [correctAnswer, setCorrectAnswer] = useState(""); // 今日の単語 + const [round, setRound] = useState(0); // ラウンド(現在の行番号+1) + const [columncnt, setColumncnt] = useState(0); // 現在の列番号 + const [alphabetMatch, setAlphabetMatch] = + useState(initAlphabetMatch); // アルファベットの判定リスト - // Enterを押した際 - useEffect(() => { - if (!judge) return; - if (gameState !== "Playing") return; + // セーブ/ロードが不要なState + const [judge, setJudge] = useState(false); // Enterを押したか - pushedEnterProcess(correctAnswer, answerList, matchList, round, - setMatchList, setAlphabetMatch, setGameState) - .then((isValid) => { - /* 単語が妥当でない場合 */ - if (!isValid) { - alert("データセットに存在しない単語です"); + // 初回レンダリング時 + useEffect(() => { + // 今日の単語を取得 + getTodaysWord(setCorrectAnswer, setTodaysNo); + setRound(round + 1); // ラウンドを1にセット + }, []); - // 回答欄を1行リセット - setAnswerList((prevState) => - prevState.map((row, index) => - index === round - 1 ? Array(5).fill("") : row - ) - ); - setColumncnt(0); // 列番号を0にリセット - } + // Enterを押した際 + useEffect(() => { + if (!judge) return; + if (gameState !== "Playing") return; - /* 問題ない場合 */ - else { - setRound(round + 1); // 次の行へ移行 - setColumncnt(0); // 列番号を0にリセット - } - }); + pushedEnterProcess( + correctAnswer, + answerList, + matchList, + round, + setMatchList, + setAlphabetMatch, + setGameState + ).then((isValid) => { + /* 単語が妥当でない場合 */ + if (!isValid) { + alert("データセットに存在しない単語です"); - setJudge(false); + // 回答欄を1行リセット + setAnswerList((prevState) => + prevState.map((row, index) => + index === round - 1 ? Array(5).fill("") : row + ) + ); + setColumncnt(0); // 列番号を0にリセット + } else { + /* 問題ない場合 */ + setRound(round + 1); // 次の行へ移行 + setColumncnt(0); // 列番号を0にリセット + } + }); + + setJudge(false); }, [judge]); return (
- - - - + + + +
); }; const appStyle: React.CSSProperties = { - margin: "0 auto", - width: "100%", - maxWidth: "600px", // 最大幅を指定する + margin: "0 auto", + width: "100%", + maxWidth: "600px", // 最大幅を指定する }; diff --git a/src/components/keyboard.tsx b/src/components/keyboard.tsx index 3c9f010..3176b47 100644 --- a/src/components/keyboard.tsx +++ b/src/components/keyboard.tsx @@ -1,243 +1,245 @@ import React, { useState, useEffect } from "react"; import { AlphabetMatch } from "../interfaces/AlphabetMatch"; -import { GameState } from "../interfaces/GameState"; +import { GameState } from "../types/GameState"; type appProps = { - round: number; - setRound: React.Dispatch>; - columncnt: number; - setColumncnt: React.Dispatch>; - answerList: string[][]; - setAnswerList: React.Dispatch>; - setJudge: React.Dispatch>; - alphabetMatch: AlphabetMatch; - gameState: GameState; + round: number; + setRound: React.Dispatch>; + columncnt: number; + setColumncnt: React.Dispatch>; + answerList: string[][]; + setAnswerList: React.Dispatch>; + setJudge: React.Dispatch>; + alphabetMatch: AlphabetMatch; + gameState: GameState; }; type Props = { - round: number; - setRound: React.Dispatch>; - columncnt: number; - setColumncnt: React.Dispatch>; - answerList: string[][], - setAnswerList: React.Dispatch>; - keyLayout: string[]; - setJudge: React.Dispatch>; - alphabetMatch: AlphabetMatch; - gameState: GameState; + round: number; + setRound: React.Dispatch>; + columncnt: number; + setColumncnt: React.Dispatch>; + answerList: string[][]; + setAnswerList: React.Dispatch>; + keyLayout: string[]; + setJudge: React.Dispatch>; + alphabetMatch: AlphabetMatch; + gameState: GameState; }; - // アルファベットの判定リストを表す型をAlphabetMatchに変更 const KeyboardRow = (props: Props) => { - const [windowWidth, setWindowWidth] = useState(window.innerWidth); - const updateDimensions = () => { - setWindowWidth(window.innerWidth); - }; - useEffect(() => { - window.addEventListener("resize", updateDimensions); - return () => window.removeEventListener("resize", updateDimensions); - }, []); - - const updateAnswer = ( - prevState: string[][], - letter: string, - row: number, - column: number - ) => { - const tmpList = Array.from(prevState); - tmpList[row][column] = letter; - - return tmpList; - }; - - const handleClick = async ( - event: React.MouseEvent - ) => { - const letter = event.currentTarget.value; - - // ゲームが終了している場合は何もしない - if (props.gameState !== "Playing") return; - - // Enter入力 - if (letter == "Enter") { - // 文字数不足 - if (props.columncnt < 5) { - alert("文字数が足りません"); - } - - // 5文字入力した場合はフラグ送信(判定依頼) - else { - props.setJudge(true); - } - } - - // Delete入力 - else if (letter == "Delete") { - // 1文字以上入力 - if (props.columncnt > 0) { - props.setAnswerList((prevState) => - updateAnswer(prevState, "", props.round-1, props.columncnt - 1) - ); - props.setColumncnt((prev) => prev - 1); - } - } - - // アルファベット入力 - else if (props.columncnt < 5) { - props.setAnswerList((prevState) => - updateAnswer(prevState, letter, props.round-1, props.columncnt) - ); - props.setColumncnt((prev) => prev + 1); - } - }; - - // キーボードのCSSスタイル - const keyboardStyle: React.CSSProperties = { - borderSpacing: windowWidth < 600 ? "3px 3px" : "6px 6px", - display: "flex", - justifyContent: "center", - }; - - // ボタンのCSSスタイル - const buttonStyle: React.CSSProperties = { - backgroundColor: "d9d9d9", - borderRadius: "4px", - border: "none", - width: windowWidth < 600 ? "30px" : "45px", - height: windowWidth < 600 ? "45px" : "60px", - fontSize: windowWidth < 600 ? "10px" : "13px", - fontWeight: "bold", - cursor: "pointer", - }; - - // EnterとDeleteのCSSスタイル - // buttonStyleとの差分のみ記述 - const enterAndDeleteButtonStyle: React.CSSProperties = { - ...buttonStyle, - width: windowWidth < 600 ? "50px" : "70px", - }; - - // AlphabetMatchの結果に基づくスタイル - const matchStyles: Record = { - NoUse: {}, // NoUseはデフォルトスタイルを使用 - Green: { backgroundColor: "538d4e" }, - Yellow: { backgroundColor: "b59f3b" }, - Black: { backgroundColor: "3a3a3c"}, - }; - - // スタイルを決定する関数 - const getButtonStyle = (key: string, matchResult: Record) => { - // EnterとDelete用のスタイル - if (key === "Enter" || key === "Delete") { - return { - ...buttonStyle, - ...enterAndDeleteButtonStyle, - ...matchStyles[matchResult[key]], // matchResultからスタイルを適用 - }; - } - - // 通常キー用のスタイル - return { - ...buttonStyle, - ...matchStyles[matchResult[key]], // matchResultからスタイルを適用 - }; - }; - - return ( - // mapによりキーボードtable作成 - - - - {props.keyLayout.map((key, i) => ( - - ))} - - -
- {/* EnterとDeleteのときのみstyleを変更 */} - -
- ); - }; - - 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", - ]; - - return ( -
- - - -
- ); - }; + const [windowWidth, setWindowWidth] = useState(window.innerWidth); + const updateDimensions = () => { + setWindowWidth(window.innerWidth); + }; + useEffect(() => { + window.addEventListener("resize", updateDimensions); + return () => window.removeEventListener("resize", updateDimensions); + }, []); + + const updateAnswer = ( + prevState: string[][], + letter: string, + row: number, + column: number + ) => { + const tmpList = Array.from(prevState); + tmpList[row][column] = letter; + + return tmpList; + }; + + const handleClick = async ( + event: React.MouseEvent + ) => { + const letter = event.currentTarget.value; + + // ゲームが終了している場合は何もしない + if (props.gameState !== "Playing") return; + + // Enter入力 + if (letter == "Enter") { + // 文字数不足 + if (props.columncnt < 5) { + alert("文字数が足りません"); + } + + // 5文字入力した場合はフラグ送信(判定依頼) + else { + props.setJudge(true); + } + } + + // Delete入力 + else if (letter == "Delete") { + // 1文字以上入力 + if (props.columncnt > 0) { + props.setAnswerList((prevState) => + updateAnswer(prevState, "", props.round - 1, props.columncnt - 1) + ); + props.setColumncnt((prev) => prev - 1); + } + } + + // アルファベット入力 + else if (props.columncnt < 5) { + props.setAnswerList((prevState) => + updateAnswer(prevState, letter, props.round - 1, props.columncnt) + ); + props.setColumncnt((prev) => prev + 1); + } + }; + + // キーボードのCSSスタイル + const keyboardStyle: React.CSSProperties = { + borderSpacing: windowWidth < 600 ? "3px 3px" : "6px 6px", + display: "flex", + justifyContent: "center", + }; + + // ボタンのCSSスタイル + const buttonStyle: React.CSSProperties = { + backgroundColor: "d9d9d9", + borderRadius: "4px", + border: "none", + width: windowWidth < 600 ? "30px" : "45px", + height: windowWidth < 600 ? "45px" : "60px", + fontSize: windowWidth < 600 ? "10px" : "13px", + fontWeight: "bold", + cursor: "pointer", + }; + + // EnterとDeleteのCSSスタイル + // buttonStyleとの差分のみ記述 + const enterAndDeleteButtonStyle: React.CSSProperties = { + ...buttonStyle, + width: windowWidth < 600 ? "50px" : "70px", + }; + + // AlphabetMatchの結果に基づくスタイル + const matchStyles: Record = { + NoUse: {}, // NoUseはデフォルトスタイルを使用 + Green: { backgroundColor: "538d4e" }, + Yellow: { backgroundColor: "b59f3b" }, + Black: { backgroundColor: "3a3a3c" }, + }; + + // スタイルを決定する関数 + const getButtonStyle = (key: string, matchResult: Record) => { + // EnterとDelete用のスタイル + if (key === "Enter" || key === "Delete") { + return { + ...buttonStyle, + ...enterAndDeleteButtonStyle, + ...matchStyles[matchResult[key]], // matchResultからスタイルを適用 + }; + } + + // 通常キー用のスタイル + return { + ...buttonStyle, + ...matchStyles[matchResult[key]], // matchResultからスタイルを適用 + }; + }; + + return ( + // mapによりキーボードtable作成 + + + + {props.keyLayout.map((key, i) => ( + + ))} + + +
+ {/* EnterとDeleteのときのみstyleを変更 */} + +
+ ); +}; + +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", + ]; + + return ( +
+ + + +
+ ); +}; diff --git a/src/game_logics/checkClear.ts b/src/game_logics/checkClear.ts index 7dd328b..7b2fe27 100644 --- a/src/game_logics/checkClear.ts +++ b/src/game_logics/checkClear.ts @@ -1,36 +1,35 @@ -import { GameState } from "../interfaces/GameState"; +import { GameState } from "../types/GameState"; // クリア判定 export const checkClear = ( - correctAnswer: string, - answerList: string[][], - round: number - ): GameState => { + correctAnswer: string, + answerList: string[][], + round: number +): GameState => { + // 正解が空文字だった場合はサーバーエラー + if (correctAnswer === "") { + alert("Server Error: Please reload the page."); + return "Playing"; + } - // 正解が空文字だった場合はサーバーエラー - if (correctAnswer === "") { - alert("Server Error: Please reload the page."); - return "Playing"; - } + // ワードを抽出 + const wordList = []; + for (let j = 0; j < 5; j++) { + wordList.push(answerList[round - 1][j]); + } + const submitWord = wordList.join(""); - // ワードを抽出 - const wordList = []; - for (let j = 0; j < 5; j++) { - wordList.push(answerList[round - 1][j]); - } - const submitWord = wordList.join(""); + // クリア + if (submitWord == correctAnswer) { + alert("clear!!"); + return "GameClear"; + } + // 6回目で不正解だった場合 + else if (round == 6) { + alert(correctAnswer); + return "GameOver"; + } - // クリア - if (submitWord == correctAnswer) { - alert("clear!!"); - return "GameClear"; - } - // 6回目で不正解だった場合 - else if (round == 6) { - alert(correctAnswer); - return "GameOver"; - } - - // ゲーム続行 - return "Playing"; + // ゲーム続行 + return "Playing"; }; diff --git a/src/game_logics/pushedEnterProcess.ts b/src/game_logics/pushedEnterProcess.ts index 7ceb8ba..ca8239f 100644 --- a/src/game_logics/pushedEnterProcess.ts +++ b/src/game_logics/pushedEnterProcess.ts @@ -1,4 +1,4 @@ -import { GameState } from "../interfaces/GameState"; +import { GameState } from "../types/GameState"; import { checkWordValidity } from "./checkWordValidity"; import { checkWordMatch } from "./checkWordMatch"; @@ -6,38 +6,45 @@ import { checkClear } from "./checkClear"; import { AlphabetMatch } from "../interfaces/AlphabetMatch"; -export const pushedEnterProcess = async ( - correctAnswer: string, - answerList: string[][], - matchList: string[][], - round: number, - setMatchList: React.Dispatch>, - setAlphabetMatch: React.Dispatch>, - setGameState: React.Dispatch> - ) : Promise => { - - // 単語の妥当性判定 - const isValid = await checkWordValidity(answerList, round); - if (!isValid) return false; - - // 単語一致判定 - const tmpMatchList = checkWordMatch(correctAnswer, answerList, matchList, round); - // クリア判定 - const _gameState = checkClear(correctAnswer, answerList, round); - setGameState(_gameState); - - // スタイル更新 - setMatchList(tmpMatchList); - - // アルファベットの判定リスト更新 - const newMatch: AlphabetMatch = {}; - for (let i = 0; i < correctAnswer.length; i++) { - newMatch[answerList[round - 1][i]] = tmpMatchList[round - 1][i] as "Green" | "Yellow" | "Black"; - } - setAlphabetMatch((prevMatch: AlphabetMatch) => ({ - ...prevMatch, - ...newMatch - })); - - return true; -} \ No newline at end of file +export const pushedEnterProcess = async ( + correctAnswer: string, + answerList: string[][], + matchList: string[][], + round: number, + setMatchList: React.Dispatch>, + setAlphabetMatch: React.Dispatch>, + setGameState: React.Dispatch> +): Promise => { + // 単語の妥当性判定 + const isValid = await checkWordValidity(answerList, round); + if (!isValid) return false; + + // 単語一致判定 + const tmpMatchList = checkWordMatch( + correctAnswer, + answerList, + matchList, + round + ); + // クリア判定 + const _gameState = checkClear(correctAnswer, answerList, round); + setGameState(_gameState); + + // スタイル更新 + setMatchList(tmpMatchList); + + // アルファベットの判定リスト更新 + const newMatch: AlphabetMatch = {}; + for (let i = 0; i < correctAnswer.length; i++) { + newMatch[answerList[round - 1][i]] = tmpMatchList[round - 1][i] as + | "Green" + | "Yellow" + | "Black"; + } + setAlphabetMatch((prevMatch: AlphabetMatch) => ({ + ...prevMatch, + ...newMatch, + })); + + return true; +}; diff --git a/src/interfaces/GameData.ts b/src/interfaces/GameData.ts new file mode 100644 index 0000000..32ebe97 --- /dev/null +++ b/src/interfaces/GameData.ts @@ -0,0 +1,4 @@ +export interface GameData { + todaysNo: number; + answerList: string[][]; +} \ No newline at end of file diff --git a/src/utils/limitDiaryPlay.ts b/src/load/limitDiaryPlay.ts similarity index 100% rename from src/utils/limitDiaryPlay.ts rename to src/load/limitDiaryPlay.ts diff --git a/src/interfaces/GameState.ts b/src/types/GameState.ts similarity index 100% rename from src/interfaces/GameState.ts rename to src/types/GameState.ts diff --git a/src/utils/saveAndLoadInLocalStorage.ts b/src/utils/saveAndLoadInLocalStorage.ts new file mode 100644 index 0000000..96d0a40 --- /dev/null +++ b/src/utils/saveAndLoadInLocalStorage.ts @@ -0,0 +1,18 @@ +// export const saveGameData = (gameData: ) => { +// // gameDataオブジェクトには、推測した単語や試行回数などが含まれる +// const data = JSON.stringify(gameData); +// localStorage.setItem('wordleGameData', data); +// } + +// export const loadGameData() { +// const data = localStorage.getItem('wordleGameData'); +// if (data) { +// return JSON.parse(data); +// } else { +// return null; // データが存在しない場合はnullを返す +// } +// } + +// function clearGameData() { +// localStorage.removeItem('wordleGameData'); +// } From 818844205cedb619d1fd5552ed76adcb328904cb Mon Sep 17 00:00:00 2001 From: Suke-H Date: Sun, 21 Apr 2024 21:08:21 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20#23=20=E3=82=BB=E3=83=BC=E3=83=96/?= =?UTF-8?q?=E3=83=AD=E3=83=BC=E3=83=89/=E3=83=AA=E3=82=BB=E3=83=83?= =?UTF-8?q?=E3=83=88=E5=AE=9F=E8=A3=85=E6=99=82=E3=81=AB=E3=80=81=E3=82=AD?= =?UTF-8?q?=E3=83=BC=E3=83=9C=E3=83=BC=E3=83=89=E3=81=AE=E5=95=8F=E9=A1=8C?= =?UTF-8?q?=E7=99=BA=E7=94=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1文字入力したら、全ての配列に入力されてしまう --- src/App.tsx | 31 +++++++-- src/game_logics/pushedEnterProcess.ts | 2 +- src/interfaces/GameData.ts | 4 -- src/load/limitDiaryPlay.ts | 18 ----- src/load/saveAndLoad.ts | 96 ++++++++++++++++++++++++++ src/utils/saveAndLoadInLocalStorage.ts | 50 +++++++++----- 6 files changed, 158 insertions(+), 43 deletions(-) delete mode 100644 src/interfaces/GameData.ts delete mode 100644 src/load/limitDiaryPlay.ts create mode 100644 src/load/saveAndLoad.ts diff --git a/src/App.tsx b/src/App.tsx index 819f761..e251dc4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,6 +12,8 @@ import { pushedEnterProcess } from "./game_logics/pushedEnterProcess"; import { getTodaysWord } from "./utils/getTodaysWord"; import { makeGameResultText } from "./utils/makeGameResultText"; +import { saveGameData, loadGameData, LoadDataSetters } from "./load/saveAndLoad"; + export const App = (): JSX.Element => { // (リスト初期化) const initAnswerList: string[][] = new Array(6); @@ -35,27 +37,45 @@ export const App = (): JSX.Element => { const initAlphabetMatch = initializeAlphabetMatch(); /* State */ - // セーブが必要なState + + // APIから取得するデータ const [todaysNo, setTodaysNo] = useState(0); // 本日のお題番号 + const [correctAnswer, setCorrectAnswer] = useState(""); // 今日の単語 + + // セーブが必要なState const [answerList, setAnswerList] = useState(initAnswerList); // 回答欄の文字列 // 上記を元にロードするState const [matchList, setMatchList] = useState(initMatchList); // 回答欄のマッチ状況(White/Black/Yellow/Grren) const [gameState, setGameState] = useState("Playing"); // ゲームの状態(Playing/GameClear/GameOver) - const [correctAnswer, setCorrectAnswer] = useState(""); // 今日の単語 const [round, setRound] = useState(0); // ラウンド(現在の行番号+1) - const [columncnt, setColumncnt] = useState(0); // 現在の列番号 const [alphabetMatch, setAlphabetMatch] = useState(initAlphabetMatch); // アルファベットの判定リスト // セーブ/ロードが不要なState const [judge, setJudge] = useState(false); // Enterを押したか + const [columncnt, setColumncnt] = useState(0); // 現在の列番号 // 初回レンダリング時 useEffect(() => { // 今日の単語を取得 getTodaysWord(setCorrectAnswer, setTodaysNo); - setRound(round + 1); // ラウンドを1にセット + console.log("todaysNo", todaysNo); + + // setRound(round + 1); // ラウンドを1にセット + // ロード処理 + const loadDataSetters: LoadDataSetters = { + setAnswerList: setAnswerList, + setMatchList: setMatchList, + setGameState: setGameState, + setRound: setRound, + setAlphabetMatch: setAlphabetMatch, + } + loadGameData(todaysNo, correctAnswer, loadDataSetters); + + console.table(answerList); + console.table(matchList); + }, []); // Enterを押した際 @@ -88,6 +108,9 @@ export const App = (): JSX.Element => { /* 問題ない場合 */ setRound(round + 1); // 次の行へ移行 setColumncnt(0); // 列番号を0にリセット + + // セーブ処理 + saveGameData(todaysNo, answerList); } }); diff --git a/src/game_logics/pushedEnterProcess.ts b/src/game_logics/pushedEnterProcess.ts index ca8239f..ca18eae 100644 --- a/src/game_logics/pushedEnterProcess.ts +++ b/src/game_logics/pushedEnterProcess.ts @@ -35,7 +35,7 @@ export const pushedEnterProcess = async ( // アルファベットの判定リスト更新 const newMatch: AlphabetMatch = {}; - for (let i = 0; i < correctAnswer.length; i++) { + for (let i = 0; i < 5; i++) { newMatch[answerList[round - 1][i]] = tmpMatchList[round - 1][i] as | "Green" | "Yellow" diff --git a/src/interfaces/GameData.ts b/src/interfaces/GameData.ts deleted file mode 100644 index 32ebe97..0000000 --- a/src/interfaces/GameData.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface GameData { - todaysNo: number; - answerList: string[][]; -} \ No newline at end of file diff --git a/src/load/limitDiaryPlay.ts b/src/load/limitDiaryPlay.ts deleted file mode 100644 index 96d0a40..0000000 --- a/src/load/limitDiaryPlay.ts +++ /dev/null @@ -1,18 +0,0 @@ -// export const saveGameData = (gameData: ) => { -// // gameDataオブジェクトには、推測した単語や試行回数などが含まれる -// const data = JSON.stringify(gameData); -// localStorage.setItem('wordleGameData', data); -// } - -// export const loadGameData() { -// const data = localStorage.getItem('wordleGameData'); -// if (data) { -// return JSON.parse(data); -// } else { -// return null; // データが存在しない場合はnullを返す -// } -// } - -// function clearGameData() { -// localStorage.removeItem('wordleGameData'); -// } diff --git a/src/load/saveAndLoad.ts b/src/load/saveAndLoad.ts new file mode 100644 index 0000000..77af13d --- /dev/null +++ b/src/load/saveAndLoad.ts @@ -0,0 +1,96 @@ +import { saveGameDataInLocal, loadGameDataInLocal, resetGameDataInLocal } from "../utils/saveAndLoadInLocalStorage"; +import { checkWordMatch } from "../game_logics/checkWordMatch"; +import { checkClear } from "../game_logics/checkClear"; +import { AlphabetMatch } from "../interfaces/AlphabetMatch"; +import { GameState } from "../types/GameState"; + +export interface LoadDataSetters { + setAnswerList: React.Dispatch>; + setMatchList: React.Dispatch>; + setGameState: React.Dispatch>; + setRound: React.Dispatch>; + setAlphabetMatch: React.Dispatch>; +} + +export const saveGameData = (todaysNo: number, answerList: string[][]) => { + saveGameDataInLocal(todaysNo, answerList); +} + +export const loadGameData = (todaysNo: number, correctAnswer: string, loadDataSetters: LoadDataSetters) => { + // ローカルストレージからロード + const answerList = loadGameDataInLocal(todaysNo); + + // ロードしたデータをセット + loadDataSetters.setAnswerList(answerList); + + // ラウンド数をセット + // answerListの中で空文字列がある場合はその行番号をセット + const index = answerList.findIndex((row) => row.includes("")) + 1; + // 空文字列がない(indexが0)場合は6をセット + const round = index !== 0 ? index : 6; + + loadDataSetters.setRound(round); + + // ラウンドが1だった場合 + if (round === 1) { + // ローカルストレージをクリアして終了 + resetGameData(); + return; + } + + // ゲームの状態をセット + loadDataSetters.setGameState(checkClear(correctAnswer, answerList, round)); + + // マッチリストをセット + const matchList = calcMatchList(answerList, correctAnswer, round) + loadDataSetters.setMatchList(matchList); + + // 使用したアルファベットをセット + loadDataSetters.setAlphabetMatch(calcAlphabetMatch(answerList, matchList, round)); +} + +const resetGameData = () => { + resetGameDataInLocal(); +} + +const calcMatchList = (answerList: string[][], correctAnswer: string, round: number): string[][] => { + // 空配列を作成 + let matchList: string[][] = new Array(6); + for (let i = 0; i < 6; i++) { + matchList[i] = new Array(5).fill("White"); + } + // 1文字ずつ判定 + for (let i = 1; i <= round; i++) { + matchList = checkWordMatch(correctAnswer, answerList, matchList, i); + } + + return matchList; +} + +// 全アルファベットを'NoUse'で初期化する関数 +const initializeAlphabetMatch = (): AlphabetMatch => { + const result: AlphabetMatch = {}; + + for (let charCode = 65; charCode <= 90; charCode++) { + const letter = String.fromCharCode(charCode); + result[letter] = "NoUse"; + } + return result; +} + +const calcAlphabetMatch = (answerList: string[][], matchList: string[][], round: number): AlphabetMatch => { + const newMatch = initializeAlphabetMatch(); + for (let i = 0; i < round; i++) { + for (let j = 0; j < 5; j++) { + newMatch[answerList[i][j]] = matchList[i][j] as + | "Green" + | "Yellow" + | "Black"; + } + } + + return newMatch; +} + + + diff --git a/src/utils/saveAndLoadInLocalStorage.ts b/src/utils/saveAndLoadInLocalStorage.ts index 96d0a40..0f5b8bf 100644 --- a/src/utils/saveAndLoadInLocalStorage.ts +++ b/src/utils/saveAndLoadInLocalStorage.ts @@ -1,18 +1,36 @@ -// export const saveGameData = (gameData: ) => { -// // gameDataオブジェクトには、推測した単語や試行回数などが含まれる -// const data = JSON.stringify(gameData); -// localStorage.setItem('wordleGameData', data); -// } +export const saveGameDataInLocal = (todaysNo: number, answerList: string[][]) => { + const gameData = { + todaysNo: todaysNo.toString(), + answerList: answerList + }; -// export const loadGameData() { -// const data = localStorage.getItem('wordleGameData'); -// if (data) { -// return JSON.parse(data); -// } else { -// return null; // データが存在しない場合はnullを返す -// } -// } + console.log("save!!", gameData); -// function clearGameData() { -// localStorage.removeItem('wordleGameData'); -// } + localStorage.setItem('gameData', JSON.stringify(gameData)); + } + + export const loadGameDataInLocal = (todaysNo: number): string[][] => { + const json_data = localStorage.getItem('gameData'); + + // ローカルストレージにデータがある場合 + if (json_data) { + const data = JSON.parse(json_data); + + // 本日のお題番号が一致している場合 + if (data.todaysNo === todaysNo.toString()) { + console.log("load!!", data); + return data.answerList; + } + } + + // ローカルストレージにデータがない場合 | 本日のお題番号が一致していない場合 + console.log("load failed..."); + const initAnswerList = new Array(6).fill(new Array(5).fill("")); + return initAnswerList; + } + + export const resetGameDataInLocal = () => { + console.log("reset!!"); + localStorage.removeItem('gameData'); + } + \ No newline at end of file From c8182b111f2e91be516037ff523255d629f92e39 Mon Sep 17 00:00:00 2001 From: Suke-H Date: Sun, 21 Apr 2024 23:59:44 +0900 Subject: [PATCH 5/7] =?UTF-8?q?fix:=20#23=20=E9=85=8D=E5=88=97=E3=82=B7?= =?UTF-8?q?=E3=83=A3=E3=83=AD=E3=83=BC=E3=82=B3=E3=83=94=E3=83=BC=E3=81=AE?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E3=81=8A=E3=82=88=E3=81=B3await/async?= =?UTF-8?q?=E3=81=AB=E3=82=88=E3=82=8B=E5=90=8C=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - answerListの各行がシャローコピーになっていたので修正 - お題と番号(todaysNo)を取得する処理が待機できてなく、todaysNoが0固定になっていたため、async/awaitで待機するように修正 --- src/App.tsx | 294 +++++++++++++------------ src/load/saveAndLoad.ts | 10 +- src/utils/saveAndLoadInLocalStorage.ts | 73 +++--- 3 files changed, 194 insertions(+), 183 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index e251dc4..2e7bc0b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,144 +1,150 @@ -import { useState, useEffect } from "react"; - -import { Answer } from "./components/answer"; -import { Keyboard } from "./components/keyboard"; -import { Notes } from "./components/notes"; -import { ShareResultButton } from "./components/ShareResultButton"; - -import { AlphabetMatch } from "./interfaces/AlphabetMatch"; -import { GameState } from "./types/GameState"; - -import { pushedEnterProcess } from "./game_logics/pushedEnterProcess"; -import { getTodaysWord } from "./utils/getTodaysWord"; -import { makeGameResultText } from "./utils/makeGameResultText"; - -import { saveGameData, loadGameData, LoadDataSetters } from "./load/saveAndLoad"; - -export const App = (): JSX.Element => { - // (リスト初期化) - const initAnswerList: string[][] = new Array(6); - for (let i = 0; i < 6; i++) { - initAnswerList[i] = new Array(5).fill(""); - } - const initMatchList: string[][] = new Array(6); - for (let i = 0; i < 6; i++) { - initMatchList[i] = new Array(5).fill("White"); - } - // 全アルファベットを'NoUse'で初期化する関数 - const initializeAlphabetMatch = (): AlphabetMatch => { - const result: AlphabetMatch = {}; - for (let charCode = 65; charCode <= 90; charCode++) { - const letter = String.fromCharCode(charCode); - result[letter] = "NoUse"; - } - return result; - }; - // アルファベットの判定リストを初期化 - const initAlphabetMatch = initializeAlphabetMatch(); - - /* State */ - - // APIから取得するデータ - const [todaysNo, setTodaysNo] = useState(0); // 本日のお題番号 - const [correctAnswer, setCorrectAnswer] = useState(""); // 今日の単語 - - // セーブが必要なState - const [answerList, setAnswerList] = useState(initAnswerList); // 回答欄の文字列 - - // 上記を元にロードするState - const [matchList, setMatchList] = useState(initMatchList); // 回答欄のマッチ状況(White/Black/Yellow/Grren) - const [gameState, setGameState] = useState("Playing"); // ゲームの状態(Playing/GameClear/GameOver) - const [round, setRound] = useState(0); // ラウンド(現在の行番号+1) - const [alphabetMatch, setAlphabetMatch] = - useState(initAlphabetMatch); // アルファベットの判定リスト - - // セーブ/ロードが不要なState - const [judge, setJudge] = useState(false); // Enterを押したか - const [columncnt, setColumncnt] = useState(0); // 現在の列番号 - - // 初回レンダリング時 - useEffect(() => { - // 今日の単語を取得 - getTodaysWord(setCorrectAnswer, setTodaysNo); - console.log("todaysNo", todaysNo); - - // setRound(round + 1); // ラウンドを1にセット - // ロード処理 - const loadDataSetters: LoadDataSetters = { - setAnswerList: setAnswerList, - setMatchList: setMatchList, - setGameState: setGameState, - setRound: setRound, - setAlphabetMatch: setAlphabetMatch, - } - loadGameData(todaysNo, correctAnswer, loadDataSetters); - - console.table(answerList); - console.table(matchList); - - }, []); - - // Enterを押した際 - useEffect(() => { - if (!judge) return; - if (gameState !== "Playing") return; - - pushedEnterProcess( - correctAnswer, - answerList, - matchList, - round, - setMatchList, - setAlphabetMatch, - setGameState - ).then((isValid) => { - /* 単語が妥当でない場合 */ - if (!isValid) { - alert("データセットに存在しない単語です"); - - // 回答欄を1行リセット - setAnswerList((prevState) => - prevState.map((row, index) => - index === round - 1 ? Array(5).fill("") : row - ) - ); - setColumncnt(0); // 列番号を0にリセット - } else { - - /* 問題ない場合 */ - setRound(round + 1); // 次の行へ移行 - setColumncnt(0); // 列番号を0にリセット - - // セーブ処理 - saveGameData(todaysNo, answerList); - } - }); - - setJudge(false); - }, [judge]); - - return ( -
- - - - -
- ); -}; - -const appStyle: React.CSSProperties = { - margin: "0 auto", - width: "100%", - maxWidth: "600px", // 最大幅を指定する -}; +import { useState, useEffect } from "react"; + +import { Answer } from "./components/answer"; +import { Keyboard } from "./components/keyboard"; +import { Notes } from "./components/notes"; +import { ShareResultButton } from "./components/ShareResultButton"; + +import { AlphabetMatch } from "./interfaces/AlphabetMatch"; +import { GameState } from "./types/GameState"; + +import { pushedEnterProcess } from "./game_logics/pushedEnterProcess"; +import { getTodaysWord } from "./utils/getTodaysWord"; +import { makeGameResultText } from "./utils/makeGameResultText"; + +import { saveGameData, loadGameData, LoadDataSetters } from "./load/saveAndLoad"; + +export const App = (): JSX.Element => { + // (リスト初期化) + const initAnswerList: string[][] = new Array(6); + for (let i = 0; i < 6; i++) { + initAnswerList[i] = new Array(5).fill(""); + } + const initMatchList: string[][] = new Array(6); + for (let i = 0; i < 6; i++) { + initMatchList[i] = new Array(5).fill("White"); + } + // 全アルファベットを'NoUse'で初期化する関数 + const initializeAlphabetMatch = (): AlphabetMatch => { + const result: AlphabetMatch = {}; + for (let charCode = 65; charCode <= 90; charCode++) { + const letter = String.fromCharCode(charCode); + result[letter] = "NoUse"; + } + return result; + }; + // アルファベットの判定リストを初期化 + const initAlphabetMatch = initializeAlphabetMatch(); + + /* State */ + + // APIから取得するデータ + const [todaysNo, setTodaysNo] = useState(0); // 本日のお題番号 + const [correctAnswer, setCorrectAnswer] = useState(""); // 今日の単語 + + // セーブが必要なState + const [answerList, setAnswerList] = useState(initAnswerList); // 回答欄の文字列 + + // 上記を元にロードするState + const [matchList, setMatchList] = useState(initMatchList); // 回答欄のマッチ状況(White/Black/Yellow/Grren) + const [gameState, setGameState] = useState("Playing"); // ゲームの状態(Playing/GameClear/GameOver) + const [round, setRound] = useState(0); // ラウンド(現在の行番号+1) + const [alphabetMatch, setAlphabetMatch] = + useState(initAlphabetMatch); // アルファベットの判定リスト + + // セーブ/ロードが不要なState + const [judge, setJudge] = useState(false); // Enterを押したか + const [columncnt, setColumncnt] = useState(0); // 現在の列番号 + + // 初回レンダリング時 + useEffect(() => { + const fetchData = async () => { + await getTodaysWord(setCorrectAnswer, setTodaysNo); + }; + + fetchData(); + }, []); // コンポーネントがマウントされたときに一度だけ実行される + + useEffect(() => { + // todaysNoが更新されたときに実行する処理 + if (todaysNo === 0) return; // 初期値の場合は処理をスキップ + + console.log("todaysNo updated", todaysNo); // 更新された値でログ出力 + + const loadDataSetters: LoadDataSetters = { + setAnswerList: setAnswerList, + setMatchList: setMatchList, + setGameState: setGameState, + setRound: setRound, + setAlphabetMatch: setAlphabetMatch, + }; + + // todaysNo と correctAnswer が更新されていることを前提にロード処理を呼び出し + loadGameData(todaysNo, correctAnswer, loadDataSetters); + + }, [todaysNo]); // todaysNoが変更されたときに実行される + + // Enterを押した際 + useEffect(() => { + if (!judge) return; + if (gameState !== "Playing") return; + + pushedEnterProcess( + correctAnswer, + answerList, + matchList, + round, + setMatchList, + setAlphabetMatch, + setGameState + ).then((isValid) => { + /* 単語が妥当でない場合 */ + if (!isValid) { + alert("データセットに存在しない単語です"); + + // 回答欄を1行リセット + setAnswerList((prevState) => + prevState.map((row, index) => + index === round - 1 ? Array(5).fill("") : row + ) + ); + setColumncnt(0); // 列番号を0にリセット + } else { + + /* 問題ない場合 */ + setRound(round + 1); // 次の行へ移行 + setColumncnt(0); // 列番号を0にリセット + + // セーブ処理 + saveGameData(todaysNo, answerList); + } + }); + + setJudge(false); + }, [judge]); + + return ( +
+ + + + +
+ ); +}; + +const appStyle: React.CSSProperties = { + margin: "0 auto", + width: "100%", + maxWidth: "600px", // 最大幅を指定する +}; diff --git a/src/load/saveAndLoad.ts b/src/load/saveAndLoad.ts index 77af13d..6bc604c 100644 --- a/src/load/saveAndLoad.ts +++ b/src/load/saveAndLoad.ts @@ -19,17 +19,19 @@ export const saveGameData = (todaysNo: number, answerList: string[][]) => { export const loadGameData = (todaysNo: number, correctAnswer: string, loadDataSetters: LoadDataSetters) => { // ローカルストレージからロード const answerList = loadGameDataInLocal(todaysNo); + // console.table(answerList); // ロードしたデータをセット loadDataSetters.setAnswerList(answerList); // ラウンド数をセット // answerListの中で空文字列がある場合はその行番号をセット - const index = answerList.findIndex((row) => row.includes("")) + 1; - // 空文字列がない(indexが0)場合は6をセット - const round = index !== 0 ? index : 6; + const index = answerList.findIndex((row) => row.includes("")); + // 空文字列がない(indexが-1)場合は5をセット + const round = index !== -1 ? index : 5; + // console.log("round", round, "index", index); - loadDataSetters.setRound(round); + loadDataSetters.setRound(round+1); // ラウンドが1だった場合 if (round === 1) { diff --git a/src/utils/saveAndLoadInLocalStorage.ts b/src/utils/saveAndLoadInLocalStorage.ts index 0f5b8bf..0a2b624 100644 --- a/src/utils/saveAndLoadInLocalStorage.ts +++ b/src/utils/saveAndLoadInLocalStorage.ts @@ -1,36 +1,39 @@ -export const saveGameDataInLocal = (todaysNo: number, answerList: string[][]) => { - const gameData = { - todaysNo: todaysNo.toString(), - answerList: answerList - }; - - console.log("save!!", gameData); - - localStorage.setItem('gameData', JSON.stringify(gameData)); - } - - export const loadGameDataInLocal = (todaysNo: number): string[][] => { - const json_data = localStorage.getItem('gameData'); - - // ローカルストレージにデータがある場合 - if (json_data) { - const data = JSON.parse(json_data); - - // 本日のお題番号が一致している場合 - if (data.todaysNo === todaysNo.toString()) { - console.log("load!!", data); - return data.answerList; - } - } - - // ローカルストレージにデータがない場合 | 本日のお題番号が一致していない場合 - console.log("load failed..."); - const initAnswerList = new Array(6).fill(new Array(5).fill("")); - return initAnswerList; - } - - export const resetGameDataInLocal = () => { - console.log("reset!!"); - localStorage.removeItem('gameData'); - } +export const saveGameDataInLocal = (todaysNo: number, answerList: string[][]) => { + const gameData = { + todaysNo: todaysNo.toString(), + answerList: answerList + }; + + console.log("save!!", gameData); + + localStorage.setItem('gameData', JSON.stringify(gameData)); + } + + export const loadGameDataInLocal = (todaysNo: number): string[][] => { + const json_data = localStorage.getItem('gameData'); + + // ローカルストレージにデータがある場合 + if (json_data) { + const data = JSON.parse(json_data); + + // 本日のお題番号が一致している場合 + if (data.todaysNo === todaysNo.toString()) { + console.log("load!!", data); + return data.answerList; + } + } + + // ローカルストレージにデータがない場合 | 本日のお題番号が一致していない場合 + console.log("load failed..."); + const initAnswerList: string[][] = new Array(6); + for (let i = 0; i < 6; i++) { + initAnswerList[i] = new Array(5).fill(""); + } + return initAnswerList; + } + + export const resetGameDataInLocal = () => { + console.log("reset!!"); + localStorage.removeItem('gameData'); + } \ No newline at end of file From 7c244410f7144b36254eafb5348fee20a951a793 Mon Sep 17 00:00:00 2001 From: Suke-H Date: Mon, 22 Apr 2024 00:03:35 +0900 Subject: [PATCH 6/7] =?UTF-8?q?fix:=20#23=20=E6=AC=A1=E3=81=AE=E6=97=A5?= =?UTF-8?q?=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=9F=E9=9A=9B=E3=81=AE=E3=82=A8?= =?UTF-8?q?=E3=83=A9=E3=83=BC=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/load/saveAndLoad.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/load/saveAndLoad.ts b/src/load/saveAndLoad.ts index 6bc604c..05f713e 100644 --- a/src/load/saveAndLoad.ts +++ b/src/load/saveAndLoad.ts @@ -33,8 +33,8 @@ export const loadGameData = (todaysNo: number, correctAnswer: string, loadDataSe loadDataSetters.setRound(round+1); - // ラウンドが1だった場合 - if (round === 1) { + // ラウンドが0だった場合 + if (round === 0) { // ローカルストレージをクリアして終了 resetGameData(); return; From 0ac11f130d2a28ad9dded4c0707472fba8cce210 Mon Sep 17 00:00:00 2001 From: Suke-H Date: Mon, 22 Apr 2024 00:17:08 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20#23=20=E3=83=AD=E3=83=BC=E3=83=89?= =?UTF-8?q?=E6=99=82=E3=81=AB=E3=82=AD=E3=83=BC=E3=83=9C=E3=83=BC=E3=83=89?= =?UTF-8?q?=E5=85=A5=E5=8A=9B=E3=82=92=E6=8B=92=E5=90=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 12 ++++++++++-- src/components/keyboard.tsx | 8 ++++++++ src/game_logics/checkWordValidity.ts | 23 +++++++++++------------ src/utils/saveAndLoadInLocalStorage.ts | 5 ----- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 2e7bc0b..797c2a6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,6 +14,9 @@ import { makeGameResultText } from "./utils/makeGameResultText"; import { saveGameData, loadGameData, LoadDataSetters } from "./load/saveAndLoad"; +// debug +// import { resetGameDataInLocal } from "./utils/saveAndLoadInLocalStorage"; + export const App = (): JSX.Element => { // (リスト初期化) const initAnswerList: string[][] = new Array(6); @@ -55,6 +58,7 @@ export const App = (): JSX.Element => { // セーブ/ロードが不要なState const [judge, setJudge] = useState(false); // Enterを押したか const [columncnt, setColumncnt] = useState(0); // 現在の列番号 + const [isLoadFinished, setIsLoadFinished] = useState(false); // ロードが完了したか // 初回レンダリング時 useEffect(() => { @@ -68,8 +72,9 @@ export const App = (): JSX.Element => { useEffect(() => { // todaysNoが更新されたときに実行する処理 if (todaysNo === 0) return; // 初期値の場合は処理をスキップ - - console.log("todaysNo updated", todaysNo); // 更新された値でログ出力 + + // debug + // resetGameDataInLocal(); const loadDataSetters: LoadDataSetters = { setAnswerList: setAnswerList, @@ -81,6 +86,8 @@ export const App = (): JSX.Element => { // todaysNo と correctAnswer が更新されていることを前提にロード処理を呼び出し loadGameData(todaysNo, correctAnswer, loadDataSetters); + // ロード完了 + setIsLoadFinished(true); }, [todaysNo]); // todaysNoが変更されたときに実行される @@ -136,6 +143,7 @@ export const App = (): JSX.Element => { setJudge={setJudge} alphabetMatch={alphabetMatch} gameState={gameState} + isLoadFinished={isLoadFinished} /> diff --git a/src/components/keyboard.tsx b/src/components/keyboard.tsx index 3176b47..d9a90a0 100644 --- a/src/components/keyboard.tsx +++ b/src/components/keyboard.tsx @@ -12,6 +12,7 @@ type appProps = { setJudge: React.Dispatch>; alphabetMatch: AlphabetMatch; gameState: GameState; + isLoadFinished: boolean; }; type Props = { @@ -25,6 +26,7 @@ type Props = { setJudge: React.Dispatch>; alphabetMatch: AlphabetMatch; gameState: GameState; + isLoadFinished: boolean; }; // アルファベットの判定リストを表す型をAlphabetMatchに変更 @@ -58,6 +60,9 @@ const KeyboardRow = (props: Props) => { // ゲームが終了している場合は何もしない if (props.gameState !== "Playing") return; + // ロードが完了していない場合は何もしない + if (!props.isLoadFinished) return; + // Enter入力 if (letter == "Enter") { // 文字数不足 @@ -215,6 +220,7 @@ export const Keyboard = (props: appProps) => { setJudge={props.setJudge} alphabetMatch={props.alphabetMatch} gameState={props.gameState} + isLoadFinished={props.isLoadFinished} /> { setJudge={props.setJudge} alphabetMatch={props.alphabetMatch} gameState={props.gameState} + isLoadFinished={props.isLoadFinished} /> { setJudge={props.setJudge} alphabetMatch={props.alphabetMatch} gameState={props.gameState} + isLoadFinished={props.isLoadFinished} /> ); diff --git a/src/game_logics/checkWordValidity.ts b/src/game_logics/checkWordValidity.ts index e929ca5..54342f1 100644 --- a/src/game_logics/checkWordValidity.ts +++ b/src/game_logics/checkWordValidity.ts @@ -1,13 +1,12 @@ -import axios from "axios"; - -// 単語の妥当性判定 -export const checkWordValidity = async ( answerList: string[][], round: number ) : Promise => { - const { data } = await axios.post('https://yan5p8s0dg.execute-api.ap-southeast-2.amazonaws.com/WORDLE', - {"word": answerList[round - 1].join("")},); - console.log(data); - if (data.isValid === undefined) { - return false; - } - - return data.isValid; +import axios from "axios"; + +// 単語の妥当性判定 +export const checkWordValidity = async ( answerList: string[][], round: number ) : Promise => { + const { data } = await axios.post('https://yan5p8s0dg.execute-api.ap-southeast-2.amazonaws.com/WORDLE', + {"word": answerList[round - 1].join("")},); + if (data.isValid === undefined) { + return false; + } + + return data.isValid; } \ No newline at end of file diff --git a/src/utils/saveAndLoadInLocalStorage.ts b/src/utils/saveAndLoadInLocalStorage.ts index 0a2b624..3cc6bd5 100644 --- a/src/utils/saveAndLoadInLocalStorage.ts +++ b/src/utils/saveAndLoadInLocalStorage.ts @@ -4,8 +4,6 @@ export const saveGameDataInLocal = (todaysNo: number, answerList: string[][]) => answerList: answerList }; - console.log("save!!", gameData); - localStorage.setItem('gameData', JSON.stringify(gameData)); } @@ -18,13 +16,11 @@ export const saveGameDataInLocal = (todaysNo: number, answerList: string[][]) => // 本日のお題番号が一致している場合 if (data.todaysNo === todaysNo.toString()) { - console.log("load!!", data); return data.answerList; } } // ローカルストレージにデータがない場合 | 本日のお題番号が一致していない場合 - console.log("load failed..."); const initAnswerList: string[][] = new Array(6); for (let i = 0; i < 6; i++) { initAnswerList[i] = new Array(5).fill(""); @@ -33,7 +29,6 @@ export const saveGameDataInLocal = (todaysNo: number, answerList: string[][]) => } export const resetGameDataInLocal = () => { - console.log("reset!!"); localStorage.removeItem('gameData'); } \ No newline at end of file