trackler 2.0.5.9 → 2.0.5.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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