<repo_name>baharestani/shokunin.10xdev<file_sep>/10xDev.Tests/PresenterTests.cs
using System;
using Shouldly;
using Xunit;

namespace _10xDev.Tests
{
    public class PresenterTests
    {
        [Fact]
        public void Get10XDev_throws_exception_for_more_than_one_results()
        {
            var results = new[]
            {
                new[] {"name1", "name2"},
                new[] {"name2", "name1"}
            };

            var presenter = new Presenter();

            Should.Throw<InvalidOperationException>(() => presenter.Get10XDeveloper(results));
        }

        [Fact]
        public void Get10XDev_announce_firstName_if_only_one_result_found()
        {
            var results = new[]
            {
                new[]
                {
                    "Winner", "Other"
                }
            };

            var presenter = new Presenter();

            presenter.Get10XDeveloper(results).ShouldBe("The 10X Developer is Winner");
        }

        [Fact]
        public void GetLeaderBoard_throws_exception_for_more_than_one_results()
        {
            var results = new[]
            {
                new[] {"name1", "name2"},
                new[] {"name2", "name1"}
            };

            var presenter = new Presenter();

            Should.Throw<InvalidOperationException>(() => presenter.GetLeaderBoard(results));
        }

        [Fact]
        public void GetLeaderBoard_should_announce_all_names_if_single_result()
        {
            var results = new[]
            {
                new[] {"Name1", "Name2", "Name3"},
            };

            var presenter = new Presenter();
            var leaderBoard = presenter.GetLeaderBoard(results);
            leaderBoard.ShouldBe("Leader board:\n" +
                                 "1. Name1\n" +
                                 "2. Name2\n" +
                                 "3. Name3\n"
            );
        }
    }
}
<file_sep>/10xDev/RulesProvider.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using _10xDev.Rules;

namespace _10xDev
{
    public class RulesProvider : IRuleProvider
    {
        private readonly IFactsRepository _factsRepository;
        public RulesProvider(IFactsRepository factsRepository)
        {
            _factsRepository = factsRepository;
        }

        public IEnumerable<ISortingRule> GetRules()
        {
            var facts = _factsRepository.GetFacts();
            var ruleTypes = CollectAllRuleTypes();

            return ruleTypes.SelectMany(ruleType =>
                Regex.Matches(facts, ruleType.GetCustomAttribute<RuleAttribute>().Pattern)
                    .Select(match => ActivateRule(ruleType, match)));
        }

        private static ISortingRule ActivateRule(Type ruleType, Match regexMatch)
        {
            const int groupMatchingWhole = 1;
            var args = regexMatch.Groups.Skip(groupMatchingWhole).Select(g => (object) g.Value).ToArray();
            return (ISortingRule) Activator.CreateInstance(ruleType, args);
        }
        
        private static IEnumerable<Type> CollectAllRuleTypes()
        {
            return Assembly.GetCallingAssembly()
                .GetExportedTypes()
                .Where(t => Attribute.IsDefined(t, typeof(RuleAttribute)));
        }
    }
}
<file_sep>/10xDev/TextFileFactsRepository.cs
using System.IO;

namespace _10xDev
{
    public class TextFileFactsRepository : IFactsRepository
    {
        private readonly string _path;

        public TextFileFactsRepository(string path)
        {
            _path = path;
        }
        
        public string GetFacts()
        {
            return File.ReadAllText(_path);
        }
    }
}
<file_sep>/10xDev/IFactsRepository.cs
namespace _10xDev
{
    public interface IFactsRepository
    {
        string GetFacts();
    }
}
<file_sep>/README.md
# August Challenge: Find the 10x Developer
A solution to ThoughtWorks Australia shokunin coding challenge

## Background
Jessie, Evan, John, Sarah and Matt are all engineers in the same delivery team (note: any resemblance to actual TWers, living or dead, is purely coincidental)... and each of them has a different level of coding skill to the other.  This means it possible to order them from best to... "least best".  Importantly, the best of them is the mythical 10x Developer!!!

## Here's what we know
- Jessie is not the best developer
- Evan is not the worst developer
- John is not the best developer or the worst developer
- Sarah is a better developer than Evan
- Matt is not directly below or above John as a developer
- John is not directly below or above Evan as a developer

## Challenge
You need to write a solution to compute these answers:
1. Who is the 10x developer on the team?
1. What is the correct ordering of the members of the team according to their coding skills?

## Prerequisite

.NET Core 2.2 ([download](https://dotnet.microsoft.com/download))

## Usage

Define the facts as English sentences in `facts.txt` file.

### Mac OS, Linux

- Run the application: `./go`
- Run the tests: `./go test`

### Other OS
- Run the application: `dotnet run -p 10xDev`
- Run the tests: `dotnet test`
<file_sep>/go
#!/usr/bin/env sh

if [ $# = 0 ]; then
  dotnet run -p 10xDev
fi

if [ "$1" = "test" ]; then
  dotnet test 10xDev.Tests
fi
<file_sep>/10xDev/PermutationService.cs
using System.Collections.Generic;
using System.Linq;

namespace _10xDev
{
    public static class PermutationService
    {
        public static IEnumerable<IEnumerable<string>> GenerateFor(string[] items)
        {
            if (items.Length == 1)
                yield return items;

            foreach (var item in items)
            {
                var otherItems = items.Except(new[] {item});
                foreach (var permutation in GenerateFor(otherItems.ToArray()))
                {
                    yield return permutation.Prepend(item);
                }
            }
        }
    }
}
<file_sep>/10xDev/Rules/RuleAttribute.cs
using System;

namespace _10xDev.Rules
{
    public class RuleAttribute : Attribute
    {
        public string Pattern { get; }

        public RuleAttribute(string pattern)
        {
            Pattern = pattern;
        }
    }
}
<file_sep>/10xDev.Tests/SortServiceTests.cs
using System.Collections.Generic;
using System.Linq;
using _10xDev.Rules;
using Shouldly;
using Xunit;

namespace _10xDev.Tests
{
    public class SortServiceTests
    {
        private readonly string[] _names = {"Jessie", "Evan", "John", "Sarah", "Matt"};
        private readonly IEnumerable<IEnumerable<string>> _possibleResults;

        public SortServiceTests()
        {
            var sortService = new SortService(new RulesProvider(new TextFileFactsRepository("facts.txt")));
            _possibleResults = sortService.GetPossibleSorts(_names);
        }

        [Fact]
        public void Sorted_names_should_always_contain_all_of_them()
        {
            _possibleResults.ShouldAllBe(p => p.Count() == _names.Length);
            _possibleResults.ShouldAllBe(p=> p.Contains("Jessie"));
            _possibleResults.ShouldAllBe(p=> p.Contains("Evan"));
            _possibleResults.ShouldAllBe(p=> p.Contains("John"));
            _possibleResults.ShouldAllBe(p=> p.Contains("Sarah"));
            _possibleResults.ShouldAllBe(p=> p.Contains("Matt"));
        }

        [Fact]
        public void Jessie_is_not_the_best_developer()
        {
            _possibleResults.ShouldNotContain(p => p.First() == "Jessie");
        }

        [Fact]
        public void Evan_is_not_the_worst_developer()
        {
            _possibleResults.ShouldNotContain(p => p.Last() == "Evan");
        }

        [Fact]
        public void John_is_not_the_best_developer_or_the_worst_developer()
        {
            _possibleResults.ShouldNotContain(p => p.Last() == "John");
            _possibleResults.ShouldNotContain(p => p.First() == "John");
        }

        [Fact]
        public void Sarah_is_a_better_developer_than_Evan()
        {
            _possibleResults.ShouldAllBe(result =>
                result.IndexOf("Sarah") < result.IndexOf("Evan")
            );
        }

        [Fact]
        public void Matt_is_not_directly_below_or_above_John_as_a_developer()
        {
            _possibleResults.ShouldNotContain(result => result.Stringify().Contains("Matt,John"));
            _possibleResults.ShouldNotContain(result => result.Stringify().Contains("John,Matt"));
        }

        [Fact]
        public void John_is_not_directly_below_or_above_Evan_as_a_developer()
        {
            _possibleResults.ShouldNotContain(result => result.Stringify().Contains("John,Evan"));
            _possibleResults.ShouldNotContain(result => result.Stringify().Contains("Evan,John"));
        }

        [Fact]
        public void When_all_rules_apply_there_will_be_one_result()
        {
            _possibleResults.Count().ShouldBe(1);
        }
    }
}
<file_sep>/10xDev/Rules/NotTheWorstRule.cs
using System.Collections.Generic;
using System.Linq;

namespace _10xDev.Rules
{
    [Rule(@"\s*(.+) is not.*the worst")]
    public class NotTheWorstRule : ISortingRule
    {
        private readonly string _name;

        public NotTheWorstRule(string name) => _name = name;

        public bool Matches(IEnumerable<string> names) => names.Last() != _name;
    }
}
<file_sep>/10xDev/Presenter.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace _10xDev
{
    public class Presenter
    {
        public string Get10XDeveloper(IEnumerable<string>[] results)
        {
            var result = EnsureSingleResult(results);
            return $"The 10X Developer is {result[0]}";
        }

        public string GetLeaderBoard(IEnumerable<string>[] results)
        {
            var result = EnsureSingleResult(results);
            var leaderBoardBuilder = new StringBuilder();
            leaderBoardBuilder.AppendLine("Leader board:");
            for (var i = 0; i < result.Length; i++)
            {
                leaderBoardBuilder.AppendLine($"{i + 1}. {result[i]}");
            }

            return leaderBoardBuilder.ToString();
        }

        private static string[] EnsureSingleResult(IEnumerable<string>[] results)
        {
            return results.Length == 1 ?
                results.Single().ToArray() :
                throw new InvalidOperationException("More than one possible ordering! You many not have enough rules.");
        }
    }
}
<file_sep>/10xDev/Extensions.cs
using System;
using System.Collections.Generic;
using System.Linq;

namespace _10xDev
{
    public static class Extensions
    {
        public static int IndexOf<T>(this IEnumerable<T> @this, T element)
        {
            return Array.IndexOf(@this.ToArray(), element);
        }

        public static string Stringify(this IEnumerable<string> @this)
        {
            return string.Join(',', @this);
        }
    }
}
<file_sep>/10xDev/IRuleProvider.cs
using System.Collections.Generic;
using _10xDev.Rules;

namespace _10xDev
{
    public interface IRuleProvider
    {
        IEnumerable<ISortingRule> GetRules();
    }
}
<file_sep>/10xDev.Tests/PermutationServiceTests.cs
using System;
using System.Collections;
using System.Linq;
using Shouldly;
using Xunit;

namespace _10xDev.Tests
{
    public class PermutationServiceTests
    {
        [Fact]
        public void GeneratesPermutationsForGivenNames()
        {
            var items = new[] {"a", "b", "c"};
            var permutations = PermutationService.GenerateFor(items).ToArray();

            permutations.Length.ShouldBe(6);
            permutations.ShouldContain(p => p.SequenceEqual(new[] {"a", "b", "c"}));
            permutations.ShouldContain(p => p.SequenceEqual(new[] {"b", "a", "c"}));
            permutations.ShouldContain(p => p.SequenceEqual(new[] {"c", "a", "b"}));
            permutations.ShouldContain(p => p.SequenceEqual(new[] {"a", "c", "b"}));
            permutations.ShouldContain(p => p.SequenceEqual(new[] {"b", "c", "a"}));
            permutations.ShouldContain(p => p.SequenceEqual(new[] {"c", "b", "a"}));
        }

        [Fact]
        public void GenerateNoResultsWithNoErrorsForEmptyInput()
        {
            var items = new string[0];
            var permutations = PermutationService.GenerateFor(items).ToArray();

            permutations.Length.ShouldBe(0);
        }
    }
}
<file_sep>/10xDev/Rules/ISortingRule.cs
using System.Collections.Generic;

namespace _10xDev.Rules
{
    public interface ISortingRule
    {
        bool Matches(IEnumerable<string> names);
    }
}
<file_sep>/10xDev/Rules/NotNextToRule.cs
using System;
using System.Collections.Generic;

namespace _10xDev.Rules
{
    [Rule(@"\s*(.+) is not directly below or above (.+) as a developer")]
    public class NotNextToRule : ISortingRule
    {
        private readonly string _name1;
        private readonly string _name2;

        public NotNextToRule(string name1, string name2)
        {
            _name1 = name1;
            _name2 = name2;
        }

        public bool Matches(IEnumerable<string> names) => Math.Abs(names.IndexOf(_name1) - names.IndexOf(_name2)) > 1;
    }
}
<file_sep>/10xDev/Rules/BetterRule.cs
using System.Collections.Generic;
using System.Linq;

namespace _10xDev.Rules
{
    [Rule(@"\s*(.+) is a better developer than (.+)")]
    public class BetterRule : ISortingRule
    {
        private readonly string _better;
        private readonly string _worse;

        public BetterRule(string better, string worse)
        {
            _better = better;
            _worse = worse;
        }

        public bool Matches(IEnumerable<string> names)
        {
            var namesArray = names.ToArray();
            return namesArray.IndexOf(_better) < namesArray.IndexOf(_worse);
        }
    }
}
<file_sep>/10xDev/Program.cs
﻿using System;
using System.Linq;

namespace _10xDev
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                string[] names = {"Jessie", "Evan", "John", "Sarah", "Matt"};

                var sortService = new SortService(new RulesProvider(new TextFileFactsRepository("facts.txt")));
                var presenter = new Presenter();

                var results = sortService.GetPossibleSorts(names).ToArray();
                Console.WriteLine(presenter.Get10XDeveloper(results));
                Console.WriteLine(presenter.GetLeaderBoard(results));
            }
            catch (Exception e)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine(e.Message);
                Console.ResetColor();
            }
        }
    }
}
<file_sep>/10xDev/SortService.cs
using System.Collections.Generic;
using System.Linq;
using _10xDev.Rules;

namespace _10xDev
{
    public class SortService
    {
        private readonly IRuleProvider _ruleProvider;

        public SortService(IRuleProvider ruleProvider)
        {
            _ruleProvider = ruleProvider;
        }

        public IEnumerable<IEnumerable<string>> GetPossibleSorts(string[] names)
        {
            var permutations = PermutationService.GenerateFor(names);
            var rules = _ruleProvider.GetRules();
            return permutations.Where(p => rules.All(r => r.Matches(p)));
        }


    }
}
