using System.Collections.Generic;
public static void Main(string[] args)
var parser = new Parser();
Console.WriteLine("Input game results in separate lines, Enter empty line when done: ");
var sInputLine = Console.ReadLine();
while (!string.IsNullOrWhiteSpace(sInputLine))
if (parser.TryParse(sInputLine, out winner, out loser))
games.RecordGame(winner, loser);
Console.WriteLine("Invalid input, Ignored.");
Console.WriteLine("Keep entering game results in the valid format.");
sInputLine = Console.ReadLine();
Console.WriteLine("Results:");
var players = games.GetPlayersRanking().ToArray();
var sb = new StringBuilder();
foreach (var player in players)
sb.AppendFormat("{0} -> ", player.Name);
Console.WriteLine(sb.ToString().Substring(0, sb.Length - 4));
Console.WriteLine(ex.Message);
private static void WriteUsage()
Console.WriteLine("Supported input formats are:");
Console.WriteLine("<player1> won against <player2>");
Console.WriteLine("<player1> lost to <player2>");
private readonly Dictionary<Player, List<Player>> playersAdjList = new Dictionary<Player, List<Player>>();
public void RecordGame(Player winner, Player loser)
if (!playersAdjList.ContainsKey(winner))
playersAdjList.Add(winner, new List<Player>());
if (!playersAdjList.ContainsKey(loser))
playersAdjList.Add(loser, new List<Player>());
if (!playersAdjList[winner].Contains(loser))
playersAdjList[winner].Add(loser);
public IEnumerable<Player> GetPlayersRanking()
if (!AreAllResultsRecorded())
throw new InsufficientInputException();
var list = new LinkedList<Player>();
foreach (var keyValuePair in playersAdjList)
var currentNode = list.First;
var newPlayer = keyValuePair.Key;
var newPlayerAdjList = keyValuePair.Value;
while (currentNode != null)
if (newPlayerAdjList.Contains(currentNode.Value))
list.AddBefore(currentNode, newPlayer);
currentNode = currentNode.Next;
private bool AreAllResultsRecorded()
foreach (var keyValuePair in playersAdjList)
totalMatchCount += keyValuePair.Value.Count;
var playersCount = playersAdjList.Keys.Count;
return totalMatchCount == playersCount * (playersCount - 1) / 2;
internal class InsufficientInputException : Exception
public InsufficientInputException(): base("Insufficient games data to determine ranking.")
private readonly List<IExpressionParser> expressionParsers;
expressionParsers = new List<IExpressionParser>
new LostToExpressionParser(),
new WonAgainstExpressionParser()
public bool TryParse(string expression, out Player winner, out Player loser)
foreach (var parser in expressionParsers)
if (parser.TryParse(expression, out winner, out loser))
interface IExpressionParser
bool TryParse(string expression, out Player winner, out Player Loser);
abstract class SimpleExpressionParser : IExpressionParser
private readonly string[] conditionWords;
protected SimpleExpressionParser(params string[] args)
if (args == null || args.Length == 0)
throw new ArgumentException("Parser requires at least one word representing the condition");
public abstract bool TryParse(string expression, out Player winner, out Player loser);
protected bool TryParseTokens(string expression, out string firstToken, out string lastToken)
if (string.IsNullOrWhiteSpace(expression))
var tokens = expression.ToLower().Split(null);
if (tokens.Length != conditionWords.Length + 2)
foreach (var conditionWord in conditionWords)
if (tokens[i] != conditionWord)
lastToken = tokens[tokens.Length - 1];
class LostToExpressionParser : SimpleExpressionParser
public LostToExpressionParser() : base("lost", "to")
public override bool TryParse(string expression, out Player winner, out Player loser)
if (TryParseTokens(expression, out firstPlayer, out secondPlayer))
winner = Player.GetPlayer(secondPlayer);
loser = Player.GetPlayer(firstPlayer);
class WonAgainstExpressionParser : SimpleExpressionParser
public WonAgainstExpressionParser():base("won", "against")
public override bool TryParse(string expression, out Player winner, out Player loser)
if (TryParseTokens(expression, out firstPlayer, out secondPlayer))
winner = Player.GetPlayer(firstPlayer);
loser = Player.GetPlayer(secondPlayer);
private static readonly Dictionary<string, Player> Players = new Dictionary<string, Player>();
public string Name { get; private set; }
public static Player GetPlayer(string name)
name = name.Trim().ToLower();
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Name cannot be empty");
if (!Players.ContainsKey(name))
Players.Add(name, new Player {Name = name});