trackler 2.0.8.38 → 2.0.8.39

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: 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 }