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
    Commented Apr 14, 2020 at 13:41

1 Answer 1

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\$ Commented Apr 14, 2020 at 13:21

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