3
\$\begingroup\$

As working on a little private programming challenge, I tried "Rock Paper Scissors" without arrays or if-statements. It works as intended, though, I am sure this can be done a little bit more optimized. Especially in the context of Math/Programming.

What I try to achieve:

  • Create 2 Players, one Computer one Human.
  • They both can make a Gesture (Rock, Paper or Scissors at Random or to Choose for the Human Player).
  • The value of each Gesture is calculated into a Score
  • The calculated Scores are calculated into a Winner of the Duel

The Players are two classes that implement an abstract class. The Winner and Gesture are Enums with specified values

The random Method just sets a random Gesture value for the player like:

 public void Random() => _gesture = (Gesture)_random.Next(0, 3);

The score for a single Hand is created as if it were points on a circle at 0, 120 and 240 degrees sample

((double)(_winner + 1) * 3 + (double)_gesture * 2) % 6 * Math.PI / 3;

The opposite hand is done the same, however, it starts at 180 degrees ( 180, 300, 60 degrees)

I try to find the position of both hands on the circle and calculate that into a unique value that results in a winner.

(Winner)(Math.Round(Math.Sin(other.Score() - this.Score())) * -(int)_winner);

This all works nice, however I suppose that the latter both rules could be reduced.

double Score() => ((double)(_winner + 1) * 3 + (double)_gesture * 2) % 6 * Math.PI / 3;
public Winner Duel(Player other) => (Winner)(Math.Round(Math.Sin(other.Score() - this.Score())) * -(int)_winner);

Complete code:

using System;

namespace RockPaperScissors
{
    public enum Winner
    {
        Draw = 0,
        Computer = -1,
        Human = 1
    }

    public enum Gesture
    {
        Rock = 0,
        Paper = 1,
        Scissors = 2,
    }

    public abstract class Player
    {              
        private static readonly Random _random = new Random(DateTime.Now.Millisecond);
        private readonly Winner _winner;  
        protected Gesture _gesture;    

        protected Player(Winner winner)
        {
            _winner = winner;
        }

        public Gesture Gesture => _gesture;
        public void Random() => _gesture = (Gesture)_random.Next(0, 3);
        double Score() => ((double)(_winner + 1) * 3 + (double)_gesture * 2) % 6 * Math.PI / 3;
        public Winner Duel(Player other) => (Winner)(Math.Round(Math.Sin(other.Score() - this.Score())) * -(int)_winner);
    }

    public class Computer : Player
    {
        public Computer() : base(Winner.Computer)  { }
    }

    public class Human : Player
    {
        public Human() : base(Winner.Human) { }
        public void Choose(Gesture gesture) => _gesture = gesture;
    }

    class Program
    {

        static void Main(string[] args)
        {
            Computer computer = new Computer();
            Human human = new Human();

            for(int i = 0; i < 10; i ++)
            {
                computer.Random();
                human.Random();

                Console.WriteLine($"Computer does {computer.Gesture}, Human does {human.Gesture}");
                Console.WriteLine($"Winner = {computer.Duel(human)}");
                Console.WriteLine($"Winner = {human.Duel(computer)}");

                Console.ReadLine();
            }
        }
    }
}
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers. Feel free to ask a new question instead, although I'd recommend waiting at least 24 hours between questions. More answers may be coming in. \$\endgroup\$ – Mast Apr 14 '20 at 13:41
3
\$\begingroup\$

This will do

int a = _random.Next(0, 3);
int b = _random.Next(0, 3);
int winner = (a - b + 4) % 3 - 1;

There is no need to subtract angles in radians or degrees as you can simply use integers. Technically a - b is sufficient to determine the winner and + 4 % 3 - 1 wraps it to the -1/0/+1 interval.

\$\endgroup\$
1
  • \$\begingroup\$ So indeed, it becomes public Winner Duel(Player other) => (Winner)((((int)Gesture - (int)other.Gesture + 4) % 3 - 1) * (int)_winner); (I added a bit for the function working on both players) \$\endgroup\$ – Caspar Kleijne Apr 14 '20 at 13:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.