diff --git a/src/App.tsx b/src/App.tsx index 1752108..797c2a6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,111 +1,158 @@ -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 { 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(); - - /* State */ - const [answerList, setAnswerList] = useState(initAnswerList); // 回答欄の文字列 - const [ matchList, setMatchList ] = useState(initMatchList); // 回答欄のマッチ状況(White/Black/Yellow/Grren) - 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); // アルファベットの判定リスト - - // 初回レンダリング時 - useEffect(() => { - // 今日の単語を取得 - getTodaysWord(setCorrectAnswer, setTodaysNo); - setRound(round + 1); // ラウンドを1にセット - }, []); - - // Enterを押した際 - useEffect(() => { - if (!judge) return; - - pushedEnterProcess(correctAnswer, answerList, matchList, round, setMatchList, setAlphabetMatch) - .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にリセット - } - }); - - 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"; + +// debug +// import { resetGameDataInLocal } from "./utils/saveAndLoadInLocalStorage"; + +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); // 現在の列番号 + const [isLoadFinished, setIsLoadFinished] = useState(false); // ロードが完了したか + + // 初回レンダリング時 + useEffect(() => { + const fetchData = async () => { + await getTodaysWord(setCorrectAnswer, setTodaysNo); + }; + + fetchData(); + }, []); // コンポーネントがマウントされたときに一度だけ実行される + + useEffect(() => { + // todaysNoが更新されたときに実行する処理 + if (todaysNo === 0) return; // 初期値の場合は処理をスキップ + + // debug + // resetGameDataInLocal(); + + const loadDataSetters: LoadDataSetters = { + setAnswerList: setAnswerList, + setMatchList: setMatchList, + setGameState: setGameState, + setRound: setRound, + setAlphabetMatch: setAlphabetMatch, + }; + + // todaysNo と correctAnswer が更新されていることを前提にロード処理を呼び出し + loadGameData(todaysNo, correctAnswer, loadDataSetters); + // ロード完了 + setIsLoadFinished(true); + + }, [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/components/keyboard.tsx b/src/components/keyboard.tsx index 9135218..d9a90a0 100644 --- a/src/components/keyboard.tsx +++ b/src/components/keyboard.tsx @@ -1,236 +1,253 @@ import React, { useState, useEffect } from "react"; import { AlphabetMatch } from "../interfaces/AlphabetMatch"; +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; + round: number; + setRound: React.Dispatch>; + columncnt: number; + setColumncnt: React.Dispatch>; + answerList: string[][]; + setAnswerList: React.Dispatch>; + setJudge: React.Dispatch>; + alphabetMatch: AlphabetMatch; + gameState: GameState; + isLoadFinished: boolean; }; type Props = { - round: number; - setRound: React.Dispatch>; - columncnt: number; - setColumncnt: React.Dispatch>; - answerList: string[][], - setAnswerList: React.Dispatch>; - keyLayout: string[]; - setJudge: React.Dispatch>; - alphabetMatch: AlphabetMatch; + round: number; + setRound: React.Dispatch>; + columncnt: number; + setColumncnt: React.Dispatch>; + answerList: string[][]; + setAnswerList: React.Dispatch>; + keyLayout: string[]; + setJudge: React.Dispatch>; + alphabetMatch: AlphabetMatch; + gameState: GameState; + isLoadFinished: boolean; }; - // アルファベットの判定リストを表す型を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; - - // 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; + + // ロードが完了していない場合は何もしない + if (!props.isLoadFinished) 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 ea500ca..7b2fe27 100644 --- a/src/game_logics/checkClear.ts +++ b/src/game_logics/checkClear.ts @@ -1,28 +1,35 @@ +import { GameState } from "../types/GameState"; + // クリア判定 export const checkClear = ( - correctAnswer: string, - answerList: string[][], - round: number - ) => { - // 正解が空文字だった場合はサーバーエラー - if (correctAnswer === "") { - alert("Server Error: Please reload the page."); - return; - } + correctAnswer: string, + answerList: string[][], + round: number +): GameState => { + // 正解が空文字だった場合はサーバーエラー + 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!!"); - } - // 6回目で不正解だった場合 - else if (round == 6) { - alert(correctAnswer); - } + // ゲーム続行 + return "Playing"; }; 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/game_logics/pushedEnterProcess.ts b/src/game_logics/pushedEnterProcess.ts index 1232333..ca18eae 100644 --- a/src/game_logics/pushedEnterProcess.ts +++ b/src/game_logics/pushedEnterProcess.ts @@ -1,38 +1,50 @@ +import { GameState } from "../types/GameState"; + import { checkWordValidity } from "./checkWordValidity"; import { checkWordMatch } from "./checkWordMatch"; 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> - ) : Promise => { +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 isValid = await checkWordValidity(answerList, round); - if (!isValid) return false; + // 単語一致判定 + const tmpMatchList = checkWordMatch( + correctAnswer, + answerList, + matchList, + round + ); + // クリア判定 + const _gameState = checkClear(correctAnswer, answerList, round); + setGameState(_gameState); - // 単語一致判定 - const tmpMatchList = checkWordMatch(correctAnswer, answerList, matchList, round); - // クリア判定 - checkClear(correctAnswer, answerList, round); - // スタイル更新 - setMatchList(tmpMatchList); + // スタイル更新 + 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 - })); + // アルファベットの判定リスト更新 + const newMatch: AlphabetMatch = {}; + for (let i = 0; i < 5; 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 + return true; +}; diff --git a/src/load/saveAndLoad.ts b/src/load/saveAndLoad.ts new file mode 100644 index 0000000..05f713e --- /dev/null +++ b/src/load/saveAndLoad.ts @@ -0,0 +1,98 @@ +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); + // console.table(answerList); + + // ロードしたデータをセット + loadDataSetters.setAnswerList(answerList); + + // ラウンド数をセット + // answerListの中で空文字列がある場合はその行番号をセット + 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+1); + + // ラウンドが0だった場合 + if (round === 0) { + // ローカルストレージをクリアして終了 + 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/types/GameState.ts b/src/types/GameState.ts new file mode 100644 index 0000000..261f4d8 --- /dev/null +++ b/src/types/GameState.ts @@ -0,0 +1,2 @@ +// ゲームの状態 +export type GameState = 'Playing' | 'GameClear' | 'GameOver'; \ No newline at end of file diff --git a/src/utils/saveAndLoadInLocalStorage.ts b/src/utils/saveAndLoadInLocalStorage.ts new file mode 100644 index 0000000..3cc6bd5 --- /dev/null +++ b/src/utils/saveAndLoadInLocalStorage.ts @@ -0,0 +1,34 @@ +export const saveGameDataInLocal = (todaysNo: number, answerList: string[][]) => { + const gameData = { + todaysNo: todaysNo.toString(), + answerList: answerList + }; + + 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()) { + return data.answerList; + } + } + + // ローカルストレージにデータがない場合 | 本日のお題番号が一致していない場合 + const initAnswerList: string[][] = new Array(6); + for (let i = 0; i < 6; i++) { + initAnswerList[i] = new Array(5).fill(""); + } + return initAnswerList; + } + + export const resetGameDataInLocal = () => { + localStorage.removeItem('gameData'); + } + \ No newline at end of file