LoginSignup
2
0
記事投稿キャンペーン 「2024年!初アウトプットをしよう」

ReactでSlotMachineを作る(Step1: 基礎実装)

Last updated at Posted at 2023-12-31

はじめに

ReactでSlotMachineを作成します。今回はStep1のため、簡単な実装です。
段々と仕様を難しくしていこうと思います。

成果物

slotmachine.gif

▪️実装Step
Step1:基本編←いまここ
Step2:Coin機能実装編
Step3: 仕様追加、リファクタリング実施
Step4:ワイルドカード、セブンカードの追加
Step5:ディレクトリ整理/リファクタリング

仕様
・スピンボタンを押すと果物が切り替わる
・3つ揃うと「勝利!」のテキストが表示される

ソースコード

ディレクトリ構成
~/develop/react/react_slot_machine$ tree -I node_modules 
.
├── README.md
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src
│   ├── App.tsx
│   ├── components
│   │   ├── Reel.tsx
│   │   ├── SlotMachine.test.tsx
│   │   └── SlotMachine.tsx
│   ├── index.tsx
│   ├── logo.svg
│   └── setupTests.ts
├── tsconfig.json
└── yarn.lock

4 directories, 17 files
package.json
{
  "name": "react_slot_machine",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@emotion/react": "^11.11.3",
    "@emotion/styled": "^11.11.0",
    "@mui/material": "^5.15.2",
    "@mui/styled-engine-sc": "^6.0.0-alpha.10",
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^13.0.0",
    "@testing-library/user-event": "^13.2.1",
    "@types/jest": "^27.0.1",
    "@types/node": "^16.7.13",
    "@types/react": "^18.0.0",
    "@types/react-dom": "^18.0.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "styled-components": "^6.1.6",
    "typescript": "^4.4.2",
    "web-vitals": "^2.1.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}
src/App.tsx
import React from "react";
import { SlotMachine } from "./components/SlotMachine";
import { ThemeProvider, createTheme } from "@mui/material";

const theme = createTheme({
  palette: {
    primary: {
      main: "#007bff",
    },
  },
});

function App() {
  return (
    <ThemeProvider theme={theme}>
      <SlotMachine />
    </ThemeProvider>
  );
}

export default App;
src/components/SlotMachine.tsx
import React, { useState } from "react";
import { Reel } from "./Reel";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";

const symbols = ["🍒", "🍋", "🍊", "🍇", "🍉"];

export const SlotMachine: React.FC = () => {
  const [reels, setReels] = useState(Array(3).fill(symbols[0]));

  const spinReels = () => {
    setReels(
      reels.map(() => symbols[Math.floor(Math.random() * symbols.length)])
    );
  };

  const isWin = new Set(reels).size === 1;

  return (
    <Box sx={{ textAlign: "center", marginTop: 4 }}>
      <Box sx={{ display: "flex", justifyContent: "center", gap: 2 }}>
        {reels.map((symbol, index) => (
          <Reel key={index} symbol={symbol} />
        ))}
      </Box>
      <Button
        variant="contained"
        color="primary"
        style={{ marginTop: "16px" }}
        onClick={spinReels}
      >
        スピン
      </Button>
      {isWin && <Box sx={{ marginTop: 2 }}>勝利!</Box>}
    </Box>
  );
};

src/components/Reel.tsx
import React from "react";
import Box from "@mui/material/Box";

type ReelProps = {
  symbol: string;
};

export const Reel: React.FC<ReelProps> = ({ symbol }) => {
  return (
    <Box
      sx={{
        fontSize: "2rem",
        padding: "10px",
        border: "1px solid #ccc",
        borderRadius: "4px",
      }}
    >
      {symbol}
    </Box>
  );
};

src/components/SlotMachine.test.tsx
import React from "react";
import { render, fireEvent, screen } from "@testing-library/react";
import { SlotMachine } from "./SlotMachine";

describe("SlotMachine コンポーネント", () => {
  test("初期レンダリングでスピンボタンが表示される", () => {
    render(<SlotMachine />);
    expect(screen.getByRole("button", { name: "スピン" })).toBeInTheDocument();
  });

  test("スピンボタンをクリックするとリールが回転する", () => {
    render(<SlotMachine />);
    const initialReels = screen
      .getAllByText("🍒")
      .map((reel) => reel.textContent);
    fireEvent.click(screen.getByRole("button", { name: "スピン" }));
    const updatedReels = screen
      .getAllByText(/🍒|🍋|🍊|🍇|🍉/)
      .map((reel) => reel.textContent);
    expect(updatedReels).not.toEqual(initialReels);
  });
});

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0