Finish for loop example

Also add new projects; should have been 2 commits but oh well
This commit is contained in:
Blizzard Finnegan 2025-03-09 15:18:46 -04:00
parent a5c4a9970e
commit c1dfa3dd9d
Signed by: blizzardfinnegan
GPG key ID: 61C1E13067E0018E
33 changed files with 1450 additions and 0 deletions

View file

@ -0,0 +1,27 @@
{
"authors": [
"norbs57"
],
"contributors": [
"junedev"
],
"files": {
"solution": [
"animal_magic.go"
],
"test": [
"animal_magic_test.go"
],
"exemplar": [
".meta/exemplar.go"
],
"invalidator": [
"go.mod"
]
},
"icon": "roll-the-die",
"blurb": "Learn how to work with pseudo-random numbers",
"custom": {
"taskIdsEnabled": true
}
}

View file

@ -0,0 +1 @@
{"track":"go","exercise":"animal-magic","id":"8e4b4f22a6b14b5b8e6f4cce16f324df","url":"https://exercism.org/tracks/go/exercises/animal-magic","handle":"snowyforest","is_requester":true,"auto_approve":false}

41
animal-magic/HELP.md Normal file
View file

@ -0,0 +1,41 @@
# Help
## Running the tests
To run the tests run the command `go test` from within the exercise directory.
If the test suite contains benchmarks, you can run these with the `--bench` and `--benchmem`
flags:
go test -v --bench . --benchmem
Keep in mind that each reviewer will run benchmarks on a different machine, with
different specs, so the results from these benchmark tests may vary.
## Submitting your solution
You can submit your solution using the `exercism submit animal_magic.go` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Go track's documentation](https://exercism.org/docs/tracks/go)
- The [Go track's programming category on the forum](https://forum.exercism.org/c/programming/go)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
To get help if you're having trouble, you can use one of the following resources:
- [How to Write Go Code](https://golang.org/doc/code.html)
- [Effective Go](https://golang.org/doc/effective_go.html)
- [Go Resources](http://golang.org/help)
- [StackOverflow](http://stackoverflow.com/questions/tagged/go)

24
animal-magic/HINTS.md Normal file
View file

@ -0,0 +1,24 @@
# Hints
## General
This [article][gobyexample] gives a nice introduction to package `math/rand`.
If you are working locally and use Go 1.19 or lower, make sure to seed your random number generator with the current time.
## 1. Roll a die.
This [article][yourbasic] shows how to generate integers in a certain range.
## 2. Generate wand energy.
Function [rand.Float64][float64] returns a random `float64` number between 0.0 and 1.0.
## 3. Shuffle a slice.
Create a slice with the eight animal strings, then call [rand.Shuffle][shuffle] to put it into a random order.
[gobyexample]: https://gobyexample.com/random-numbers
[yourbasic]: https://yourbasic.org/golang/generate-number-random-range
[shuffle]: https://pkg.go.dev/math/rand#Rand.Shuffle
[float64]: https://pkg.go.dev/math/rand#Float64

96
animal-magic/README.md Normal file
View file

@ -0,0 +1,96 @@
# Animal Magic
Welcome to Animal Magic on Exercism's Go Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
## Introduction
Package [math/rand][mathrand] provides support for generating pseudo-random numbers.
Here is how to generate a random integer between `0` and `99`:
```go
n := rand.Intn(100) // n is a random int, 0 <= n < 100
```
Function `rand.Float64` returns a random floating point number between `0.0` and `1.0`:
```go
f := rand.Float64() // f is a random float64, 0.0 <= f < 1.0
```
There is also support for shuffling a slice (or other data structures):
```go
x := []string{"a", "b", "c", "d", "e"}
// shuffling the slice put its elements into a random order
rand.Shuffle(len(x), func(i, j int) {
x[i], x[j] = x[j], x[i]
})
```
## Seeds
The number sequences generated by package `math/rand` are not truly random.
Given a specific "seed" value, the results are entirely deterministic.
In Go 1.20+ the seed is automatically picked at random so you will see a different sequence of random numbers each time you run your program.
In prior versions of Go, the seed was `1` by default.
So to get different sequences for various runs of the program, you had to manually seed the random number generator, e.g. with the current time, before retrieving any random numbers.
```go
rand.Seed(time.Now().UnixNano())
```
[mathrand]: https://pkg.go.dev/math/rand
## Instructions
Elaine is working on a new children's game that features animals and magic wands.
It is time to code functions for rolling a die, generating random wand energy and shuffling a slice.
## 1. Roll a die.
Implement a `RollADie` function.
This will be the traditional twenty-sided die with numbers 1 to 20.
```go
d := RollADie() // d will be assigned a random int, 1 <= d <= 20
```
## 2. Generate wand energy.
Implement a `GenerateWandEnergy` function.
The wand energy should be a random floating point number equal or greater than 0.0 and less than 12.0.
```go
f := GenerateWandEnergy() // f will be assigned a random float64, 0.0 <= f < 12.0
```
## 3. Shuffle a slice.
The game features eight different animals:
- ant
- beaver
- cat
- dog
- elephant
- fox
- giraffe
- hedgehog
Write a function `ShuffleAnimals` that returns a slice with the eight animals in random order.
## Source
### Created by
- @norbs57
### Contributed to by
- @junedev

View file

@ -0,0 +1,16 @@
package chance
// RollADie returns a random int d with 1 <= d <= 20.
func RollADie() int {
panic("Please implement the RollADie function")
}
// GenerateWandEnergy returns a random float64 f with 0.0 <= f < 12.0.
func GenerateWandEnergy() float64 {
panic("Please implement the GenerateWandEnergy function")
}
// ShuffleAnimals returns a slice with all eight animal strings in random order.
func ShuffleAnimals() []string {
panic("Please implement the ShuffleAnimals function")
}

View file

@ -0,0 +1,103 @@
//nolint:gosec // In the context of this exercise, it is fine to use math.Rand instead of crypto.Rand.
package chance
import (
"sort"
"testing"
)
func TestRollADie(t *testing.T) {
const tests = 100
var got int
foundDifferent := false
var last int
for i := 0; i < tests; i++ {
got = RollADie()
if got < 1 || got > 20 {
t.Fatalf("RollADie() out of range: %d", got)
}
if i > 0 && got != last {
foundDifferent = true
}
last = got
}
if !foundDifferent {
t.Errorf("RollADie() always generates the same number: %d", got)
}
}
func TestWandEnergy(t *testing.T) {
const tests = 200
const bucketSize float64 = 0.6
var got float64
foundDifferent := false
var last float64
numBuckets := int(12.0 / bucketSize)
buckets := make([]int, numBuckets)
for i := 0; i < tests; i++ {
got = GenerateWandEnergy()
if got < 0.0 || got >= 12.0 {
t.Fatalf("GenerateWandEnergy() out of range: %f", got)
}
if i > 0 && got != last {
foundDifferent = true
}
buckets[int(got/bucketSize)]++
last = got
}
if !foundDifferent {
t.Fatalf("GenerateWandEnergy() always generates the same number: %f", got)
}
var low, high float64
for i, v := range buckets {
if v == 0 {
low = float64(i) * bucketSize
high = float64(i+1) * bucketSize
break
}
}
if high != 0.0 {
t.Errorf("GenerateWandEnergy() results are not uniformly distributed. %.2f to %.2f should contain values.", low, high)
}
}
func TestShuffleAnimals(t *testing.T) {
const tests = 100
animals := []string{"ant", "beaver", "cat", "dog", "elephant", "fox", "giraffe", "hedgehog"}
foundDifferent := false
var last []string
var got []string
for i := 0; i < tests; i++ {
got = ShuffleAnimals()
gotSorted := make([]string, len(got))
copy(gotSorted, got)
sort.Strings(gotSorted)
if !slicesEqual(gotSorted, animals) {
t.Fatalf("ShuffleAnimals() returns incorrect slice: %v", got)
}
if i > 0 && !foundDifferent && !slicesEqual(last, got) {
foundDifferent = true
}
last = got
}
if !foundDifferent {
t.Errorf("ShuffleAnimals() always generates the same slice: %v", got)
}
}
func slicesEqual(a, b []string) bool {
if len(a) != len(b) {
return false
}
if len(a) == 0 {
return true
}
size := len(a)
for i := 0; i < size; i++ {
if a[i] != b[i] {
return false
}
}
return true
}

3
animal-magic/go.mod Normal file
View file

@ -0,0 +1,3 @@
module chance
go 1.18

View file

@ -0,0 +1,29 @@
{
"authors": [
"sachinmk27"
],
"contributors": [
"andrerfcsantos"
],
"files": {
"solution": [
"bird_watcher.go"
],
"test": [
"bird_watcher_test.go"
],
"exemplar": [
".meta/exemplar.go"
],
"invalidator": [
"go.mod"
]
},
"forked_from": [
"javascript/bird-watcher"
],
"blurb": "Count the birds in your garden with for loops.",
"custom": {
"taskIdsEnabled": true
}
}

View file

@ -0,0 +1 @@
{"track":"go","exercise":"bird-watcher","id":"653e6b56965346b5be73fe5f6985f4e8","url":"https://exercism.org/tracks/go/exercises/bird-watcher","handle":"snowyforest","is_requester":true,"auto_approve":false}

41
bird-watcher/HELP.md Normal file
View file

@ -0,0 +1,41 @@
# Help
## Running the tests
To run the tests run the command `go test` from within the exercise directory.
If the test suite contains benchmarks, you can run these with the `--bench` and `--benchmem`
flags:
go test -v --bench . --benchmem
Keep in mind that each reviewer will run benchmarks on a different machine, with
different specs, so the results from these benchmark tests may vary.
## Submitting your solution
You can submit your solution using the `exercism submit bird_watcher.go` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Go track's documentation](https://exercism.org/docs/tracks/go)
- The [Go track's programming category on the forum](https://forum.exercism.org/c/programming/go)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
To get help if you're having trouble, you can use one of the following resources:
- [How to Write Go Code](https://golang.org/doc/code.html)
- [Effective Go](https://golang.org/doc/effective_go.html)
- [Go Resources](http://golang.org/help)
- [StackOverflow](http://stackoverflow.com/questions/tagged/go)

21
bird-watcher/HINTS.md Normal file
View file

@ -0,0 +1,21 @@
# Hints
## 1. Determine the total number of birds that you counted so far
- Refer to the exercise introduction for an example how to use a for loop to iterate over a slice.
- Use a helper variable to store the total count and increase that variable as you go through the slice.
- Think about the correct initial value for that helper variable.
## 2. Calculate the number of visiting birds in a specific week
- This task is similar to the first one, you can copy your code as a starting point.
- Think about which indexes in the slice you would need to take into account for week number 1 and 2, respectively.
- Now find a general way to calculate the first and the last index that should be considered.
- With that you can set up the for loop to only iterate over the relevant section of the slice.
## 3. Fix a counting mistake
- Again you need to set up a for loop to iterate over the whole bird count slice.
- This time you only need to visit every second entry in the slice.
- Change the step so the counter variable is increased accordingly after each iteration.
- In the body of the for loop you can use the increment operator to change the value of an element in a slice in-place.

98
bird-watcher/README.md Normal file
View file

@ -0,0 +1,98 @@
# Bird Watcher
Welcome to Bird Watcher on Exercism's Go Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
## Introduction
## General syntax
The for loop is one of the most commonly used statements to repeatedly execute some logic. In Go it consists of the `for` keyword, a header and a code block that contains the body of the loop wrapped in curly brackets. The header consists of 3 components separated by semicolons `;`: init, condition and post.
```go
for init; condition; post {
// loop body - code that is executed repeatedly as long as the condition is true
}
```
- The **init** component is some code that runs only once before the loop starts.
- The **condition** component must be some expression that evaluates to a boolean and controls when the loop should stop. The code inside the loop will run as long as this condition evaluates to true. As soon as this expression evaluates to false, no more iterations of the loop will run.
- The **post** component is some code that will run at the end of each iteration.
**Note:** Unlike other languages, there are no parentheses `()` surrounding the three components of the header. In fact, inserting such parenthesis is a compilation error. However, the braces `{ }` surrounding the loop body are always required.
## For Loops - An example
The init component usually sets up a counter variable, the condition checks whether the loop should be continued or stopped and the post component usually increments the counter at the end of each repetition.
```go
for i := 1; i < 10; i++ {
fmt.Println(i)
}
```
This loop will print the numbers from `1` to `9` (including `9`).
Defining the step is often done using an increment or decrement statement, as shown in the example above.
## Instructions
You are an avid bird watcher that keeps track of how many birds have visited your garden.
Usually you use a tally in a notebook to count the birds, but to better work with your data, you've digitalized the bird counts for the past weeks.
## 1. Determine the total number of birds that you counted so far
Let us start analyzing the data by getting a high level view.
Find out how many birds you counted in total since you started your logs.
Implement a function `TotalBirdCount` that accepts a slice of `int`s that contains the bird count per day.
It should return the total number of birds that you counted.
```go
birdsPerDay := []int{2, 5, 0, 7, 4, 1, 3, 0, 2, 5, 0, 1, 3, 1}
TotalBirdCount(birdsPerDay)
// => 34
```
## 2. Calculate the number of visiting birds in a specific week
Now that you got a general feel for your bird count numbers, you want to make a more fine-grained analysis.
Implement a function `BirdsInWeek` that accepts a slice of bird counts per day and a week number.
It returns the total number of birds that you counted in that specific week.
You can assume weeks are always tracked completely.
```go
birdsPerDay := []int{2, 5, 0, 7, 4, 1, 3, 0, 2, 5, 0, 1, 3, 1}
BirdsInWeek(birdsPerDay, 2)
// => 12
```
## 3. Fix a counting mistake
You realized that all the time you were trying to keep track of the birds, there was one bird that was hiding in a far corner of the garden.
You figured out that this bird always spent every second day in your garden.
You do not know exactly where it was in between those days but definitely not in your garden.
Your bird watcher intuition also tells you that the bird was in your garden on the first day that you tracked in your list.
Given this new information, write a function `FixBirdCountLog` that takes a slice of birds counted per day as an argument and returns the slice after correcting the counting mistake.
```go
birdsPerDay := []int{2, 5, 0, 7, 4, 1}
FixBirdCountLog(birdsPerDay)
// => [3 5 1 7 5 1]
```
## Source
### Created by
- @sachinmk27
### Contributed to by
- @andrerfcsantos

View file

@ -0,0 +1,26 @@
package birdwatcher
// TotalBirdCount return the total bird count by summing
// the individual day's counts.
func TotalBirdCount(birdsPerDay []int) int {
var temp = 0
for i := 0; i < len(birdsPerDay); i++{
temp += birdsPerDay[i]
}
return temp
}
// BirdsInWeek returns the total bird count by summing
// only the items belonging to the given week.
func BirdsInWeek(birdsPerDay []int, week int) int {
return TotalBirdCount(birdsPerDay[( week-1 )*7:week*7])
}
// FixBirdCountLog returns the bird counts after correcting
// the bird counts for alternate days.
func FixBirdCountLog(birdsPerDay []int) []int {
for i := 0; i < len(birdsPerDay); i+=2{
birdsPerDay[i] += 1
}
return birdsPerDay
}

View file

@ -0,0 +1,128 @@
package birdwatcher
import (
"reflect"
"testing"
)
// testRunnerTaskID=1
func TestTotalBirdCount(t *testing.T) {
tests := []struct {
name string
birdCounts []int
want int
}{
{
name: "calculates the correct total number of birds",
birdCounts: []int{9, 0, 8, 4, 5, 1, 3},
want: 30,
},
{
name: "works for a short bird count list",
birdCounts: []int{2},
want: 2,
},
{
name: "works for a long bird count list",
birdCounts: []int{2, 8, 4, 1, 3, 5, 0, 4, 1, 6, 0, 3, 0, 1, 5, 4, 1, 1, 2, 6},
want: 57,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := TotalBirdCount(tt.birdCounts); got != tt.want {
t.Errorf("TotalBirdCount(%v) = %v; want %v", tt.birdCounts, got, tt.want)
}
})
}
}
// testRunnerTaskID=2
func TestBirdsInWeek(t *testing.T) {
tests := []struct {
name string
birdCounts []int
week int
want int
}{
{
name: "calculates the number of birds in the first week",
birdCounts: []int{3, 0, 5, 1, 0, 4, 1, 0, 3, 4, 3, 0, 8, 0},
week: 1,
want: 14,
},
{
name: "calculates the number of birds for a week in the middle of the log",
birdCounts: []int{4, 7, 3, 2, 1, 1, 2, 0, 2, 3, 2, 7, 1, 3, 0, 6, 5, 3, 7, 2, 3},
week: 2,
want: 18,
},
{
name: "calculates the number of birds for a week in the end of the log",
birdCounts: []int{4, 7, 3, 2, 1, 1, 2, 0, 2, 3, 2, 7, 1, 3, 0, 6, 5, 3, 7, 2, 3},
week: 3,
want: 26,
},
{
name: "works when there is only one week",
birdCounts: []int{3, 0, 3, 3, 2, 1, 0},
week: 1,
want: 12,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := BirdsInWeek(tt.birdCounts, tt.week); got != tt.want {
t.Errorf("BirdsInWeek(%v) = %v; want %v", tt.birdCounts, got, tt.want)
}
})
}
}
// testRunnerTaskID=3
func TestFixBirdCount(t *testing.T) {
tests := []struct {
name string
birdCounts []int
week int
want []int
}{
{
name: "returns a bird count list with the corrected values",
birdCounts: []int{3, 0, 5, 1, 0, 4, 1, 0, 3, 4, 3, 0},
want: []int{4, 0, 6, 1, 1, 4, 2, 0, 4, 4, 4, 0},
},
{
name: "works for a short bird count list",
birdCounts: []int{4, 2},
want: []int{5, 2},
},
{
name: "works for a long bird count list",
birdCounts: []int{2, 8, 4, 1, 3, 5, 0, 4, 1, 6, 0, 3, 0, 1, 5, 4, 1, 1, 2, 6},
want: []int{3, 8, 5, 1, 4, 5, 1, 4, 2, 6, 1, 3, 1, 1, 6, 4, 2, 1, 3, 6},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
input := make([]int, len(tt.birdCounts))
copy(input, tt.birdCounts)
if got := FixBirdCountLog(tt.birdCounts); !reflect.DeepEqual(tt.want, got) {
t.Errorf("FixBirdCountLog(%v) = %v; want %v", input, got, tt.want)
}
})
}
}
// testRunnerTaskID=3
func TestFixBirdCountDoesNotCreateNewSlice(t *testing.T) {
counts := []int{4, 0, 6, 1, 1, 4, 2, 0, 4, 4, 4, 0}
got := FixBirdCountLog(counts)
if reflect.ValueOf(got).Pointer() != reflect.ValueOf(counts).Pointer() {
t.Error("it looks like that you are creating a new slice in the function FixBirdCountLog - " +
"please make sure you are modifying the slice passed as argument")
}
}

3
bird-watcher/go.mod Normal file
View file

@ -0,0 +1,3 @@
module birdwatcher
go 1.18

View file

@ -0,0 +1,31 @@
{
"blurb": "Score a bowling game",
"authors": [
"leenipper"
],
"contributors": [
"alebaffa",
"bitfield",
"ekingery",
"ferhatelmas",
"hilary",
"robphoenix",
"sebito91"
],
"files": {
"solution": [
"bowling.go"
],
"test": [
"bowling_test.go"
],
"example": [
".meta/example.go"
],
"editor": [
"cases_test.go"
]
},
"source": "The Bowling Game Kata at but UncleBob",
"source_url": "http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata"
}

View file

@ -0,0 +1 @@
{"track":"go","exercise":"bowling","id":"2c5a26bebd194100881e4b942e520e1c","url":"https://exercism.org/tracks/go/exercises/bowling","handle":"snowyforest","is_requester":true,"auto_approve":false}

41
bowling/HELP.md Normal file
View file

@ -0,0 +1,41 @@
# Help
## Running the tests
To run the tests run the command `go test` from within the exercise directory.
If the test suite contains benchmarks, you can run these with the `--bench` and `--benchmem`
flags:
go test -v --bench . --benchmem
Keep in mind that each reviewer will run benchmarks on a different machine, with
different specs, so the results from these benchmark tests may vary.
## Submitting your solution
You can submit your solution using the `exercism submit bowling.go` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Go track's documentation](https://exercism.org/docs/tracks/go)
- The [Go track's programming category on the forum](https://forum.exercism.org/c/programming/go)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
To get help if you're having trouble, you can use one of the following resources:
- [How to Write Go Code](https://golang.org/doc/code.html)
- [Effective Go](https://golang.org/doc/effective_go.html)
- [Go Resources](http://golang.org/help)
- [StackOverflow](http://stackoverflow.com/questions/tagged/go)

86
bowling/README.md Normal file
View file

@ -0,0 +1,86 @@
# Bowling
Welcome to Bowling on Exercism's Go Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
Score a bowling game.
Bowling is a game where players roll a heavy ball to knock down pins
arranged in a triangle. Write code to keep track of the score
of a game of bowling.
## Scoring Bowling
The game consists of 10 frames. A frame is composed of one or two ball
throws with 10 pins standing at frame initialization. There are three
cases for the tabulation of a frame.
* An open frame is where a score of less than 10 is recorded for the
frame. In this case the score for the frame is the number of pins
knocked down.
* A spare is where all ten pins are knocked down by the second
throw. The total value of a spare is 10 plus the number of pins
knocked down in their next throw.
* A strike is where all ten pins are knocked down by the first
throw. The total value of a strike is 10 plus the number of pins
knocked down in the next two throws. If a strike is immediately
followed by a second strike, then the value of the first strike
cannot be determined until the ball is thrown one more time.
Here is a three frame example:
| Frame 1 | Frame 2 | Frame 3 |
| :-------------: |:-------------:| :---------------------:|
| X (strike) | 5/ (spare) | 9 0 (open frame) |
Frame 1 is (10 + 5 + 5) = 20
Frame 2 is (5 + 5 + 9) = 19
Frame 3 is (9 + 0) = 9
This means the current running total is 48.
The tenth frame in the game is a special case. If someone throws a
strike or a spare then they get a fill ball. Fill balls exist to
calculate the total of the 10th frame. Scoring a strike or spare on
the fill ball does not give the player more fill balls. The total
value of the 10th frame is the total number of pins knocked down.
For a tenth frame of X1/ (strike and a spare), the total value is 20.
For a tenth frame of XXX (three strikes), the total value is 30.
## Requirements
Write code to keep track of the score of a game of bowling. It should
support two operations:
* `roll(pins : int)` is called each time the player rolls a ball. The
argument is the number of pins knocked down.
* `score() : int` is called only at the very end of the game. It
returns the total score for that game.
## Source
### Created by
- @leenipper
### Contributed to by
- @alebaffa
- @bitfield
- @ekingery
- @ferhatelmas
- @hilary
- @robphoenix
- @sebito91
### Based on
The Bowling Game Kata at but UncleBob - http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata

15
bowling/bowling.go Normal file
View file

@ -0,0 +1,15 @@
package bowling
// Define the Game type here.
func NewGame() *Game {
panic("Please implement the NewGame function")
}
func (g *Game) Roll(pins int) error {
panic("Please implement the Roll function")
}
func (g *Game) Score() (int, error) {
panic("Please implement the Score function")
}

66
bowling/bowling_test.go Normal file
View file

@ -0,0 +1,66 @@
package bowling
import "testing"
const previousRollErrorMessage = `FAIL: %s
Unexpected error occurred: %q
while applying the previous rolls for the
test case: %v
The error was returned from Roll(%d) for previousRolls[%d].`
func applyPreviousRolls(g *Game, rolls []int) (index, pins int, err error) {
for index, pins := range rolls {
if err := g.Roll(pins); err != nil {
return index, pins, err
}
}
return 0, 0, nil
}
func TestScore(t *testing.T) {
for _, tc := range scoreTestCases {
g := NewGame()
index, pins, err := applyPreviousRolls(g, tc.previousRolls)
if err != nil {
t.Fatalf(previousRollErrorMessage,
tc.description, err, tc.previousRolls, pins, index)
}
score, err := g.Score()
if tc.valid {
var _ error = err
if err != nil {
t.Fatalf("FAIL: %s : Score() after Previous Rolls: %#v expected %d, got error %s",
tc.description, tc.previousRolls, tc.score, err)
} else if score != tc.score {
t.Fatalf("%s : Score() after Previous Rolls: %#v expected %d, got %d",
tc.description, tc.previousRolls, tc.score, score)
}
} else if err == nil {
t.Fatalf("FAIL: %s : Score() after Previous Rolls: %#v expected an error, got score %d\n\tExplanation: %s",
tc.description, tc.previousRolls, score, tc.explainText)
}
t.Logf("PASS: %s", tc.description)
}
}
func TestRoll(t *testing.T) {
for _, tc := range rollTestCases {
g := NewGame()
index, pins, err := applyPreviousRolls(g, tc.previousRolls)
if err != nil {
t.Fatalf(previousRollErrorMessage,
tc.description, err, tc.previousRolls, pins, index)
}
err = g.Roll(tc.roll)
if tc.valid && err != nil {
var _ error = err
t.Fatalf("FAIL: %s : Roll(%d) after Previous Rolls: %#v got unexpected error \"%s\".",
tc.description, tc.roll, tc.previousRolls, err)
} else if !tc.valid && err == nil {
t.Fatalf("FAIL: %s : Roll(%d) after Previous Rolls: %#v expected an error.\n\tExplanation: %s",
tc.description, tc.roll, tc.previousRolls, tc.explainText)
}
t.Logf("PASS: %s", tc.description)
}
}

240
bowling/cases_test.go Normal file
View file

@ -0,0 +1,240 @@
package bowling
// Source: exercism/problem-specifications
// Commit: 1806718 bowling: add tests for rolling after bonus rolls
// Problem Specifications Version: 1.2.0
var scoreTestCases = []struct {
description string
previousRolls []int // bowling rolls to do before the Score() test
valid bool // true => no error, false => error expected
score int // when .valid == true, the expected score value
explainText string // when .valid == false, error explanation text
}{
{
"should be able to score a game with all zeros",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
true,
0,
"",
},
{
"should be able to score a game with no strikes or spares",
[]int{3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6},
true,
90,
"",
},
{
"a spare followed by zeros is worth ten points",
[]int{6, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
true,
10,
"",
},
{
"points scored in the roll after a spare are counted twice",
[]int{6, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
true,
16,
"",
},
{
"consecutive spares each get a one roll bonus",
[]int{5, 5, 3, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
true,
31,
"",
},
{
"a spare in the last frame gets a one roll bonus that is counted once",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 7},
true,
17,
"",
},
{
"a strike earns ten points in a frame with a single roll",
[]int{10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
true,
10,
"",
},
{
"points scored in the two rolls after a strike are counted twice as a bonus",
[]int{10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
true,
26,
"",
},
{
"consecutive strikes each get the two roll bonus",
[]int{10, 10, 10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
true,
81,
"",
},
{
"a strike in the last frame gets a two roll bonus that is counted once",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 1},
true,
18,
"",
},
{
"rolling a spare with the two roll bonus does not get a bonus roll",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 3},
true,
20,
"",
},
{
"strikes with the two roll bonus do not get bonus rolls",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10},
true,
30,
"",
},
{
"a strike with the one roll bonus after a spare in the last frame does not get a bonus",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 10},
true,
20,
"",
},
{
"all strikes is a perfect game",
[]int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
true,
300,
"",
},
{
"two bonus rolls after a strike in the last frame can score more than 10 points if one is a strike",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 6},
true,
26,
"",
},
{
"an unstarted game cannot be scored",
[]int{},
false,
0,
"Score cannot be taken until the end of the game",
},
{
"an incomplete game cannot be scored",
[]int{0, 0},
false,
0,
"Score cannot be taken until the end of the game",
},
{
"bonus rolls for a strike in the last frame must be rolled before score can be calculated",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10},
false,
0,
"Score cannot be taken until the end of the game",
},
{
"both bonus rolls for a strike in the last frame must be rolled before score can be calculated",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10},
false,
0,
"Score cannot be taken until the end of the game",
},
{
"bonus roll for a spare in the last frame must be rolled before score can be calculated",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3},
false,
0,
"Score cannot be taken until the end of the game",
},
}
var rollTestCases = []struct {
description string
previousRolls []int // bowling rolls to do before the Roll(roll) test
valid bool // true => no error, false => error expected
roll int // pin count for the test roll
explainText string // when .valid == false, error explanation text
}{
{
"rolls cannot score negative points",
[]int{},
false,
-1,
"Negative roll is invalid",
},
{
"a roll cannot score more than 10 points",
[]int{},
false,
11,
"Pin count exceeds pins on the lane",
},
{
"two rolls in a frame cannot score more than 10 points",
[]int{5},
false,
6,
"Pin count exceeds pins on the lane",
},
{
"bonus roll after a strike in the last frame cannot score more than 10 points",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10},
false,
11,
"Pin count exceeds pins on the lane",
},
{
"two bonus rolls after a strike in the last frame cannot score more than 10 points",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5},
false,
6,
"Pin count exceeds pins on the lane",
},
{
"the second bonus rolls after a strike in the last frame cannot be a strike if the first one is not a strike",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 6},
false,
10,
"Pin count exceeds pins on the lane",
},
{
"second bonus roll after a strike in the last frame cannot score more than 10 points",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10},
false,
11,
"Pin count exceeds pins on the lane",
},
{
"cannot roll if game already has ten frames",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
false,
0,
"Cannot roll after game is over",
},
{
"cannot roll after bonus roll for spare",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 2},
false,
2,
"Cannot roll after game is over",
},
{
"cannot roll after bonus rolls for strike",
[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 3, 2},
false,
2,
"Cannot roll after game is over",
},
}

3
bowling/go.mod Normal file
View file

@ -0,0 +1,3 @@
module bowling
go 1.16

View file

@ -0,0 +1,30 @@
{
"blurb": "Given two buckets of different size, demonstrate how to measure an exact number of liters.",
"authors": [
"leenipper"
],
"contributors": [
"bitfield",
"ekingery",
"ferhatelmas",
"hilary",
"sebito91",
"tleen"
],
"files": {
"solution": [
"two_bucket.go"
],
"test": [
"two_bucket_test.go"
],
"example": [
".meta/example.go"
],
"editor": [
"cases_test.go"
]
},
"source": "Water Pouring Problem",
"source_url": "http://demonstrations.wolfram.com/WaterPouringProblem/"
}

View file

@ -0,0 +1 @@
{"track":"go","exercise":"two-bucket","id":"5b22d3e4e9774ef7a1eaac166c5d30da","url":"https://exercism.org/tracks/go/exercises/two-bucket","handle":"snowyforest","is_requester":true,"auto_approve":false}

41
two-bucket/HELP.md Normal file
View file

@ -0,0 +1,41 @@
# Help
## Running the tests
To run the tests run the command `go test` from within the exercise directory.
If the test suite contains benchmarks, you can run these with the `--bench` and `--benchmem`
flags:
go test -v --bench . --benchmem
Keep in mind that each reviewer will run benchmarks on a different machine, with
different specs, so the results from these benchmark tests may vary.
## Submitting your solution
You can submit your solution using the `exercism submit two_bucket.go` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Go track's documentation](https://exercism.org/docs/tracks/go)
- The [Go track's programming category on the forum](https://forum.exercism.org/c/programming/go)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
To get help if you're having trouble, you can use one of the following resources:
- [How to Write Go Code](https://golang.org/doc/code.html)
- [Effective Go](https://golang.org/doc/effective_go.html)
- [Go Resources](http://golang.org/help)
- [StackOverflow](http://stackoverflow.com/questions/tagged/go)

96
two-bucket/README.md Normal file
View file

@ -0,0 +1,96 @@
# Two Bucket
Welcome to Two Bucket on Exercism's Go Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
Given two buckets of different size and which bucket to fill first, determine how many actions are required to measure an exact number of liters by strategically transferring fluid between the buckets.
There are some rules that your solution must follow:
- You can only do one action at a time.
- There are only 3 possible actions:
1. Pouring one bucket into the other bucket until either:
a) the first bucket is empty
b) the second bucket is full
2. Emptying a bucket and doing nothing to the other.
3. Filling a bucket and doing nothing to the other.
- After an action, you may not arrive at a state where the starting bucket is empty and the other bucket is full.
Your program will take as input:
- the size of bucket one
- the size of bucket two
- the desired number of liters to reach
- which bucket to fill first, either bucket one or bucket two
Your program should determine:
- the total number of actions it should take to reach the desired number of liters, including the first fill of the starting bucket
- which bucket should end up with the desired number of liters - either bucket one or bucket two
- how many liters are left in the other bucket
Note: any time a change is made to either or both buckets counts as one (1) action.
Example:
Bucket one can hold up to 7 liters, and bucket two can hold up to 11 liters. Let's say at a given step, bucket one is holding 7 liters and bucket two is holding 8 liters (7,8). If you empty bucket one and make no change to bucket two, leaving you with 0 liters and 8 liters respectively (0,8), that counts as one action. Instead, if you had poured from bucket one into bucket two until bucket two was full, resulting in 4 liters in bucket one and 11 liters in bucket two (4,11), that would also only count as one action.
Another Example:
Bucket one can hold 3 liters, and bucket two can hold up to 5 liters. You are told you must start with bucket one. So your first action is to fill bucket one. You choose to empty bucket one for your second action. For your third action, you may not fill bucket two, because this violates the third rule -- you may not end up in a state after any action where the starting bucket is empty and the other bucket is full.
Written with <3 at [Fullstack Academy](http://www.fullstackacademy.com/) by Lindsay Levine.
In package twobucket, implement a Go func, Solve, with
the following signature:
```
func Solve(sizeBucketOne,
sizeBucketTwo,
goalAmount int,
startBucket string) (goalBucket string, numSteps, otherBucketLevel int, e error)
```
Solve returns four values: the resulting goal bucket("one" or two"),
the number of moves/steps to achieve the goal amount,
the liters left in the other bucket, and an error value.
The returned error value should be nil when the parameters are valid.
Return an error for any invalid parameter.
Solve should also return an error when a solution cannot be found,
but this error relates only to the bonus exercise below, so you may
ignore that error case for your initial solution.
## Bonus exercise
Once you get `go test` passing, try `go test -tags bonus`. This uses a *build
tag* to enable tests that were not previously enabled. Build tags control which
files should be included in the package. You can read more about those at [the
Go documentation](https://golang.org/pkg/go/build/#hdr-Build_Constraints).
The exercise limits `go test` to only build the tests referenced in the
`two_bucket_test.go` file. The bonus test cases are found in the file
`bonus_test.go`. Enable those tests as described above.
To get the bonus tests to pass, the Solve func must detect when
a solution cannot be found and return an error.
A solution cannot be found when input test case bucket sizes
are not ones which allow the three operations to succeed in creating the goal amount,
which occurs when the two bucket sizes are not relatively prime to one another.
## Source
### Created by
- @leenipper
### Contributed to by
- @bitfield
- @ekingery
- @ferhatelmas
- @hilary
- @sebito91
- @tleen
### Based on
Water Pouring Problem - http://demonstrations.wolfram.com/WaterPouringProblem/

23
two-bucket/bonus_test.go Normal file
View file

@ -0,0 +1,23 @@
//go:build bonus
// +build bonus
package twobucket
import "testing"
var noSolutionCases = []bucketTestCase{
{
"No solution case 1",
2, 4, 1, "two", "", 0, 0, true,
},
{
"No solution case 2",
3, 6, 1, "one", "", 0, 0, true,
},
}
func TestSolve_bonus(t *testing.T) {
for _, tc := range noSolutionCases {
runTestCase(t, tc)
}
}

44
two-bucket/cases_test.go Normal file
View file

@ -0,0 +1,44 @@
package twobucket
// Source: exercism/problem-specifications
// Commit: edbc86b two-bucket: apply lowerCamelCase rule to variables
// Problem Specifications Version: 1.4.0
type bucketTestCase struct {
description string
bucketOne int
bucketTwo int
goal int
startBucket string
goalBucket string
moves int
otherBucket int
errorExpected bool // always false for generated test cases.
}
var testCases = []bucketTestCase{
{
"Measure using bucket one of size 3 and bucket two of size 5 - start with bucket one",
3, 5, 1, "one", "one", 4, 5, false,
},
{
"Measure using bucket one of size 3 and bucket two of size 5 - start with bucket two",
3, 5, 1, "two", "two", 8, 3, false,
},
{
"Measure using bucket one of size 7 and bucket two of size 11 - start with bucket one",
7, 11, 2, "one", "one", 14, 11, false,
},
{
"Measure using bucket one of size 7 and bucket two of size 11 - start with bucket two",
7, 11, 2, "two", "two", 18, 7, false,
},
{
"Measure one step using bucket one of size 1 and bucket two of size 3 - start with bucket two",
1, 3, 3, "two", "two", 1, 0, false,
},
{
"Measure using bucket one of size 2 and bucket two of size 3 - start with bucket one and end with bucket two",
2, 3, 3, "one", "two", 2, 2, false,
},
}

3
two-bucket/go.mod Normal file
View file

@ -0,0 +1,3 @@
module twobucket
go 1.16

5
two-bucket/two_bucket.go Normal file
View file

@ -0,0 +1,5 @@
package twobucket
func Solve(sizeBucketOne, sizeBucketTwo, goalAmount int, startBucket string) (string, int, int, error) {
panic("Please implement the Solve function")
}

View file

@ -0,0 +1,66 @@
package twobucket
import "testing"
func runTestCase(t *testing.T, tc bucketTestCase) {
g, m, other, e := Solve(tc.bucketOne, tc.bucketTwo, tc.goal, tc.startBucket)
var _ error = e
if e == nil {
if tc.errorExpected {
t.Fatalf("FAIL: %s\nSolve(%d, %d, %d, %q)\nExpected error\nActual: %q, %d, %d",
tc.description,
tc.bucketOne, tc.bucketTwo, tc.goal, tc.startBucket,
g, m, other)
}
if g != tc.goalBucket || m != tc.moves || other != tc.otherBucket {
t.Fatalf("FAIL: %s\nSolve(%d, %d, %d, %q)\nExpected: %q, %d, %d\nActual: %q, %d, %d",
tc.description,
tc.bucketOne, tc.bucketTwo, tc.goal, tc.startBucket,
tc.goalBucket, tc.moves, tc.otherBucket,
g, m, other)
}
} else if !tc.errorExpected {
t.Fatalf("FAIL: %s\nSolve(%d, %d, %d, %q)\nExpected: %q, %d, %d\nGot Error %q",
tc.description,
tc.bucketOne, tc.bucketTwo, tc.goal, tc.startBucket,
tc.goalBucket, tc.moves, tc.otherBucket,
e)
}
t.Logf("PASS: %s", tc.description)
}
func TestSolve(t *testing.T) {
for _, tc := range append(testCases, errorTestCases...) {
runTestCase(t, tc)
}
}
func BenchmarkSolve(b *testing.B) {
if testing.Short() {
b.Skip("skipping benchmark in short mode.")
}
for i := 0; i < b.N; i++ {
for _, tc := range append(testCases, errorTestCases...) {
Solve(tc.bucketOne, tc.bucketTwo, tc.goal, tc.startBucket)
}
}
}
var errorTestCases = []bucketTestCase{
{
"Invalid first bucket size",
0, 5, 5, "one", "one", 1, 0, true,
},
{
"Invalid second bucket size",
3, 0, 3, "one", "one", 1, 0, true,
},
{
"Invalid goal amount",
1, 1, 0, "one", "one", 0, 1, true,
},
{
"Invalid start bucket name",
3, 5, 1, "three", "one", 4, 5, true,
},
}