trackler 2.0.8.38 → 2.0.8.39

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: fcc166e36a7b734f6e7c78d015a93311bb78eb39
4
- data.tar.gz: f7439a7ec5b911ba95a279186d799368c895db5e
3
+ metadata.gz: f16f5a4fa0fdde0642c976d3e020bdf84d359613
4
+ data.tar.gz: 940657bebb2d0ea237f01459d6beeab37766ee9b
5
5
  SHA512:
6
- metadata.gz: bee112127f3bd191392aadd16e0a8b7d6f7b245debddf91f48b8066ec12d79b3890e78d8a5149807e9ce1a38f4356942a99ee43a28ce769aac5bad86c4b358a5
7
- data.tar.gz: a73867dadaf36325e0e92a64bc6638541f105fffd3b60e30bb8534beb86cd3f37df6821bd1c1b7edbc5b38cec31075ba7285c1e6b4a4e309bf6c04e85bf71b5e
6
+ metadata.gz: f67845a691fb409f1c1453bca11cd939f37647ba716bac4a6ccd8d15cb7d0d50f43098c3448491adfc7724d5ef1c1922e96c6c98b1eefc60368e9786dd26fe87
7
+ data.tar.gz: 59000762c30e40be13816d4d7413ceff5ca4529b98fb912874a12a7a7a9ea272c003019bf7047769f16e73d19dde9e2912c91f15790735f2aa45099bf53630e3
@@ -1,3 +1,3 @@
1
1
  module Trackler
2
- VERSION = "2.0.8.38"
2
+ VERSION = "2.0.8.39"
3
3
  end
@@ -1,2 +1,2 @@
1
1
  ## Hints
2
- The `Foldl` andf `Foldr` methods are "fold" functions, which is a concept well-known in the functional programming world, but less so in the object-oriented one. If you'd like more background information, check out this[fold](https://en.wikipedia.org/wiki/Fold_(higher-order_function)) page.
2
+ The `Foldl` and `Foldr` methods are "fold" functions, which is a concept well-known in the functional programming world, but less so in the object-oriented one. If you'd like more background information, check out this [fold](https://en.wikipedia.org/wiki/Fold_(higher-order_function)) page.
@@ -0,0 +1,3 @@
1
+ ## Hints
2
+ This exercise requires the use of a Dictionary. For more information see
3
+ [this page.](https://msdn.microsoft.com/en-us/library/s4ys34ea(v=vs.110).aspx)
@@ -505,6 +505,14 @@
505
505
  "Arrays",
506
506
  "Algorithms"
507
507
  ]
508
+ },
509
+ {
510
+ "difficulty": 1,
511
+ "slug": "bowling",
512
+ "topics": [
513
+ "Control-flow (conditionals)",
514
+ "Arrays"
515
+ ]
508
516
  }
509
517
  ]
510
518
  }
@@ -0,0 +1,77 @@
1
+ package bowling
2
+
3
+ import "testing"
4
+
5
+ const targetTestVersion = 1
6
+
7
+ const previousRollErrorMessage = `FAIL: %s
8
+ Unexpected error occurred: %q
9
+ while applying the previous rolls for the
10
+ test case: %v
11
+ The error was returned from Roll(%d) for previousRolls[%d].`
12
+
13
+ func TestTestVersion(t *testing.T) {
14
+ if testVersion != targetTestVersion {
15
+ t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
16
+ }
17
+ }
18
+
19
+ func applyPreviousRolls(g *Game, rolls []int) (int, int, error) {
20
+ for index, pins := range rolls {
21
+ var err error
22
+ if err = g.Roll(pins); err != nil {
23
+ return index, pins, err
24
+ }
25
+ }
26
+ return 0, 0, nil
27
+ }
28
+
29
+ func TestScore(t *testing.T) {
30
+ for _, tc := range scoreTestCases {
31
+ g := NewGame()
32
+ index, pins, err := applyPreviousRolls(g, tc.previousRolls)
33
+ if err != nil {
34
+ t.Fatalf(previousRollErrorMessage,
35
+ tc.description, err, tc.previousRolls, pins, index)
36
+ }
37
+ score, err := g.Score()
38
+ if tc.valid {
39
+ var _ error = err
40
+ if err != nil {
41
+ t.Fatalf("FAIL: %s : Score() after Previous Rolls: %#v expected %d, got error %s",
42
+ tc.description, tc.previousRolls, tc.score, err)
43
+ } else {
44
+ if score != tc.score {
45
+ t.Fatalf("%s : Score() after Previous Rolls: %#v expected %d, got %d",
46
+ tc.description, tc.previousRolls, tc.score, score)
47
+ }
48
+ }
49
+ } else if err == nil {
50
+ t.Fatalf("FAIL: %s : Score() after Previous Rolls: %#v expected an error, got score %d\n\tExplanation: %s",
51
+ tc.description, tc.previousRolls, score, tc.explainText)
52
+ }
53
+ t.Logf("PASS: %s", tc.description)
54
+ }
55
+ }
56
+
57
+ func TestRoll(t *testing.T) {
58
+ for _, tc := range rollTestCases {
59
+ g := NewGame()
60
+ index, pins, err := applyPreviousRolls(g, tc.previousRolls)
61
+ if err != nil {
62
+ t.Fatalf(previousRollErrorMessage,
63
+ tc.description, err, tc.previousRolls, pins, index)
64
+ }
65
+ err = g.Roll(tc.roll)
66
+ if tc.valid && err != nil {
67
+ var _ error = err
68
+ t.Fatalf("FAIL: %s : Roll(%d) after Previous Rolls: %#v got unexpected error \"%s\".",
69
+ tc.description, tc.roll, tc.previousRolls, err)
70
+
71
+ } else if !tc.valid && err == nil {
72
+ t.Fatalf("FAIL: %s : Roll(%d) after Previous Rolls: %#v expected an error.\n\tExplanation: %s",
73
+ tc.description, tc.roll, tc.previousRolls, tc.explainText)
74
+ }
75
+ t.Logf("PASS: %s", tc.description)
76
+ }
77
+ }
@@ -0,0 +1,225 @@
1
+ package bowling
2
+
3
+ // Source: exercism/x-common
4
+ // Commit: 3cf5eb9 bowling: Make canonical-data.json compliant
5
+ // x-common version: 1.0.0
6
+
7
+ var scoreTestCases = []struct {
8
+ description string
9
+ previousRolls []int // bowling rolls to do before the Score() test
10
+ valid bool // true => no error, false => error expected
11
+ score int // when .valid == true, the expected score value
12
+ explainText string // when .valid == false, error explanation text
13
+ }{
14
+ {
15
+ "should be able to score a game with all zeros",
16
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
17
+ true,
18
+ 0,
19
+ "",
20
+ },
21
+ {
22
+ "should be able to score a game with no strikes or spares",
23
+ []int{3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6},
24
+ true,
25
+ 90,
26
+ "",
27
+ },
28
+ {
29
+ "a spare followed by zeros is worth ten points",
30
+ []int{6, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
31
+ true,
32
+ 10,
33
+ "",
34
+ },
35
+ {
36
+ "points scored in the roll after a spare are counted twice",
37
+ []int{6, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
38
+ true,
39
+ 16,
40
+ "",
41
+ },
42
+ {
43
+ "consecutive spares each get a one roll bonus",
44
+ []int{5, 5, 3, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
45
+ true,
46
+ 31,
47
+ "",
48
+ },
49
+ {
50
+ "a spare in the last frame gets a one roll bonus that is counted once",
51
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 7},
52
+ true,
53
+ 17,
54
+ "",
55
+ },
56
+ {
57
+ "a strike earns ten points in a frame with a single roll",
58
+ []int{10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
59
+ true,
60
+ 10,
61
+ "",
62
+ },
63
+ {
64
+ "points scored in the two rolls after a strike are counted twice as a bonus",
65
+ []int{10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
66
+ true,
67
+ 26,
68
+ "",
69
+ },
70
+ {
71
+ "consecutive strikes each get the two roll bonus",
72
+ []int{10, 10, 10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
73
+ true,
74
+ 81,
75
+ "",
76
+ },
77
+ {
78
+ "a strike in the last frame gets a two roll bonus that is counted once",
79
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 1},
80
+ true,
81
+ 18,
82
+ "",
83
+ },
84
+ {
85
+ "rolling a spare with the two roll bonus does not get a bonus roll",
86
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 3},
87
+ true,
88
+ 20,
89
+ "",
90
+ },
91
+ {
92
+ "strikes with the two roll bonus do not get bonus rolls",
93
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10},
94
+ true,
95
+ 30,
96
+ "",
97
+ },
98
+ {
99
+ "a strike with the one roll bonus after a spare in the last frame does not get a bonus",
100
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 10},
101
+ true,
102
+ 20,
103
+ "",
104
+ },
105
+ {
106
+ "all strikes is a perfect game",
107
+ []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
108
+ true,
109
+ 300,
110
+ "",
111
+ },
112
+
113
+ {
114
+ "two bonus rolls after a strike in the last frame can score more than 10 points if one is a strike",
115
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 6},
116
+ true,
117
+ 26,
118
+ "",
119
+ },
120
+
121
+ {
122
+ "an unstarted game can not be scored",
123
+ []int{},
124
+ false,
125
+ 0,
126
+ "Score cannot be taken until the end of the game",
127
+ },
128
+ {
129
+ "an incomplete game can not be scored",
130
+ []int{0, 0},
131
+ false,
132
+ 0,
133
+ "Score cannot be taken until the end of the game",
134
+ },
135
+
136
+ {
137
+ "bonus rolls for a strike in the last frame must be rolled before score can be calculated",
138
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10},
139
+ false,
140
+ 0,
141
+ "Score cannot be taken until the end of the game",
142
+ },
143
+ {
144
+ "both bonus rolls for a strike in the last frame must be rolled before score can be calculated",
145
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10},
146
+ false,
147
+ 0,
148
+ "Score cannot be taken until the end of the game",
149
+ },
150
+ {
151
+ "bonus roll for a spare in the last frame must be rolled before score can be calculated",
152
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3},
153
+ false,
154
+ 0,
155
+ "Score cannot be taken until the end of the game",
156
+ },
157
+ }
158
+
159
+ var rollTestCases = []struct {
160
+ description string
161
+ previousRolls []int // bowling rolls to do before the Roll(roll) test
162
+ valid bool // true => no error, false => error expected
163
+ roll int // pin count for the test roll
164
+ explainText string // when .valid == false, error explanation text
165
+ }{
166
+
167
+ {
168
+ "rolls can not score negative points",
169
+ []int{},
170
+ false,
171
+ -1,
172
+ "Negative roll is invalid",
173
+ },
174
+ {
175
+ "a roll can not score more than 10 points",
176
+ []int{},
177
+ false,
178
+ 11,
179
+ "Pin count exceeds pins on the lane",
180
+ },
181
+ {
182
+ "two rolls in a frame can not score more than 10 points",
183
+ []int{5},
184
+ false,
185
+ 6,
186
+ "Pin count exceeds pins on the lane",
187
+ },
188
+ {
189
+ "bonus roll after a strike in the last frame can not score more than 10 points",
190
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10},
191
+ false,
192
+ 11,
193
+ "Pin count exceeds pins on the lane",
194
+ },
195
+ {
196
+ "two bonus rolls after a strike in the last frame can not score more than 10 points",
197
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5},
198
+ false,
199
+ 6,
200
+ "Pin count exceeds pins on the lane",
201
+ },
202
+
203
+ {
204
+ "the second bonus rolls after a strike in the last frame can not be a strike if the first one is not a strike",
205
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 6},
206
+ false,
207
+ 10,
208
+ "Pin count exceeds pins on the lane",
209
+ },
210
+ {
211
+ "second bonus roll after a strike in the last frame can not score than 10 points",
212
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10},
213
+ false,
214
+ 11,
215
+ "Pin count exceeds pins on the lane",
216
+ },
217
+
218
+ {
219
+ "cannot roll if game already has ten frames",
220
+ []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
221
+ false,
222
+ 0,
223
+ "Cannot roll after game is over",
224
+ },
225
+ }
@@ -0,0 +1,133 @@
1
+ // Package bowling implements scoring for the game of bowling.
2
+ package bowling
3
+
4
+ import "errors"
5
+
6
+ const testVersion = 1
7
+
8
+ var (
9
+ ErrNegativeRollIsInvalid = errors.New("Negative roll is invalid")
10
+ ErrPinCountExceedsPinsOnTheLane = errors.New("Pin count exceeds pins on the lane")
11
+ ErrPrematureScore = errors.New("Score cannot be taken until the end of the game")
12
+ ErrCannotRollAfterGameOver = errors.New("Cannot roll after game is over")
13
+ )
14
+
15
+ const (
16
+ pinsPerFrame = 10
17
+ framesPerGame = 10
18
+ maxRollsPerFrame = 2
19
+ maxRollsLastFrame = 3
20
+ maxRolls = (maxRollsPerFrame * (framesPerGame - 1)) + maxRollsLastFrame
21
+ )
22
+
23
+ // Game records the data to track a game's progress.
24
+ type Game struct {
25
+ rolls [maxRolls]int // storage for the rolls
26
+ nRolls int // counts the rolls accumulated.
27
+ nFrames int // counts completed frames, up to framesPerGame.
28
+ rFrameStart int // tracks the starting roll of each frame.
29
+ }
30
+
31
+ // NewGame returns a fresh zero-valued game struct.
32
+ func NewGame() *Game {
33
+ return &Game{}
34
+ }
35
+
36
+ // Roll records one roll for a bowling frame with 'pins' knocked down.
37
+ // An error is possible depending on pin value and previous rolls.
38
+ func (g *Game) Roll(pins int) error {
39
+ // Validate pin count on roll.
40
+ if pins > pinsPerFrame {
41
+ return ErrPinCountExceedsPinsOnTheLane
42
+ }
43
+ if pins < 0 {
44
+ return ErrNegativeRollIsInvalid
45
+ }
46
+ if g.completedFrames() == framesPerGame {
47
+ return ErrCannotRollAfterGameOver
48
+ }
49
+ // Record the roll.
50
+ g.rolls[g.nRolls] = pins
51
+ g.nRolls++
52
+ if pins == pinsPerFrame && g.completedFrames() < framesPerGame-1 {
53
+ // Frames before last one can be strikes with no problems.
54
+ g.completeTheFrame()
55
+ return nil
56
+ }
57
+ if g.rollsThisFrame() == maxRollsPerFrame {
58
+ // Have counted normal max rolls on a frame.
59
+ if g.rawFrameScore(g.rFrameStart) > pinsPerFrame {
60
+ // Unless we have completed all but last frame, cannot count > pinsPerFrame.
61
+ if g.completedFrames() != framesPerGame-1 || !g.isStrike(g.rFrameStart) {
62
+ return ErrPinCountExceedsPinsOnTheLane
63
+ }
64
+ }
65
+ if g.completedFrames() < framesPerGame-1 {
66
+ // Completed frames before last one with maxRollsPerFrame.
67
+ g.completeTheFrame()
68
+ return nil
69
+ }
70
+ // For last frame, is it complete now ?
71
+ if g.rawFrameScore(g.rFrameStart) < pinsPerFrame {
72
+ // Yes, complete.
73
+ g.completeTheFrame()
74
+ }
75
+ } else if g.rollsThisFrame() == maxRollsLastFrame {
76
+ // Extra roll on the last frame.
77
+ if g.isStrike(g.rFrameStart) {
78
+ // First was a strike.
79
+ if !g.isStrike(g.rFrameStart + 1) {
80
+ // Second was NOT a strike, so last 2 rolls cannot exceed pinsPerFrame.
81
+ if g.strikeBonus(g.rFrameStart) > pinsPerFrame {
82
+ return ErrPinCountExceedsPinsOnTheLane
83
+ }
84
+ }
85
+ if b := g.strikeBonus(g.rFrameStart); b > pinsPerFrame && b < 2*pinsPerFrame {
86
+ // Unless one of the bonuses was a strike, bonus frames too high.
87
+ if !g.isStrike(g.rFrameStart+1) && !g.isStrike(g.rFrameStart+2) {
88
+ return ErrPinCountExceedsPinsOnTheLane
89
+ }
90
+ }
91
+ } else if !g.isSpare(g.rFrameStart) {
92
+ // Attempt to make extra roll in last frame without strike or spare.
93
+ return ErrCannotRollAfterGameOver
94
+ }
95
+ // Completed last frame.
96
+ g.completeTheFrame()
97
+ }
98
+
99
+ return nil
100
+ }
101
+
102
+ // Score returns the score of the game with a potential error.
103
+ func (g *Game) Score() (int, error) {
104
+ if g.completedFrames() != framesPerGame {
105
+ return 0, ErrPrematureScore
106
+ }
107
+
108
+ score := 0
109
+ frameStart := 0
110
+
111
+ for frame := 0; frame < framesPerGame; frame++ {
112
+ if g.isStrike(frameStart) {
113
+ score += pinsPerFrame + g.strikeBonus(frameStart)
114
+ frameStart++
115
+ } else if g.isSpare(frameStart) {
116
+ score += pinsPerFrame + g.spareBonus(frameStart)
117
+ frameStart += maxRollsPerFrame
118
+ } else {
119
+ score += g.rawFrameScore(frameStart)
120
+ frameStart += maxRollsPerFrame
121
+ }
122
+ }
123
+ return score, nil
124
+ }
125
+
126
+ func (g *Game) rollsThisFrame() int { return g.nRolls - g.rFrameStart }
127
+ func (g *Game) completeTheFrame() { g.nFrames++; g.rFrameStart = g.nRolls }
128
+ func (g *Game) completedFrames() int { return g.nFrames }
129
+ func (g *Game) isStrike(f int) bool { return g.rolls[f] == pinsPerFrame }
130
+ func (g *Game) rawFrameScore(f int) int { return g.rolls[f] + g.rolls[f+1] }
131
+ func (g *Game) spareBonus(f int) int { return g.rolls[f+2] }
132
+ func (g *Game) strikeBonus(f int) int { return g.rolls[f+1] + g.rolls[f+2] }
133
+ func (g *Game) isSpare(f int) bool { return (g.rolls[f] + g.rolls[f+1]) == pinsPerFrame }