Skip to content

Instantly share code, notes, and snippets.

@simoneb
Last active September 3, 2015 21:44
Show Gist options
  • Save simoneb/215545 to your computer and use it in GitHub Desktop.
Save simoneb/215545 to your computer and use it in GitHub Desktop.
KataPotter
[TestFixture]
public class Test : Calculator
{
[Test]
public void No_books_are_0()
{
Assert.AreEqual(0, Price());
}
[Test]
public void One_book_is_8()
{
Assert.AreEqual(8, Price(0));
}
[Test]
public void Two_same_books_is_16()
{
Assert.AreEqual(8 * 2, Price(0, 0));
}
[Test]
public void Three_same_books_is_24()
{
Assert.AreEqual(8 * 3, Price(1, 1, 1));
}
[Test]
public void Two_distinct_books_should_get_5_percent_discount()
{
Assert.AreEqual(8 * 2 * 0.95, Price(0, 1));
}
[Test]
public void Three_distinct_books_should_get_10_percent_discount()
{
Assert.AreEqual(8 * 3 * 0.9, Price(0, 2, 4));
}
[Test]
public void Four_distinct_books_should_get_20_percent_discount()
{
Assert.AreEqual(8 * 4 * 0.8, Price(0, 1, 2, 4));
}
[Test]
public void Five_distinct_books_should_get_25_percent_discount()
{
Assert.AreEqual(8 * 5 * 0.75, Price(0, 1, 2, 3, 4));
}
[Test]
public void Mixed_case()
{
Assert.AreEqual(8 + (8 * 2 * 0.95), Price(0, 0, 1));
}
[Test]
public void Complex_case()
{
Assert.AreEqual(51.2, Price(0, 0, 1, 1, 2, 2, 3, 4));
}
[Test]
public void I_fell_on_this_one()
{
Assert.AreEqual(78.8, Price(0, 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 4));
}
[Test]
public void And_on_this_one_too()
{
Assert.AreEqual(100, Price(0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4));
}
}
public class Calculator
{
public decimal Price(params int[] books)
{
var cart = new Cart(books);
return cart.Total;
}
}
public class Cart
{
private readonly Collection<BookSet> bookSets = new Collection<BookSet>();
public Cart(params int[] books)
{
var sortedByOccurrence = books.GroupBy(b => b)
.OrderByDescending(g => g.Count())
.SelectMany(b => b);
foreach (var b in sortedByOccurrence)
Add(b);
}
private void Add(int book)
{
if (bookSets.Any(s => s.CanAccept(book)))
bookSets.Where(g => g.CanAccept(book))
.OrderBy(s => TotalWithNewBookInSet(s, book))
.First()
.Accept(book);
else
bookSets.Add(new BookSet(book));
}
public decimal Total
{
get { return TotalWith(bookSets); }
}
private decimal TotalWithNewBookInSet(BookSet set, int book)
{
return TotalWith(bookSets.Except(new[]{set})) + set.TotalWith(book);
}
private decimal TotalWith(IEnumerable<BookSet> sets)
{
return sets.Sum(s => s.Total);
}
}
internal class BookSet
{
private readonly List<int> books = new List<int>();
public BookSet(int book)
{
books.Add(book);
}
public decimal Total
{
get { return TotalWith(books); }
}
private static decimal ComputeDiscount(IEnumerable<int> books)
{
switch(books.Count())
{
case 1: return 0m;
case 2: return 0.05m;
case 3: return 0.1m;
case 4: return 0.2m;
case 5: return 0.25m;
default: throw new NotSupportedException();
}
}
public bool CanAccept(int book)
{
return !books.Contains(book);
}
public void Accept(int book)
{
books.Add(book);
}
public decimal TotalWith(int book)
{
return TotalWith(books.Concat(new[] {book}));
}
private static decimal TotalWith(IEnumerable<int> books)
{
return 8 * books.Count() * (1 - ComputeDiscount(books));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment