using Amazon.AmazingGame.Model;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Amazon.AmazingGame
static void Main(string[] args)
const long totalNumberOfScoreSubmission = 100000;
var amazingGameTestBed = new AmazingGameTestBed(totalNumberOfScoreSubmission);
amazingGameTestBed.ExecuteTest();
using Amazon.AmazingGame.Model;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Amazon.AmazingGame
public class AmazingGameTestBed
private const int PRINT_PERIOD = 1000;
private const int SIZE_OF_TOP_LIST = 100;
private const string RANK_FORMAT = "{0,-4} | {1,-5} | {2,-20}";
private PlayerScoreRepository _playerScoreRepository;
private RandomPlayerScoreGenerator _randomPlayerScoreGenerator;
private long _totalNumberOfScoreSubmission;
public AmazingGameTestBed(long totalNumberOfScoreSubmission)
this._totalNumberOfScoreSubmission = totalNumberOfScoreSubmission;
this._playerScoreRepository = new PlayerScoreRepository();
this._randomPlayerScoreGenerator = new RandomPlayerScoreGenerator();
private void SubmitRandomPlayerScore()
PlayerScore randomPlayerScore = _randomPlayerScoreGenerator.Next();
_playerScoreRepository.InsertScore(randomPlayerScore);
public void ExecuteTest()
for (long i = 0; i < _totalNumberOfScoreSubmission; i++)
SubmitRandomPlayerScore();
if(( (i + 1) % PRINT_PERIOD ) == 0)
IEnumerable<PlayerScore> topScores = _playerScoreRepository.GetTopScoresDescendingly(SIZE_OF_TOP_LIST);
AssertIfTheListIsDescendinglySorted(topScores);
PrintPlayerScores(topScores);
private static void AssertIfTheListIsDescendinglySorted(IEnumerable<PlayerScore> topScores)
long prevScore = topScores.First().Score;
foreach (var playerScore in topScores)
if (prevScore < playerScore.Score)
throw new ArgumentException("The top scores are not in descending order");
private static void PrintPlayerScores(IEnumerable<PlayerScore> topScores)
string printedRow = string.Format(RANK_FORMAT,"Rank","Score","Player Name");
Console.WriteLine(printedRow);
printedRow = string.Format(RANK_FORMAT, "----", "
Console.WriteLine(printedRow);
foreach (var playerScore in topScores)
printedRow = string.Format(RANK_FORMAT, rank, playerScore.Score, playerScore.GetPlayerName());
Console.WriteLine(printedRow);
Console.WriteLine("*************************************");
using Amazon.AmazingGame.Model;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Amazon.AmazingGame
public class RandomPlayerScoreGenerator
private const int MAX_SCORE = 1000;
private Random randomGenerator;
public RandomPlayerScoreGenerator()
randomGenerator = new Random();
public PlayerScore Next()
long randomScore = randomGenerator.Next(MAX_SCORE);
string randomPlayerName = "Player_" + playerIndex;
PlayerScore playerScore = new PlayerScore()
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Amazon.AmazingGame.Model
public int Id { get; set; }
public string Name { get; set; }
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Amazon.AmazingGame.Model
public class PlayerScore : IComparable<PlayerScore>
public Player Player { get; set; }
public long Score { get; set; }
public string GetPlayerName()
public int CompareTo(PlayerScore other)
int comparisonResult = this.Score.CompareTo(other.Score);
public bool IsGreaterThan(long score)
bool isGreater = this.Score > score;
public bool IsLowerThan(long score)
bool isLower = this.Score < score;
using System.Collections.Generic;
using Amazon.AmazingGame.DataStructures;
using Amazon.AmazingGame.Model;
namespace Amazon.AmazingGame
public class PlayerScoreRepository
private Heap<PlayerScore> _datasource;
public PlayerScoreRepository()
_datasource = new Heap<PlayerScore>();
public void InsertScore(PlayerScore playerScore)
_datasource.Add(playerScore);
public IEnumerable<PlayerScore> GetTopScoresDescendingly(long n)
long numberOfScores = _datasource.Count;
long sizeOfTopList = Math.Min(numberOfScores, n);
return _datasource.GetTop(n);
using System.Collections.Generic;
namespace Amazon.AmazingGame.DataStructures
internal class HeapNode<T> : IComparable<HeapNode<T>> where T : IComparable<T>
public T Data { get; set; }
public HeapNode<T> Left { get; set; }
public HeapNode<T> Right { get; set; }
if (null == Left && null == Right)
public IEnumerable<T> GetChildren()
public int CompareTo(HeapNode<T> other)
int comparisonResult = this.Data.CompareTo(other.Data);
internal class Heap<T> : IEnumerable<T>
private T minumumTopScore;
private HeapNode<T> _rootNode;
private long _numberOfItems = 0;
public void Add(T newData)
HeapNode<T> newNode = new HeapNode<T>(newData);
InsertNewNode(_rootNode, newNode);
private void InsertNewNode(HeapNode<T> topNode, HeapNode<T> newNode)
HeapNode<T> current = topNode;
HeapNode<T> maxNode = FindMax(current, newNode);
maxNode = FindMax(newNode, current.Left);
newNode.Left = current.Left;
maxNode = FindMax(newNode, current.Right);
newNode.Right = current.Right;
InsertNewNode(current.Right, newNode);
protected static HeapNode<T> FindMax(HeapNode<T> firstNode, HeapNode<T> secondNode)
if (null == firstNode && null == secondNode)
int comparisonResult = firstNode.CompareTo(secondNode);
HeapNode<T> maxNode = comparisonResult >0 ? firstNode:secondNode;
Worst case of insertion sort is O(n^2) while best base is O(n) for almost sorted inputs. Since the values that are read from the heap is almost sorted, the time complexity is much closer to O(n).
As an alternative to insertion sort which is a good fit here since the number of maximum items (n) are as low as 100, the merge sort may also be a good alternative if n is getting too big. If merge sort would have been implemented the time complexity would decrease up to O(log(n)) in return for increasing space complexity for temporary storage.
public IEnumerable<T> GetTop(long n)
throw new ArgumentException("The input must be greater than zero");
long maxIteration = Math.Min(this.Count, n);
T[] results = new T[maxIteration];
results[0] = _rootNode.Data;
Queue<HeapNode<T>> queue = new Queue<HeapNode<T>>();
if (null != _rootNode.Left)
queue.Enqueue(_rootNode.Left);
if (null != _rootNode.Right)
queue.Enqueue(_rootNode.Right);
while (queue.Count > 0 && sortedItemCount < n && sortedItemCount < this.Count)
var currentNode = queue.Dequeue();
if (currentNode.Data.CompareTo(minumumTopScore) < 0)
results[sortedItemCount] = currentNode.Data;
while (j > 0 && results[j].CompareTo(results[j - 1]) > 0)
Swap(ref results[j], ref results[j - 1]);
if (j < results.Length-1)
if (null != currentNode.Left)
queue.Enqueue(currentNode.Left);
if (null != currentNode.Right)
queue.Enqueue(currentNode.Right);
minumumTopScore = results[n - 1];
foreach (var result in results)
private void Swap(ref T node1, ref T node2)
public IEnumerator<T> GetEnumerator()
throw new NotImplementedException();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
throw new NotImplementedException();