0

I have built a rock/paper/scissors React app. The app is working fine, I just would like to add some delay when the CPU has to choose the weapon(rock/paper/scissors).

I would like that there is a window of time where the image selected by the CPU is not on the screen, while the user's choice is immediately appearing on screen.

I tried to add a setInterval() function inside my compounentDidMount() method but with no luck.

How can I add the delay only on the CPU part ?

https://codesandbox.io/s/nice-ardinghelli-96sum?file=/src/components/Main.js

Thank you very much in advance.

Main.js

import React from "react"
import Choice from "./Choice"
import TryAgain from "./TryAgain"

import paper from '../images/icon-paper.svg'
import rock from '../images/icon-rock.svg'
import scissors from '../images/icon-scissors.svg'

import './Main.css';

class Main extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            onScreen: true,
            choiceName: '',
            choiceImage: '',
            choiceBorderColor: '',
            choiceExtraBorderColor: '',
            houseChoice: '',
            results:'',
        }
        this.handleClick = this.handleClick.bind(this)
        this.handleTryAgainClick = this.handleTryAgainClick.bind(this)
    }
    
    /******************* setInterval() ******************************/
    componentDidMount() {
        // Call this function so that it fetch first time right after mounting the component
        this.handleClick();
    
        // set Interval
        this.interval = setInterval(this.handleClick, 5000);
    }

    /*function that handles the user choice*/
    handleClick = (choiceName, choiceImage, choiceBorderColor, choiceExtraBorderColor) => () => {
        this.setState({
            onScreen: false,
            choiceName,
            choiceImage,
            choiceBorderColor,
            choiceExtraBorderColor,
        })

        /*function that get a random number between 0 and 2, and set this number to the house index*/
        function getRandomInt(max) {
            return Math.floor(Math.random() * Math.floor(max));
        }
        const index = getRandomInt(3)
        this.setState({
            houseChoice: index
        })
        const results = this.getResults(choiceName, index).toUpperCase()
        this.setState({
            results: results,
        })

    
        if(results === "WIN") {
            this.props.setScore(1)
        } else if (results === "LOSE") {
            this.props.setScore(-1)
        }
        else {
            this.props.setScore(0)
        }
    }

    /*function that get the main logic and the results of the game*/
    getResults(choiceName, houseChoice) {
        if(choiceName === "paper" && houseChoice === 0) {
            return "draw"
        } else if(choiceName === "paper" && houseChoice === 1) {
            return "lose"
        } else if(choiceName === "paper" && houseChoice === 2) {
            return "win"
        }
        if(choiceName === "rock" && houseChoice === 0) {
            return "lose"
        } else if(choiceName === "rock" && houseChoice === 1) {
            return "win"
        } else if(choiceName === "rock" && houseChoice === 2) {
            return "draw"
        }
        if(choiceName === "scissors" && houseChoice === 0) {
            return "win"
        } else if(choiceName === "scissors" && houseChoice === 1) {
            return "draw"
        } else if(choiceName === "scissors" && houseChoice === 2) {
            return "lose"
        }
    }

    /*function that switches the screen and resets the index of the house*/
    handleTryAgainClick() {
        this.setState({
            onScreen: true,
            houseChoice: ''
        })
    }
3
  • By delaying the CPU choice what exactly you expect to happen? xyproblem.info Commented Nov 19, 2020 at 9:30
  • @DennisVash Yes sorry. I mean I would like that there is a window of time where the image selected by the CPU is not on the screen, while the user's choice is immediately appearing on screen. Commented Nov 19, 2020 at 9:34
  • So you need to DELAY the component render and not delaying the click. And btw you need to use setTimeout because setInterval doing something entirely different.... So read about conditional rendering. Your question is not minimal so Im not going to get over all this code for answering, hope you will get an answer. Commented Nov 19, 2020 at 9:38

5 Answers 5

2

You need to add a state, which does not render anything. For example just set the cpu choice to 4 and update your render function. Then you can add a sleep funciton like here. I made some working example of what I think you want there
The main changes I made to your code are

this.sleep(1500).then(() => {
      const index = getRandomInt(3);
      this.setState({
        houseChoice: index
      });
      const results = this.getResults(choiceName, index).toUpperCase();
      this.setState({
        results: results
      });

      /*****************calling setScore()********************/
      if (results === "WIN") {
        this.props.setScore(1);
      } else if (results === "LOSE") {
        this.props.setScore(-1);
      } else {
        this.props.setScore(0);
      }
    });

in handleClick which performs the timeout.
In the result page I added

this.state.houseChoice === 2 ? (
             /*3*/
              <div
                className="elem-container result-container"
                style={{
                  borderColor: "hsl(349, 71%, 52%)",
                  color: "hsl(349, 70%, 56%)"
                }}
              >
                <img src={rock} className="choice-image" alt="img" />
              </div>
            ) : null
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you very much for your answer! And is there a way to add a delay also on the YOU WIN/LOSE part ? I mean if there is a way that it gets displayed only when the CPU choice is appeared on screen too
Sure! Let's say we choose houseChoice === 4 to be our state when we are not yet ready to display something. We can then wrap the "result text" in a condition. I updated the example on codesandbox :) You can also replace the "null" part for example with a loading spinner or something like this
Yes! It was perfectly! Thank you very much! Your answer is correct!
1

After the human player selects a move don't immediately let the CPU select a move. Implement the componentDidUpdate lifecycle function and move the CPU's selection logic there. This also necessitates moving the checking for the win logic as well.

/*function that handles the user choice*/
handleClick = (
  choiceName,
  choiceImage,
  choiceBorderColor,
  choiceExtraBorderColor
) => () => {
  this.setState({
    onScreen: false,
    choiceName,
    choiceImage,
    choiceBorderColor,
    choiceExtraBorderColor
  });
};

Once the human player has selected a move, get the random move for the house but don't immediately enqueue the state update, instead use a setTimeout into update state in a bit.

Only check for the win if both players have selected a move but the results haven't been computed and stored in state.

componentDidUpdate(prevProps, prevState) {
  if (
    prevState.choiceName !== this.state.choiceName &&
    this.state.choiceName
  ) {
    function getRandomInt(max) {
      return Math.floor(Math.random() * Math.floor(max));
    }
    const index = getRandomInt(3);

    setTimeout(() => {
      this.setState({
        houseChoice: index
      });
    }, 2000);
  }

  if (
    this.state.choiceName &&
    [0, 1, 2].includes(this.state.houseChoice) && // We want 0 to be truthy :)
    !this.state.results
  ) {
    const results = this.getResults(
      this.state.choiceName,
      this.state.houseChoice
    ).toUpperCase();
    this.setState({
      results: results
    });

    /*****************calling setScore()********************/
    if (results === "WIN") {
      this.props.setScore(1);
    } else if (results === "LOSE") {
      this.props.setScore(-1);
    } else {
      this.props.setScore(0);
    }
  }
}

Conditionally render a "waiting on the CPU" UI

<h4 className="result-title">
  {this.state.houseChoice === ""
    ? "THE HOUSE CHOOSING"
    : "THE HOUSE PICKED"}
</h4>

{this.state.houseChoice === "" ? (
  <div>...</div>
) : this.state.houseChoice === 0 ? (
  /*1*/
  <div
    className="elem-container result-container"
    style={{
      borderColor: "hsl(230, 89%, 62%)",
      color: "hsl(230, 89%, 65%)"
    }}
  >
    <img src={paper} className="choice-image" alt="img" />
  </div>
) : this.state.houseChoice === 1 ? (
  /*2*/
  <div
    className="elem-container result-container"
    style={{
      borderColor: "hsl(39, 89%, 49%)",
      color: "hsl(40, 84%, 53%)"
    }}
  >
    <img src={scissors} className="choice-image" alt="img" />
  </div>
) : (
  /*3*/
  <div
    className="elem-container result-container"
    style={{
      borderColor: "hsl(349, 71%, 52%)",
      color: "hsl(349, 70%, 56%)"
    }}
  >
    <img src={rock} className="choice-image" alt="img" />
  </div>
)}

Conditionally render the results and try again button.

<div className="final-result-container">
  {this.state.results && (
    <>
      <h1 className="bold">YOU {this.state.results}</h1>
      <TryAgain onClick={this.handleTryAgainClick} />
    </>
  )}
</div>

Don't forget to reset the all of the state when playing again

handleTryAgainClick() {
  this.setState({
    onScreen: true,
    choiceName: "",
    choiceImage: "",
    choiceBorderColor: "",
    choiceExtraBorderColor: "",
    houseChoice: "",
    results: ""
  });
}

Edit how-to-add-delay-on-particular-function-in-react

enter image description here

enter image description here

2 Comments

Thank you very much for your answer ! And is there a way to add a delay also on the YOU WIN/LOSE part ? I mean if there is a way that it gets displayed only when the CPU choice is appeared on screen too
@zawrdo Sure. Updated the answer and codesandbox. I found and fixed a few other logical bugs I found in the code as well.
0

I'm not sure if I get the question correct, but here goes, if the delay is to be set on the render then just do not render until your choice is made. If the app has to do something after an input from the player make the following action asynchronous with async await.

Comments

0

If i get you right, maybe Debounce from lodash will be answer for you. I’ve used it on my tic tac toe app for delaying computer move. Simply add it from NPM

npm install lodash —save-dev

Then import in your file

import {debounce} from 'lodash';

Create constantly which will be debounce, which you’ve called from lodash. 500 is time,how much delay from move do you want. In this case it will be 0.5s.

const debounceComputerMove = debounce(()=>{
  computerMove();
},500);

Then when you want to call computerMove function, call debounceComputerMove instead of computerMove()

Comments

0

const sleep = (milliseconds) made a var then defined time

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.