Home > OS >  What is wrong with this approach in comparing two rows in mastermind?
What is wrong with this approach in comparing two rows in mastermind?

Time:01-24

Created a function with two ints and two lists. This approach still double counts. Example: row 1: 1,2,2,3 row 2: 1,4,2,5

It shows wrongPosition: 1 and samePlace: 2 instead of samePlace: 2 and wrongPosition:0. Also, do you have any suggestion as to how to optimise this code?

private void CompareRows()
{
    if (_resaultPawns.Length != _actualRowPawns.Length)
    {
        Debug.LogError("Arrays have different length");
        return;
     }
     int swrongPosition = 0;
     int samePlace = 0;
     List<int> alreadyCounted = new List<int>();
     List<int> alreadyCountedColors = new List<int>();

     // Check for same position with same color
     for (int i = 0; i < _resaultPawns.Length; i  )
     {
         if (_resaultPawns[i].GetComponent<MeshRenderer>().material.color == _actualRowPawns[i].GetComponent<MeshRenderer>().material.color)
         {
             if (!alreadyCounted.Contains(i))
             {
                  samePlace  ;
                  alreadyCounted.Add(i);
             }
         }
     }
    
     // Check for wrong position with same color
     for (int i = 0; i < _resaultPawns.Length; i  )
     {
         for (int j = 0; j < _actualRowPawns.Length; j  )
         {
             if (_resaultPawns[i].GetComponent<MeshRenderer>().material.color == _actualRowPawns[j].GetComponent<MeshRenderer>().material.color && !alreadyCounted.Contains(i) && !alreadyCountedColors.Contains(j))
             {
                 if (!alreadyCounted.Contains(i) && !alreadyCountedColors.Contains(j))
                 {
                     swrongPosition  ;
                     alreadyCountedColors.Add(j);
                     alreadyCounted.Add(i);
                 }
            }
        }
    }
}

CodePudding user response:

It seems pretty simple to just do this:

var hidden = new[] { 1, 2, 2, 3 };
var first = new[] { 1, 4, 2, 5 };

int sp = 0;
int wp = 0;
for (int i = 0; i < 4; i  )
{
    if (first[i] == hidden[i])
        sp  ;
    else
        for (int j = 0; j < 4; j  )
            if (first[i] == hidden[j] && first[j] != hidden[j])
            {
                wp  ;
                break;
            }
}

Console.WriteLine($"Same Place: {sp}, Wrong Place: {wp}");

That outputs Same Place: 2, Wrong Place: 0.

CodePudding user response:

See https://dotnetfiddle.net/91GVVe

I looked at this from optimizations/maintainability first and refactoring the code a bit seems to already have solved the issue as well ^^

    var samePlace = new List<int>(4);
    var wrongPosition = new List<int>(4);

    for (var i = 0; i < _resaultPawns.Length; i  )
    {
        if (_resaultPawns[i].GetComponent<MeshRenderer>().material.color == _actualRowPawns[i].GetComponent<MeshRenderer>().material.color)
        {
            // Check for 'alreadyCounted.Contains' is redundan at this stage
            
            // No additional counter needed - simply add items and use the list Count later on
            samePlace.Add(i);
        }
    }

    for (var i = 0; i < _resaultPawns.Length; i  )
    {
        // Can directly skip as already counted
        if (samePlace.Contains(i)) continue;
        
        for (var j = 0; j < _actualRowPawns.Length; j  )
        {
            // can directly skip as would have been upper loop match
            // slightly chaper though than using the 'samePlace.Contains(j)' below
            if (i == j) continue;
            
            // skip as well to not re-use already exact matches 
            if (samePlace.Contains(j)) continue;
            
            // skip to not use twice
            if (wrongPosition.Contains(j)) continue;
            
            if (_resaultPawns[i].GetComponent<MeshRenderer>().material.color == _actualRowPawns[j].GetComponent<MeshRenderer>().material.color)
            {
                wrongPosition.Add(j);
                break;
            }
        }
    }

    var wrongPositionCount = wrongPosition.Count;
    var samePlaceCount = samePlace.Count;

I guess the main bit I added was

if (samePlace.Contains(j)) continue;

=> you also want to ignore if a j index was already seen before by the first loop as it is already an exact match.

In your example

1,2,2,3 

1,4,2,5

even though the index 3 (=value 2) was already counted as an exact match it was used again as an alternative position for index 1, a case you didn't check for as it is neither in your alreadyCounted nor in alreadyCountedColour

CodePudding user response:

Linq solution just for the fun of it... instructive (allthough overkill) as it shows use of generics, tuples and how you can tweak built-in linq operations with custom comparers.

private static (int Guessed, int Misplaced) CompareRows<T>
    (IEnumerable<T> guesses, IEnumerable<T> master)
    where T: IEquatable<T>
{
    var guessesWithIndex = guesses.Select((g, i) => (g, i));
    var masterWithIndex = master.Select((m, i) => (m, i));

    var guessed = masterWithIndex.Intersect(guessesWithIndex);
    var potentiallyMisplacedGuesses = guessed.Any()?
        guessesWithIndex.Except(guessed): guessesWithIndex;
    var masterNotGuessed = guessed.Any() ?
        masterWithIndex.Except(guessed) : masterWithIndex;
    var misplaced = potentiallyMisplacedGuesses.Intersect(
        masterNotGuessed, new OnlyFirstValueComparer<T, int>());
    return (guessed.Count(), misplaced.Count());
}

class OnlyFirstValueComparer<T, K> : IEqualityComparer<(T, K)> where T: IEquatable<T>
{
    public bool Equals((T, K) x, (T, K) y)
    {
        return x.Item1.Equals(y.Item1);
    }

    public int GetHashCode((T, K) obj)
    {
        return obj.Item1.GetHashCode();
    }
}
  • Related