こんにちは、とまだです。
みなさん、頭の回転は早い方ですか?
私は30代前半ですが、数秒前にやろうとしていたことを忘れることがあり、さすがに不安になってきました。
そんなときには、脳トレが効果的らしいですね!!!!
そこで今回は、ReactとTypeScriptを使って「じゃんけん脳トレアプリ」を作るチュートリアルをご紹介します。
作るアプリのイメージ
簡単にいうと「指示通りの手を出すじゃんけん」です。
このアプリでは指示に従って勝つ、負ける、引き分けるの3つのパターンがあります。
普通、じゃんけんと言えば常に勝つことを目指しますが、その常識・ルールを覆すような指示が出されるので、脳トレになるという寸法です。
(よくありますよね)
また、正解数と不正解数が表示されるので、自分の脳トレの成果を確認できます。
では、早速作り方を見ていきましょう!
対象となる読者
- ReactとTypeScriptを使ってアプリを作ってみたい初心者
- React に興味がある方
- TypeScript に興味がある方
1. プロジェクトの作成
まずは、新しいReactプロジェクトを作成しましょう。今回は、高速な開発環境を提供する「Vite」というツールを使います。
ターミナル(コマンドプロンプトやPowerShell)を開いて、以下のコマンドを実行してください。
npm create vite@latest janken-brain-training -- --template react-ts
このコマンドは、「janken-brain-training」という名前のプロジェクトを作成します。また、ReactとTypeScriptを使用するテンプレートを選択しています。
コマンドを実行すると、新しいフォルダが作成されます。そのフォルダに移動しましょう。
cd janken-brain-training
2. 依存関係のインストール
プロジェクトのフォルダに移動したら、必要なパッケージをインストールします。以下のコマンドを実行してください。
npm install
次に、スタイリングを簡単に行えるTailwind CSSをプロジェクトに追加します。
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
3. Tailwind CSSの設定
Tailwind CSSを使用するための設定ファイルを作成します。以下のコマンドを実行してください。
npx tailwindcss init -p
次に、tailwind.config.js
ファイルを開いて、以下のように編集します。
module.exports = {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
最後に、src/index.css
ファイルを開いて、以下の内容で置き換えます。
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
これで、Tailwind CSSの設定が完了しました!
4. 型定義の作成
TypeScriptの魅力の1つは、型定義によってコードの安全性を高められることです。じゃんけん脳トレアプリで使用する型を定義しましょう。
src/types.ts
ファイルを作成し、以下の内容を記述します。
// じゃんけんの手を表す型
export type Hand = 'rock' | 'paper' | 'scissors';
// プレイヤーへの指示を表す型
export type Instruction = 'win' | 'lose' | 'draw';
// ゲームの結果を表す型
export type Result = 'correct' | 'incorrect';
// ゲームの状態を表すインターフェース
export interface GameState {
playerHand: Hand | null; // プレイヤーの選んだ手
computerHand: Hand | null; // コンピューターの手
instruction: Instruction | null; // 現在の指示
result: Result | null; // ゲームの結果
score: { // スコア
correct: number; // 正解数
incorrect: number; // 不正解数
};
}
型定義の詳細説明
-
Hand
型:じゃんけんの手(グー、チョキ、パー)を表します。 -
Instruction
型:プレイヤーへの指示(勝つ、負ける、引き分ける)を表します。 -
Result
型:ゲームの結果(正解、不正解)を表します。 -
GameState
インターフェース:ゲームの現在の状態を表します。プレイヤーの手、コンピューターの手、指示、結果、スコアが含まれます。
これらの型定義を使用することで、コード内で間違った値を使用するミスを防ぎ、開発効率を向上させることができます。
5. コンポーネントの作成
次に、アプリの主要な部分となるコンポーネントを作成します。src/components
ディレクトリを作成し、その中に以下の2つのファイルを作成してください。
ScoreBoard.tsx
これは、現在のスコアを表示するコンポーネントです。
import React from 'react';
import { GameState } from '../types';
interface ScoreBoardProps {
score: GameState['score'];
}
const ScoreBoard: React.FC<ScoreBoardProps> = ({ score }) => {
return (
<div className="mt-4 text-xl">
<p>正解: {score.correct}</p>
<p>不正解: {score.incorrect}</p>
</div>
);
};
export default ScoreBoard;
ScoreBoard コンポーネントの詳細説明
このコンポーネントは、現在の正解数と不正解数を表示します。
-
ScoreBoardProps
インターフェース:このコンポーネントが受け取るプロパティの型を定義しています。 -
score
プロパティ:GameState
のscore
オブジェクトを受け取ります。 - コンポーネントの中身:受け取った
score
オブジェクトから正解数と不正解数を表示します。
シンプルに、現在のスコアを表示するだけのコンポーネントです。
GameLogic.tsx
これは、ゲームのメインロジックを処理するコンポーネントです。
import React, { useState, useCallback, useEffect } from 'react';
import { Hand, Instruction, Result, GameState } from '../types';
const hands: Hand[] = ['rock', 'paper', 'scissors'];
const instructions: Instruction[] = ['win', 'lose', 'draw'];
const getComputerHand = (): Hand => {
const randomIndex = Math.floor(Math.random() * hands.length);
return hands[randomIndex];
};
const getInstruction = (): Instruction => {
const randomIndex = Math.floor(Math.random() * instructions.length);
return instructions[randomIndex];
};
const getResult = (playerHand: Hand, computerHand: Hand, instruction: Instruction): Result => {
const actualResult =
playerHand === computerHand ? 'draw' :
(playerHand === 'rock' && computerHand === 'scissors') ||
(playerHand === 'paper' && computerHand === 'rock') ||
(playerHand === 'scissors' && computerHand === 'paper') ? 'win' : 'lose';
return actualResult === instruction ? 'correct' : 'incorrect';
};
interface GameLogicProps {
onStateChange: (newState: GameState) => void;
}
const GameLogic: React.FC<GameLogicProps> = ({ onStateChange }) => {
const [gameState, setGameState] = useState<GameState>({
playerHand: null,
computerHand: getComputerHand(),
instruction: getInstruction(),
result: null,
score: { correct: 0, incorrect: 0 },
});
const startNewRound = useCallback(() => {
setGameState((prevState) => ({
...prevState,
playerHand: null,
computerHand: getComputerHand(),
instruction: getInstruction(),
result: null,
}));
}, []);
const playHand = useCallback((hand: Hand) => {
if (!gameState.instruction || gameState.result) return;
const result = getResult(hand, gameState.computerHand!, gameState.instruction);
setGameState((prevState) => ({
...prevState,
playerHand: hand,
result,
score: {
correct: prevState.score.correct + (result === 'correct' ? 1 : 0),
incorrect: prevState.score.incorrect + (result === 'incorrect' ? 1 : 0),
},
}));
setTimeout(startNewRound, 2000);
}, [gameState.instruction, gameState.computerHand, gameState.result, startNewRound]);
useEffect(() => {
startNewRound();
}, [startNewRound]);
useEffect(() => {
onStateChange(gameState);
}, [gameState, onStateChange]);
return (
<div className="flex justify-center">
{hands.map((hand) => (
<button
key={hand}
onClick={() => playHand(hand)}
className="px-4 py-2 m-2 text-2xl bg-blue-500 text-white rounded hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
>
{hand === 'rock' ? '✊' : hand === 'paper' ? '✋' : '✌️'} {hand === 'rock' ? 'グー' : hand === 'paper' ? 'パー' : 'チョキ'}
</button>
))}
</div>
);
};
export default GameLogic;
GameLogic コンポーネントの詳細説明
このコンポーネントは、ゲームの核となるロジックを処理します。
-
ゲームの状態管理:
-
useState
フックを使用して、ゲームの状態(gameState
)を管理します。 - 初期状態では、コンピューターの手と指示をランダムに設定します。
-
-
新しいラウンドの開始:
-
startNewRound
関数は、新しいラウンドを開始するためにゲームの状態をリセットします。 -
useCallback
フックを使用して、この関数をメモ化し、不必要な再レンダリングを防ぎます。
-
-
プレイヤーの手の処理:
-
playHand
関数は、プレイヤーが手を選んだときに呼び出されます。 - 結果を判定し、スコアを更新し、2秒後に新しいラウンドを開始します。
-
-
副作用の処理:
- 最初の
useEffect
フックは、コンポーネントがマウントされたときに最初のラウンドを開始します。 - 2番目の
useEffect
フックは、ゲームの状態が変更されるたびに親コンポーネントに通知します。
- 最初の
-
UIのレンダリング:
- 3つの手(グー、チョキ、パー)のボタンをレンダリングします。
- 各ボタンをクリックすると、対応する手が選択されます。
役割としては、ユーザーの入力を受け付け、ゲームの進行を制御する部分を担当しています。
6. App.tsxの更新
最後に、アプリケーションのメインコンポーネントである App.tsx
を更新します。このファイルでは、先ほど作成したコンポーネントを組み合わせてアプリケーション全体を構築します。
src/App.tsx
ファイルを以下の内容で置き換えてください。
import React, { useState } from 'react';
import GameLogic from './components/GameLogic';
import ScoreBoard from './components/ScoreBoard';
import { GameState } from './types';
const App: React.FC = () => {
const [gameState, setGameState] = useState<GameState>({
playerHand: null,
computerHand: null,
instruction: null,
result: null,
score: { correct: 0, incorrect: 0 },
});
return (
<div className="container mx-auto p-4 max-w-md">
<h1 className="text-3xl font-bold text-center mb-6">じゃんけん脳トレ</h1>
{gameState.instruction && (
<p className="text-xl text-center mb-4">
指示: {gameState.instruction === 'win' ? '勝ってください!' :
gameState.instruction === 'lose' ? '負けてください!' :
'引き分けてください!'}
</p>
)}
{gameState.computerHand && (
<p className="text-xl text-center mb-4">
コンピューター: {gameState.computerHand === 'rock' ? '✊' : gameState.computerHand === 'paper' ? '✋' : '✌️'}
</p>
)}
<GameLogic onStateChange={setGameState} />
<div className="h-16 flex items-center justify-center">
{gameState.result && (
<p className="text-2xl font-bold text-center">
{gameState.result === 'correct' ? '正解!' : '不正解...'}
</p>
)}
</div>
<ScoreBoard score={gameState.score} />
</div>
);
};
export default App;
App コンポーネントの詳細説明
このコンポーネントは、アプリケーション全体の構造を定義します。
-
状態管理:
-
useState
フックを使用して、ゲームの全体的な状態(gameState
)を管理します。
-
-
UIのレンダリング:
- アプリケーションのタイトルを表示します。
- 現在の指示(勝つ、負ける、引き分ける)を表示します。
- コンピューターの選んだ手を表示します。
-
GameLogic
コンポーネントを配置し、ゲームの状態変更をハンドリングします。 - 結果(正解または不正解)を表示する固定高さの領域を設けています。これにより、結果表示時にレイアウトが変動するのを防ぎます。
-
ScoreBoard
コンポーネントを使用して、現在のスコアを表示します。
全体として、ゲームの流れを視覚的に表現し、プレイヤーがゲームの進行状況を把握しやすくなるようにしています。
7. アプリケーションの実行
さあ、いよいよアプリケーションを起動してみましょう!以下のコマンドを実行してください。
npm run dev
このコマンドを実行すると、開発サーバーが起動します。ターミナルに表示されるURLをブラウザで開いてください(通常は http://localhost:5173
です)。
ブラウザでアプリケーションが表示されたら、以下の操作を試してみてください。
- 画面に表示されるコンピューターの手と指示を確認します。
- 指示に従って、適切な手のボタンをクリックします。
- 結果(正解または不正解)が表示されることを確認します。
- スコアが更新されることを確認します。
- 2秒後に新しいラウンドが始まることを確認します。
まとめ
いかがでしたでしょうか?
インタラクティブ、かつ脳トレ効果のある「じゃんけん脳トレアプリ」を作成することができました。
もう少し発展させるならば、以下のような機能を追加してみるのも面白いかもしれません。
- ゲームの難易度を調整する機能
- ゲームの時間制限を設定する機能
- プレイヤー名を入力してスコアを保存する機能