trackler 2.0.5.9 → 2.0.5.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d1f9e78737566c55c139024513a73f38029f2a0c
4
- data.tar.gz: 853360aad86762df2a9279f3ad209e4497c739ad
3
+ metadata.gz: 3a4a09d238b30d68369e40d493a03b8f095a1803
4
+ data.tar.gz: f5e9f063daeaf6f628c06eaa82147898c68ef380
5
5
  SHA512:
6
- metadata.gz: 1517522938b3420f81dd931fb53458a99858c61e4673ec0413c120fc9aff827306e257fb5a316a21a2b771c12ae8a859ea943b53967ade8471031c9761e139e6
7
- data.tar.gz: 527684182f9afe7b3e1e4a8b9fbc0a07e1a60dc0272d1188efb311678f065d4eb49118f5c059a87bb6d8a6daf0158a20e06dea9d45c69c746b01effb5541c3ed
6
+ metadata.gz: 5428931a7a5b48c186986179706882feeb43c2f422e20d177bd132256888b0caa72ec7a8a22679ba178b85dd629a8487889856875bcdd5dd1dceb307864c9618
7
+ data.tar.gz: a63b460b8a05caff7fd55f4e4310b933188426ef3e685dbbd08cbae30c58bdaa12d1b8e0eb69b548c93623c4edce95c3bd88575394ffe3d7a891f4cd640367b8
@@ -46,6 +46,7 @@ Problem areas
46
46
  -------------
47
47
  Algorithms
48
48
  Bitwise operations
49
+ Concurrency
49
50
  Domain-specific languages
50
51
  Files
51
52
  Filtering
@@ -1,3 +1,3 @@
1
1
  module Trackler
2
- VERSION = "2.0.5.9"
2
+ VERSION = "2.0.5.10"
3
3
  end
@@ -747,6 +747,15 @@
747
747
  "Algorithms"
748
748
  ]
749
749
  },
750
+ {
751
+ "slug": "go-counting",
752
+ "difficulty": 9,
753
+ "topics": [
754
+ "Parsing",
755
+ "Tuples",
756
+ "Optional values"
757
+ ]
758
+ },
750
759
  {
751
760
  "slug": "sgf-parsing",
752
761
  "difficulty": 9,
@@ -0,0 +1,147 @@
1
+ using System;
2
+ using System.Drawing;
3
+ using System.Collections.Generic;
4
+ using System.Linq;
5
+
6
+ public class GoCounting
7
+ {
8
+ public enum Player
9
+ {
10
+ None,
11
+ Black,
12
+ White
13
+ }
14
+
15
+ private readonly Player[][] board;
16
+
17
+ public GoCounting(string input)
18
+ {
19
+ board = ParseBoard(input);
20
+ }
21
+
22
+ private static Player CharToPlayer(char c)
23
+ {
24
+ switch (c)
25
+ {
26
+ case 'B': return Player.Black;
27
+ case 'W': return Player.White;
28
+ default: return Player.None;
29
+ }
30
+ }
31
+
32
+ private static Player[][] ParseBoard(string input)
33
+ {
34
+ var split = input.Split('\n');
35
+ var rows = split.Length;
36
+ var cols = split[0].Length;
37
+
38
+ return split.Select(row => row.Select(CharToPlayer).ToArray()).ToArray();
39
+ }
40
+
41
+ private int Cols => board[0].Length;
42
+ private int Rows => board.Length;
43
+
44
+ private bool IsValidCoordinate(Point coordinate) =>
45
+ coordinate.Y >= 0 && coordinate.Y < Rows &&
46
+ coordinate.X >= 0 && coordinate.X < Cols;
47
+
48
+ private Player GetPlayer(Point coordinate) => board[coordinate.Y][coordinate.X];
49
+
50
+ private bool IsEmpty(Point coordinate) => GetPlayer(coordinate) == Player.None;
51
+ private bool IsTaken(Point coordinate) => !IsEmpty(coordinate);
52
+
53
+ private IEnumerable<Point> EmptyCoordinates()
54
+ {
55
+ return Enumerable.Range(0, Cols).SelectMany(col =>
56
+ Enumerable.Range(0, Rows).Select(row => new Point(col, row)))
57
+ .Where(IsEmpty);
58
+ }
59
+
60
+ private IEnumerable<Point> NeighborCoordinates(Point coordinate)
61
+ {
62
+ var row = coordinate.Y;
63
+ var col = coordinate.X;
64
+
65
+ var coords = new[]
66
+ {
67
+ new Point(col, row - 1),
68
+ new Point(col-1, row),
69
+ new Point(col+1, row),
70
+ new Point(col, row+1)
71
+ };
72
+
73
+ return coords.Where(IsValidCoordinate);
74
+ }
75
+
76
+ private IEnumerable<Point> TakenNeighborCoordinates(Point coordinate) =>
77
+ NeighborCoordinates(coordinate).Where(IsTaken);
78
+
79
+ private IEnumerable<Point> EmptyNeighborCoordinates(Point coordinate) =>
80
+ NeighborCoordinates(coordinate).Where(IsEmpty);
81
+
82
+ private Player TerritoryOwner(HashSet<Point> coords)
83
+ {
84
+ var neighborColors = coords.SelectMany(TakenNeighborCoordinates).Select(GetPlayer);
85
+ var uniqueNeighborColors = ToSet(neighborColors);
86
+
87
+ if (uniqueNeighborColors.Count == 1)
88
+ return uniqueNeighborColors.First();
89
+
90
+ return Player.None;
91
+ }
92
+
93
+ private HashSet<Point> TerritoryHelper(HashSet<Point> remainder, HashSet<Point> acc)
94
+ {
95
+ if (!remainder.Any())
96
+ return acc;
97
+
98
+ var emptyNeighbors = new HashSet<Point>(remainder.SelectMany(EmptyNeighborCoordinates));
99
+ emptyNeighbors.ExceptWith(acc);
100
+
101
+ var newAcc = ToSet(acc);
102
+ newAcc.UnionWith(emptyNeighbors);
103
+ return TerritoryHelper(emptyNeighbors, newAcc);
104
+ }
105
+
106
+ private HashSet<Point> Territory(Point coordinate) =>
107
+ IsValidCoordinate(coordinate) && IsEmpty(coordinate)
108
+ ? TerritoryHelper(ToSingletonSet(coordinate), ToSingletonSet(coordinate))
109
+ : new HashSet<Point>();
110
+
111
+ public Tuple<Player, IEnumerable<Point>> TerritoryFor(Point coord)
112
+ {
113
+ var coords = Territory(coord);
114
+ if (!coords.Any())
115
+ return null;
116
+
117
+ var owner = TerritoryOwner(coords);
118
+ return Tuple.Create(owner, coords.AsEnumerable());
119
+ }
120
+
121
+ private Dictionary<Player, IEnumerable<Point>> TerritoriesHelper(HashSet<Point> remainder, Dictionary<Player, IEnumerable<Point>> acc)
122
+ {
123
+ if (!remainder.Any())
124
+ return acc;
125
+
126
+ var coord = remainder.First();
127
+ var coords = Territory(coord);
128
+ var owner = TerritoryOwner(coords);
129
+
130
+ var newRemainder = ToSet(remainder);
131
+ newRemainder.ExceptWith(coords);
132
+
133
+ var newAcc = new Dictionary<Player, IEnumerable<Point>>(acc);
134
+ newAcc[owner] = coords;
135
+
136
+ return TerritoriesHelper(newRemainder, newAcc);
137
+ }
138
+
139
+ public Dictionary<Player, IEnumerable<Point>> Territories()
140
+ {
141
+ var emptyCoords = EmptyCoordinates();
142
+ return TerritoriesHelper(ToSet(emptyCoords), new Dictionary<Player, IEnumerable<Point>>());
143
+ }
144
+
145
+ private static HashSet<T> ToSet<T>(IEnumerable<T> value) => new HashSet<T>(value);
146
+ private static HashSet<T> ToSingletonSet<T>(T value) => new HashSet<T> { value };
147
+ }
@@ -0,0 +1,127 @@
1
+ using NUnit.Framework;
2
+ using System;
3
+ using System.Collections.Generic;
4
+ using System.Drawing;
5
+
6
+ public class GoCountingTest
7
+ {
8
+ private static readonly string boardFiveByFive =
9
+ string.Join("\n", new[]
10
+ {
11
+ " B ",
12
+ " B B ",
13
+ "B W B",
14
+ " W W ",
15
+ " W "
16
+ });
17
+
18
+ private static readonly string board9x9 =
19
+ string.Join("\n", new[]
20
+ {
21
+ " B B ",
22
+ "B B B",
23
+ "WBBBWBBBW",
24
+ "W W W W W",
25
+ " ",
26
+ " W W W W ",
27
+ "B B B B",
28
+ " W BBB W ",
29
+ " B B "
30
+ });
31
+
32
+ [Test]
33
+ public void FiveByFiveTerritoryForBlack()
34
+ {
35
+ var board = new GoCounting(boardFiveByFive);
36
+ var result = board.TerritoryFor(new Point(0, 1));
37
+ Assert.That(result.Item1, Is.EqualTo(GoCounting.Player.Black));
38
+ Assert.That(result.Item2, Is.EquivalentTo(new[] { new Point(0, 0), new Point(0, 1), new Point(1, 0) }));
39
+ }
40
+
41
+ [Ignore("Remove to run test")]
42
+ [Test]
43
+ public void FiveByFiveTerritoryForWhite()
44
+ {
45
+ var board = new GoCounting(boardFiveByFive);
46
+ var result = board.TerritoryFor(new Point(2, 3));
47
+ Assert.That(result.Item1, Is.EqualTo(GoCounting.Player.White));
48
+ Assert.That(result.Item2, Is.EquivalentTo(new[] { new Point(2, 3) }));
49
+ }
50
+
51
+ [Ignore("Remove to run test")]
52
+ [Test]
53
+ public void FiveByFiveOpenTerritory()
54
+ {
55
+ var board = new GoCounting(boardFiveByFive);
56
+ var result = board.TerritoryFor(new Point(1, 4));
57
+ Assert.That(result.Item1, Is.EqualTo(GoCounting.Player.None));
58
+ Assert.That(result.Item2, Is.EquivalentTo(new[] { new Point(0, 3), new Point(0, 4), new Point(1, 4) }));
59
+ }
60
+
61
+ [Ignore("Remove to run test")]
62
+ [Test]
63
+ public void FiveByFiveNonTerritoryStone()
64
+ {
65
+ var board = new GoCounting(boardFiveByFive);
66
+ Assert.That(board.TerritoryFor(new Point(1, 1)), Is.Null);
67
+ }
68
+
69
+ [Ignore("Remove to run test")]
70
+ [Test]
71
+ public void FiveByFiveNonTerritoryDueToTooLowCoordinate()
72
+ {
73
+ var board = new GoCounting(boardFiveByFive);
74
+ Assert.That(board.TerritoryFor(new Point(-1, 1)), Is.Null);
75
+ }
76
+
77
+ [Ignore("Remove to run test")]
78
+ [Test]
79
+ public void FiveByFiveNonTerritoryDueToTooHighCoordinate()
80
+ {
81
+ var board = new GoCounting(boardFiveByFive);
82
+ Assert.That(board.TerritoryFor(new Point(1, 5)), Is.Null);
83
+ }
84
+
85
+ [Ignore("Remove to run test")]
86
+ [Test]
87
+ public void MinimalBoardWithNoTerritories()
88
+ {
89
+ var input = "B";
90
+ var board = new GoCounting(input);
91
+
92
+ var expected = new Dictionary<GoCounting.Player, IEnumerable<Point>>();
93
+
94
+ Assert.That(board.Territories(), Is.EquivalentTo(expected));
95
+ }
96
+
97
+ [Ignore("Remove to run test")]
98
+ [Test]
99
+ public void OneTerritoryCoveringTheWholeBoard()
100
+ {
101
+ var input = " ";
102
+ var board = new GoCounting(input);
103
+
104
+ var expected = new Dictionary<GoCounting.Player, IEnumerable<Point>>
105
+ {
106
+ [GoCounting.Player.None] = new[] { new Point(0, 0) }
107
+ };
108
+
109
+ Assert.That(board.Territories(), Is.EquivalentTo(expected));
110
+ }
111
+
112
+ [Ignore("Remove to run test")]
113
+ [Test]
114
+ public void TwoTerritoriesOnRectangularBoard()
115
+ {
116
+ var input = string.Join("\n", new[] { " BW ", " BW " });
117
+ var board = new GoCounting(input);
118
+
119
+ var expected = new Dictionary<GoCounting.Player, IEnumerable<Point>>
120
+ {
121
+ [GoCounting.Player.Black] = new[] { new Point(0, 0), new Point(0, 1) },
122
+ [GoCounting.Player.White] = new[] { new Point(3, 0), new Point(3, 1) }
123
+ };
124
+
125
+ Assert.That(board.Territories(), Is.EquivalentTo(expected));
126
+ }
127
+ }
@@ -65,13 +65,6 @@
65
65
  "Floating-point numbers"
66
66
  ]
67
67
  },
68
- {
69
- "slug": "bank-account",
70
- "difficulty": 2,
71
- "topics": [
72
- "Optional values"
73
- ]
74
- },
75
68
  {
76
69
  "slug": "grains",
77
70
  "difficulty": 2,
@@ -557,6 +550,14 @@
557
550
  "Transforming"
558
551
  ]
559
552
  },
553
+ {
554
+ "slug": "bank-account",
555
+ "difficulty": 5,
556
+ "topics": [
557
+ "Optional values",
558
+ "Concurrency"
559
+ ]
560
+ },
560
561
  {
561
562
  "slug": "markdown",
562
563
  "difficulty": 5,
@@ -574,6 +575,13 @@
574
575
  "Transforming"
575
576
  ]
576
577
  },
578
+ {
579
+ "slug": "book-store",
580
+ "difficulty": 5,
581
+ "topics": [
582
+ "Recursion"
583
+ ]
584
+ },
577
585
  {
578
586
  "slug": "tournament",
579
587
  "difficulty": 6,
@@ -1,6 +1,16 @@
1
- Exercism provides exercises and feedback but can be difficult to jump into for those learning F# for the first time. These resources can help you get started:
1
+ ## Learning F#
2
2
 
3
- * [StackOverflow - F#](http://stackoverflow.com/questions/tagged/f%23)
4
- * [F# For Fun and Profit](http://fsharpforfunandprofit.com/)
5
- * [Try F#](http://www.tryfsharp.org/Learn)
6
- * [F# Foundation](http://fsharp.org/)
3
+ ### Websites
4
+ * The [F# for Fun and Profit](http://fsharpforfunandprofit.com/) website has tons of great F# content. From a gentle introduction to the world of F# to more advanced concepts, it's all covered. Bonus: all content is also available as an [eBook](https://www.gitbook.com/book/swlaschin/fsharpforfunandprofit/details).
5
+ * The [Try F#](http://www.tryfsharp.org/Learn) website has a great [interactive tutorial](http://www.tryfsharp.org/Learn/getting-started) that allows you to write, compile and execute F# code directly in the browser.
6
+ * The [F# Foundation](http://fsharp.org/) is a non-profit organisation which aim is to promote F#. The website has lots of links to great F# content. Perhaps even more interesting is their [mentorship program](http://fsharp.org/mentorship/index.html), where you can apply to learn F# from an experienced F# mentor.
7
+
8
+ ### Videos
9
+ * [F# for the Practical Developer](https://www.youtube.com/watch?v=7z_q06HQLes&t=2070s) is a nice introduction to F#.
10
+ * [F# - Why you should give an F](https://www.youtube.com/watch?v=kKkFabSzZvU) has Daniel Chambers give a sweet introduction into F#, neatly highlighting most F#'s features.
11
+ * [Dr. Don Syme - Introduction to F#](https://channel9.msdn.com/Series/C9-Lectures-Dr-Don-Syme-Introduction-to-F-/C9-Lectures-Dr-Don-Syme-Introduction-to-F-1-of-3) has Don Syme, the designer of F#, give an introduction to F#.
12
+ * If you're a C# developer, you might like [F# for C# developers](https://vimeo.com/78908217) by Phil Trelford.
13
+
14
+ ### Books
15
+ * [Beginning F# 4.0](https://books.google.nl/books?id=puQgDAAAQBAJ&redir_esc=y)
16
+ * [Real-World Functional Programming](https://books.google.nl/books?id=KfooAQAAMAAJ&q=isbn:1933988924&dq=isbn:1933988924&hl=en&sa=X&ved=0ahUKEwj-4eCii43RAhWdYFAKHdmnAEIQ6AEIHDAA)
@@ -1,5 +1,17 @@
1
- ## Linting
2
- F# projects (.fsproj) can be linted for further code analysis via a third party tool - [FSharpLint](https://github.com/fsprojects/FSharpLint).
1
+ ## Recommended Learning Resources
3
2
 
4
- To use this, just follow the directions from the wiki page to use the [command line tool](https://github.com/duckmatt/FSharpLint/wiki/Console-Application).
3
+ ### Blogs
4
+ * Sergey Tihon's [F# Weekly blog](https://sergeytihon.wordpress.com/) is a weekly-updated website that lists recent F# news and articles.
5
+ * Mark Seemann's [blog](http://blog.ploeh.dk/) also has lots of interesting F# articles, usually focusing on how to design your application.
5
6
 
7
+ ### Social media
8
+ * [StackOverflow ](http://stackoverflow.com/questions/tagged/f%23) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.
9
+ * The [F# channel](https://functionalprogramming.slack.com/messages/fsharp/) of the [functionalprogramming slack account](https://functionalprogramming.slack.com/). To join, go to [fpchat.com](http://fpchat.com/).
10
+ * The [F# slack account](https://fsharp.slack.com). To join, you have to [become a member of the F# foundation](http://fsharp.org/guides/slack/).
11
+ * [/r/fsharp](https://www.reddit.com/r/fsharp) is the F# subreddit.
12
+
13
+ ### Videos
14
+ * The [Community for F#](https://www.youtube.com/channel/UCCQPh0mSMaVpRcKUeWPotSA/feed) YouTube channel has got lots of F# videos.
15
+
16
+ ### Books
17
+ * [Expert F# 4.0](https://books.google.nl/books?id=L_0PogEACAAJ&dq=isbn:1484207424&hl=en&sa=X&ved=0ahUKEwjs__-hi43RAhWIMFAKHUJPASwQ6AEIHDAA)
@@ -12,11 +12,13 @@ let ``Returns empty balance after opening`` () =
12
12
  [<Test>]
13
13
  [<Ignore("Remove to run test")>]
14
14
  let ``Check basic balance`` () =
15
- let account1 = mkBankAccount() |> openAccount
16
- let openingBalance = account1 |> getBalance
15
+ let account = mkBankAccount() |> openAccount
16
+ let openingBalance = account |> getBalance
17
17
 
18
- let account2 = account1 |> updateBalance 10.0
19
- let updatedBalance = account2 |> getBalance
18
+ let updatedBalance =
19
+ account
20
+ |> updateBalance 10.0
21
+ |> getBalance
20
22
 
21
23
  Assert.That(openingBalance, Is.EqualTo(Some 0.0))
22
24
  Assert.That(updatedBalance, Is.EqualTo(Some 10.0))
@@ -24,14 +26,18 @@ let ``Check basic balance`` () =
24
26
  [<Test>]
25
27
  [<Ignore("Remove to run test")>]
26
28
  let ``Balance can increment or decrement`` () =
27
- let account1 = mkBankAccount() |> openAccount
28
- let openingBalance = account1 |> getBalance
29
+ let account = mkBankAccount() |> openAccount
30
+ let openingBalance = account |> getBalance
29
31
 
30
- let account2 = account1 |> updateBalance 10.0
31
- let addedBalance = account2 |> getBalance
32
+ let addedBalance =
33
+ account
34
+ |> updateBalance 10.0
35
+ |> getBalance
32
36
 
33
- let account3 = account2 |> updateBalance -15.0
34
- let subtractedBalance = account3 |> getBalance
37
+ let subtractedBalance =
38
+ account
39
+ |> updateBalance -15.0
40
+ |> getBalance
35
41
 
36
42
  Assert.That(openingBalance, Is.EqualTo(Some 0.0))
37
43
  Assert.That(addedBalance, Is.EqualTo(Some 10.0))
@@ -45,4 +51,26 @@ let ``Account can be closed`` () =
45
51
  |> openAccount
46
52
  |> closeAccount
47
53
 
48
- Assert.That(account |> getBalance, Is.EqualTo(None))
54
+ Assert.That(account |> getBalance, Is.EqualTo(None))
55
+
56
+ [<Test>]
57
+ [<Ignore("Remove to run test")>]
58
+ let ``Account can be updated from multiple threads`` () =
59
+ let account =
60
+ mkBankAccount()
61
+ |> openAccount
62
+
63
+ let updateAccountAsync =
64
+ async {
65
+ account
66
+ |> updateBalance 1.0
67
+ |> ignore
68
+ }
69
+
70
+ updateAccountAsync
71
+ |> List.replicate 1000
72
+ |> Async.Parallel
73
+ |> Async.RunSynchronously
74
+ |> ignore
75
+
76
+ Assert.That(account |> getBalance, Is.EqualTo(Some 1000.0))
@@ -1,24 +1,29 @@
1
1
  module BankAccount
2
2
 
3
- type BankAccount =
4
- | Open of float
5
- | Closed
3
+ open System
6
4
 
7
- let mkBankAccount() = Closed
5
+ type BankAccount() =
6
+ member val Lock = new Object()
7
+ member val Balance: float option = None with get,set
8
8
 
9
- let openAccount =
10
- function
11
- | Open x -> Open x
12
- | Closed -> Open 0.0
9
+ let mkBankAccount() = BankAccount()
13
10
 
14
- let closeAccount x = Closed
11
+ let openAccount (account: BankAccount) =
12
+ lock account.Lock (fun () ->
13
+ account.Balance <- Some 0.0
14
+ account
15
+ )
15
16
 
16
- let getBalance =
17
- function
18
- | Open x -> Some x
19
- | Closed -> None
17
+ let closeAccount (account: BankAccount) =
18
+ lock account.Lock (fun () ->
19
+ account.Balance <- None
20
+ account
21
+ )
20
22
 
21
- let updateBalance change =
22
- function
23
- | Open x -> Open (x + change)
24
- | Closed -> Closed
23
+ let getBalance (account: BankAccount) = account.Balance
24
+
25
+ let updateBalance change (account: BankAccount) =
26
+ lock account.Lock (fun () ->
27
+ account.Balance <- Option.map ((+) change) account.Balance
28
+ account
29
+ )
@@ -0,0 +1,64 @@
1
+ module BookStoreTest
2
+
3
+ open System
4
+ open NUnit.Framework
5
+ open BookStore
6
+
7
+ [<Test>]
8
+ let ``Basket with single book`` () =
9
+ Assert.That(calculateTotalCost [1], Is.EqualTo(8))
10
+
11
+ [<Ignore("Remove to run test")>]
12
+ [<Test>]
13
+ let ``Basket with two of same book`` () =
14
+ Assert.That(calculateTotalCost [2; 2], Is.EqualTo(16))
15
+
16
+ [<Ignore("Remove to run test")>]
17
+ [<Test>]
18
+ let ``Empty basket`` () =
19
+ Assert.That(calculateTotalCost [], Is.EqualTo(0))
20
+
21
+ [<Ignore("Remove to run test")>]
22
+ [<Test>]
23
+ let ``Basket with two different books`` () =
24
+ Assert.That(calculateTotalCost [1; 2], Is.EqualTo(15.2))
25
+
26
+ [<Ignore("Remove to run test")>]
27
+ [<Test>]
28
+ let ``Basket with three different books`` () =
29
+ Assert.That(calculateTotalCost [1; 2; 3], Is.EqualTo(21.6))
30
+
31
+ [<Ignore("Remove to run test")>]
32
+ [<Test>]
33
+ let ``Basket with four different books`` () =
34
+ Assert.That(calculateTotalCost [1; 2; 3; 4], Is.EqualTo(25.6))
35
+
36
+ [<Ignore("Remove to run test")>]
37
+ [<Test>]
38
+ let ``Basket with five different books`` () =
39
+ Assert.That(calculateTotalCost [1; 2; 3; 4; 5], Is.EqualTo(30))
40
+
41
+ [<Ignore("Remove to run test")>]
42
+ [<Test>]
43
+ let ``Basket with eight books`` () =
44
+ Assert.That(calculateTotalCost [1; 1; 2; 2; 3; 3; 4; 5], Is.EqualTo(51.20))
45
+
46
+ [<Ignore("Remove to run test")>]
47
+ [<Test>]
48
+ let ``Basket with nine books`` () =
49
+ Assert.That(calculateTotalCost [1; 1; 2; 2; 3; 3; 4; 4; 5], Is.EqualTo(55.60))
50
+
51
+ [<Ignore("Remove to run test")>]
52
+ [<Test>]
53
+ let ``Basket with ten books`` () =
54
+ Assert.That(calculateTotalCost [1; 1; 2; 2; 3; 3; 4; 4; 5; 5], Is.EqualTo(60))
55
+
56
+ [<Ignore("Remove to run test")>]
57
+ [<Test>]
58
+ let ``Basket with eleven books`` () =
59
+ Assert.That(calculateTotalCost [1; 1; 2; 2; 3; 3; 4; 4; 5; 5; 1], Is.EqualTo(68))
60
+
61
+ [<Ignore("Remove to run test")>]
62
+ [<Test>]
63
+ let ``Basket with twelve books`` () =
64
+ Assert.That(calculateTotalCost [1; 1; 2; 2; 3; 3; 4; 4; 5; 5; 1; 2], Is.EqualTo(75.20))
@@ -0,0 +1,52 @@
1
+ module BookStore
2
+
3
+ let private costPerGroup groupSize =
4
+ let discountPercentage =
5
+ match groupSize with
6
+ | 1 -> 0.
7
+ | 2 -> 5.
8
+ | 3 -> 10.
9
+ | 4 -> 20.
10
+ | 5 -> 25.
11
+ | _ -> failwith "Invalid group size"
12
+
13
+ 8. * (groupSize |> float) * (100. - discountPercentage) / 100.
14
+
15
+ let private remove n list =
16
+ let rec removeTail n list acc =
17
+ match list with
18
+ | x::xs when x = n -> List.append (List.rev acc) xs
19
+ | x::xs -> removeTail n xs (x::acc)
20
+ | [] -> List.rev acc
21
+ removeTail n list []
22
+
23
+ let rec private calculateTotalCostHelper books priceSoFar =
24
+ match books |> List.length with
25
+ | 0 -> priceSoFar
26
+ | _ ->
27
+ let groups =
28
+ books
29
+ |> List.groupBy id
30
+ |> List.map fst
31
+
32
+ let prices =
33
+ [1 .. groups |> List.length]
34
+ |> List.map (fun i ->
35
+ let itemsToRemove =
36
+ groups
37
+ |> List.take i
38
+
39
+ let remaining =
40
+ itemsToRemove
41
+ |> List.fold (fun state t ->
42
+ remove t state)
43
+ books
44
+
45
+ calculateTotalCostHelper remaining (priceSoFar + costPerGroup i)
46
+ )
47
+
48
+ prices
49
+ |> List.min
50
+
51
+ let calculateTotalCost books =
52
+ calculateTotalCostHelper books 0.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trackler
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.5.9
4
+ version: 2.0.5.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katrina Owen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-24 00:00:00.000000000 Z
11
+ date: 2016-12-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip
@@ -1333,6 +1333,8 @@ files:
1333
1333
  - tracks/csharp/exercises/food-chain/FoodChainTest.cs
1334
1334
  - tracks/csharp/exercises/gigasecond/Example.cs
1335
1335
  - tracks/csharp/exercises/gigasecond/GigasecondTest.cs
1336
+ - tracks/csharp/exercises/go-counting/Example.cs
1337
+ - tracks/csharp/exercises/go-counting/GoCountingTest.cs
1336
1338
  - tracks/csharp/exercises/grade-school/Example.cs
1337
1339
  - tracks/csharp/exercises/grade-school/GradeSchoolTest.cs
1338
1340
  - tracks/csharp/exercises/grains/Example.cs
@@ -2420,6 +2422,8 @@ files:
2420
2422
  - tracks/fsharp/exercises/binary-search/Example.fs
2421
2423
  - tracks/fsharp/exercises/bob/BobTest.fs
2422
2424
  - tracks/fsharp/exercises/bob/Example.fs
2425
+ - tracks/fsharp/exercises/book-store/BookStoreTest.fs
2426
+ - tracks/fsharp/exercises/book-store/Example.fs
2423
2427
  - tracks/fsharp/exercises/bowling/BowlingTest.fs
2424
2428
  - tracks/fsharp/exercises/bowling/Example.fs
2425
2429
  - tracks/fsharp/exercises/bracket-push/BracketPushTest.fs