Создайте игру Rock Paper Scissor с помощью ReactJS

Опубликовано: 6 Января, 2022

Здесь оба игрока будут ходить по очереди. Начиная с первого игрока, за которым следует второй игрок. Есть три вида оружия на выбор: камень, бумага, ножницы. После того, как второй игрок играет, результат своего хода вычисляется, обновляя статус выигрыша / проигрыша обоих игроков.

Используемые технологии / Предпосылки:

  1. ReactJS
  2. Библиотека Antd для пользовательского интерфейса

Подход: контейнеры представляют собой компоненты React с отслеживанием состояния (на основе классов). Компоненты - это компоненты React без сохранения состояния (функционально-ориентированные). В этом проекте у меня есть один контейнер - контроллер, который отвечает за управление состоянием и всю игровую логику. Также есть три компонента, а именно:

 Player -> Представление сущности Player в игре;
GameControls -> Выбор камня, бумаги или ножниц; 
DecisionBox -> Отображает статус выигрыша / проигрыша для игроков;

При нажатии кнопок оружия состояние в контроллере обновляется для соответствующего игрока. Когда обновляется оружие второго игрока, результат вычисляется функцией обработчика результатов, которая охватывает все девять комбинаций камня, бумаги и ножниц, взятых по две за раз.

Структура проекта:

Шаги:

  1. Настройте проект React с помощью команды create-response-app: create-response-app << имя проекта >> –scripts-version 1.1.5. С помощью этой команды будет сгенерирована структура проекта, подобная приведенной выше, за исключением компонентов и папок контейнеров, которые должны быть созданы внутри папки src вручную.
  2. Установите библиотеку antd с помощью команды: npm install antd
  3. Удалите код из App.css .
  4. Отредактируйте код внутри App.js : компонент приложения отображает контроллер и отображает заголовок.
    App.js

    Javascript

    import React, { Component } from "react" ;
    import Controller from "./containers/controller/controller" ;
    import { Typography } from "antd" ;
    import "antd/dist/antd.css" ;
    import "./App.css" ;
    const { Title } = Typography;
    class App extends Component {
    render() {
    return (
    <div className= "App" >
    <Title level={3} style={{ textAlign: "center" }}>
    Stone Paper Scissor
    </Title>
    <Controller />
    </div>
    );
    }
    }
    export App; default
  5. Controller.js: Контроллер содержит состояние, которое включает в себя последнее использованное оружие каждым игроком, текущий статус выигрыша / проигрыша и активное состояние, указывающее, у какого игрока сейчас ход.

    Javascript

    import React, { Component } from "react" ;
    import { Row, Col, Divider } from "antd" ;
    import { Typography } from "antd" ;
    import axios from "axios" ;
    import "antd/dist/antd.css" ;
    import Player from "../../components/Player/Player" ;
    const { Title } = Typography;
    class Controller extends Component {
    state = {
    playerOne: {
    active: true ,
    weapon: "" ,
    status: ""
    },
    playerTwo: {
    active: false ,
    weapon: "" ,
    status: ""
    }
    };

    Основная идея здесь заключается в том, что при нажатии кнопок оружия соответствующее оружие игрока в состоянии должно обновляться, и должен наступить следующий ход игрока, что здесь делается с помощью функции переключения. Кроме того, если обновляется оружие второго игрока, необходимо также обновить выигрыш / проигрыш, что здесь делается с помощью функции resultHandler. Для игровой логики он содержит набор функций:

    • WeaponUpdate: для обновления оружия игрока в состоянии. Он принимает два аргумента: первый - playerId, то есть playerOne или playerTwo, так как это игра для двух игроков, а второй - это оружие, которое выбирает игрок.

      Javascript



      weaponUpdate = (player, weapon) => { this .setState({
      [player]: {
      ... this .state[player],
      weapon: weapon
      }
      });
      if (player == "playerTwo" ) {
      this .resultHandler();
      } else {
      this .toggleActive();
      }
      };
    • ToggleActive: для изменения активного статуса игроков, когда любой из игроков выбирает оружие. Активное свойство в состоянии - это логическая переменная, поэтому нам просто нужно использовать оператор NOT, чтобы изменить текущее состояние каждого игрока.

      Javascript

      toggleActive = () => {
      this .setState(prevState => {
      return {
      ...prevState,
      playerOne: {
      ...prevState.playerOne,
      active: !prevState.playerOne.active
      },
      playerTwo: {
      ...prevState.playerTwo,
      active: !prevState.playerTwo.active
      }
      };
      });
      };
    • ResultHandler: он содержит основную игровую логику, которая определяет, какой игрок выиграл. Он использует вспомогательную функцию, называемую помощником по принятию решений, которая принимает два аргумента, которые являются оружием каждого игрока, и возвращает массив, содержащий два элемента, в которых первый элемент соответствует статусу выигрыша / проигрыша первого игрока, а второй элемент - статусу второго игрока. Затем обработчик результатов обновляет состояние значениями, возвращаемыми функцией decicerhelper.

      Javascript

      resultHandler = () => {
      this .setState(prevState => {
      let [s1, s2] = this .deciderHelper(
      prevState.playerOne.weapon,
      prevState.playerTwo.weapon);
      return {
      ...prevState,
      playerOne: {
      ...prevState.playerOne,
      status: s1
      },
      playerTwo: {
      ...prevState.playerTwo,
      status: s2
      }
      };
      });
      this .toggleActive();
      };
    • DeciderHelper : это вспомогательная функция, которая принимает два входа, которые являются оружием для каждого игрока, и возвращает массив, состоящий из двух элементов, каждый из которых обозначает статус выигрыша / проигрыша игрока 1 и игрока 2 соответственно. Здесь «r» означает камень, «s» означает ножницы, «p» означает бумагу, «w» - выигрыш, а «l» - проигрыш. Учитываются все возможные комбинации камень, камень, ножницы, в случае ничьей возвращаются оба игрока со статусом победы.

      Javascript

      deciderHelper = (p1, p2) => {
      if (p1 == "r" && p2 == "s" ) {
      return [ "w" , "l" ];
      }
      if (p1 == "r" && p2 == "p" ) {
      return [ "l" , "w" ];
      }
      if (p1 == "r" && p2 == "r" ) {
      return [ "w" , "w" ];
      }
      if (p1 == "p" && p2 == "r" ) {
      return [ "w" , "l" ];
      }
      if (p1 == "p" && p2 == "s" ) {
      return [ "l" , "w" ];
      }
      if (p1 == "p" && p2 == "p" ) {
      return [ "w" , "w" ];
      }
      if (p1 == "s" && p2 == "r" ) {
      return [ "l" , "w" ];
      }
      if (p1 == "s" && p2 == "p" ) {
      return [ "w" , "l" ];
      }
      if (p1 == "s" && p2 == "s" ) {
      return [ "w" , "w" ];
      }
      };
    • Метод рендеринга:

      Javascript

      render() {
      return (
      <Row justify= "space-around" align= "middle" >
      <Col className= "gutter-row" xs={15} sm={15} md={5} lg={5}>
      <Title level={3}>Player One</Title>
      <Player
      active={ this .state.playerOne.active}
      weaponUpdate={weapon => this .weaponUpdate( "playerOne" , weapon)}
      weapon={ this .state.playerOne.weapon}
      status={ this .state.playerOne.status}
      />
      </Col>
      <Col className= "gutter-row" xs={15} sm={15} md={5} lg={5}>
      <Title level={3}>Player Two</Title>
      <Player
      active={ this .state.playerTwo.active}
      weaponUpdate={weapon => this .weaponUpdate( "playerTwo" , weapon)}
      weapon={ this .state.playerTwo.weapon}
      status={ this .state.playerTwo.status}
      />
      </Col>
      </Row>
      );
      }
      }
      export Controller; default
  6. Кодирование компонентов: Код в них очень прост. Здесь находится просто репрезентативная логика.

    • Player.js : как упоминалось ранее, это компонент без сохранения состояния, представляющий объект игрока. Он получает четыре реквизита: активный статус игрока, текущее оружие, статус выигрыша / проигрыша и functionHandler для обновления оружия. Кроме того, он использует два компонента GameControls и блок принятия решений, которые обсуждаются ниже. Кроме того, если полученные активные реквизиты истинны, то к div применяется объект CSS с именем glowEffect.

      Javascript

      import React from "react" ;
      import GameControls from "../GameControls/GameControls" ;
      import DecisionBox from "../DecisionBox/DecisionBox" ;
      const Player = props => {
      let glowEffect = {};
      if (props.active) {
      glowEffect = {
      "-webkit-box-shadow" : "0 0 20px blue" ,
      "-moz-box-shadow" : "0 0 20px blue" ,
      "box-shadow" : "0 0 20px blue"
      };
      }
      return (
      <div style={glowEffect}>
      <GameControls wUpdate={props.weaponUpdate} isActive={props.active} />
      <DecisionBox weapon={props.weapon} status={props.status} />
      </div>
      );
      };
      export Player; default
    • GameControls.js: содержит три кнопки: камень, камень, ножницы. Если плеер не активен, кнопки отключены. При нажатии кнопки вызывается функция weaponupdate, которая принимает один аргумент «p» для бумаги, «r» для камня, «s» для ножниц, и состояние соответствующего обновляется.

      Javascript

      import React from "react" ;
      import { Card } from "antd" ;
      import { Button } from "antd" ;
      import "antd/dist/antd.css" ;
      const GameControls = props => {
      return (
      <Card
      title= "Controls"
      style={{ width: "300px" , height: "250px" , alignItems: "center" }}
      >
      <p style={{ alignItems: "center" }}>
      <Button
      type= "dashed"
      size= "large"
      shape= "round"
      block
      onClick={() => props.wUpdate( "r" )}
      disabled={!props.isActive}
      >
      Rock
      </Button>
      </p>
      <p style={{ alignItems: "center" }}>
      { " " }
      <Button
      type= "dashed"
      size= "large"
      shape= "round"
      block
      onClick={() => props.wUpdate( "p" )}
      disabled={!props.isActive}
      >
      Paper
      </Button>
      </p>
      <p style={{ alignItems: "center" }}>
      <Button
      type= "dashed"
      size= "large"
      shape= "round"
      block
      onClick={() => props.wUpdate( "s" )}
      disabled={!props.isActive}
      >
      Scissors
      </Button>
      </p>
      </Card>
      );
      };
      export GameControls; default
    • DecisionBox.js : он отображает две вещи: оружие игрока и его выигрышный / проигрышный статус. Он получает два реквизита: оружие ('r' или 'p' или 's') и статус ('w' или 'l'). Два объекта: карта оружия и карта состояния используются для сопоставления сокращений с соответствующими словами, которые могут отображаться на экране.

      Javascript

      import React from "react" ;
      import { Card } from "antd" ;
      import { Typography } from "antd" ;
      const { Title } = Typography;
      const weaponMap = {
      s: "Scissors" ,
      p: "Paper" ,
      r: "Rock"
      };
      const statusMap = {
      w: "Win" ,
      l: "Loose"
      };
      const DecisionBox = props => {
      return (
      <Card
      title= "Decision Box"
      style={{
      width: "300px" ,
      height: "250px" ,
      alignItems: "center" ,
      marginTop: "15px"
      }}
      bodyStyle={{ textAlign: "center" }}
      >
      <Title level={1} type= "warning" >
      {weaponMap[props.weapon]}
      </Title>
      <Title level={2} mark type= "secondary" >
      {statusMap[props.status]}
      </Title>
      </Card>
      );
      };
      export DecisionBox; default

Выход: