Initial upload
This commit is contained in:
22
go/blackjack/.exercism/config.json
Normal file
22
go/blackjack/.exercism/config.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"blurb": "Learn about conditionals by playing Blackjack.",
|
||||
"authors": [
|
||||
"andres-zartab"
|
||||
],
|
||||
"contributors": [
|
||||
"tehsphinx",
|
||||
"andrerfcsantos"
|
||||
],
|
||||
"forked_from": [],
|
||||
"files": {
|
||||
"solution": [
|
||||
"blackjack.go"
|
||||
],
|
||||
"test": [
|
||||
"blackjack_test.go"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.go"
|
||||
]
|
||||
}
|
||||
}
|
1
go/blackjack/.exercism/metadata.json
Normal file
1
go/blackjack/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"blackjack","id":"97a16a05d3a141a19fdb0db49be634a3","url":"https://exercism.org/tracks/go/exercises/blackjack","handle":"halfdan","is_requester":true,"auto_approve":false}
|
40
go/blackjack/HELP.md
Normal file
40
go/blackjack/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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 blackjack.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)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- 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)
|
39
go/blackjack/HINTS.md
Normal file
39
go/blackjack/HINTS.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- Conditionals are used to check for certain conditions and/or criteria. The most basic way of performing a conditional operation is using a single `if` statement.
|
||||
|
||||
## 1. Calculate the score of any given card.
|
||||
|
||||
- The `ParseCard` function should take the `card` string (e.g. `ace`) and turn it into its value (e.g. 11).
|
||||
- Use a big [`switch` statement][switch_statement] on the `card` variable.
|
||||
- King, Queen, Jack and 10 can be handled with a single case.
|
||||
- The switch can have a `default` case. In any case the function should return `0` for unknown cards.
|
||||
|
||||
## 2. Determine if two cards make up a Blackjack.
|
||||
|
||||
- `IsBlackJack` checks if 2 cards have the combined value of 21.
|
||||
- Should use the `ParseCard` function to get the value for each card.
|
||||
- Should sum up the values of the 2 cards.
|
||||
- Should return `true` if the sum is equal to `21`.
|
||||
- No `if` statement is needed here. The result for the comparison can be returned.
|
||||
|
||||
## 3. Implement the decision logic for hand scores larger than 20 points.
|
||||
|
||||
- As the `LargeHand` function is only called for hands with a value larger than 20, there are only 2 different possible hands: A **BlackJack** with a total value of `21` and **2 Aces** with a total value of `22`.
|
||||
- The function should check [if][if_statement] `isBlackJack` is `true` and return "P" otherwise.
|
||||
- If `isBlackJack` is `true`, the dealerScore needs to be checked for being lower than 10. [If][if_statement] it is lower, return "W" otherwise "S".
|
||||
|
||||
## 4. Implement the decision logic for hand scores with less than 21 points.
|
||||
|
||||
- The `SmallHand` function is only called if there are no Aces on the hand (`handScore` is less than 21).
|
||||
- Implement every condition using [logical operators][logical_operators] if necessary.
|
||||
- [If][if_statement] your cards sum up to 17 or higher you should always _stand_.
|
||||
- [If][if_statement] your cards sum up to 11 or lower you should always _hit_.
|
||||
- [If][if_statement] your cards sum up to a value within the range [12, 16] you should always _stand_ if the dealer has a 6 or lower.
|
||||
- [If][if_statement] your cards sum up to a value within the range [12, 16] you should always _hit_ if the dealer has a 7 or higher.
|
||||
|
||||
[logical_operators]: https://golang.org/ref/spec#Logical_operators
|
||||
[if_statement]: https://golang.org/ref/spec#If_statements
|
||||
[switch_statement]: https://golang.org/ref/spec#Switch_statements
|
134
go/blackjack/README.md
Normal file
134
go/blackjack/README.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Blackjack
|
||||
|
||||
Welcome to Blackjack 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
|
||||
|
||||
Like other languages, Go also provides a `switch` statement. Switch statements are a shorter way to write long `if ... else if` statements. To make a switch, we start by using the keyword `switch` followed by a value or expression. We then declare each one of the conditions with the `case` keyword. We can also declare a `default` case, that will run when none of the previous `case` conditions matched:
|
||||
|
||||
```go
|
||||
operatingSystem := "windows"
|
||||
|
||||
switch operatingSystem {
|
||||
case "windows":
|
||||
// do something if the operating system is windows
|
||||
case "linux":
|
||||
// do something if the operating system is linux
|
||||
case "macos":
|
||||
// do something if the operating system is macos
|
||||
default:
|
||||
// do something if the operating system is none of the above
|
||||
}
|
||||
```
|
||||
|
||||
One interesting thing about switch statements, is that the value after the `switch` keyword can be omitted, and we can have boolean conditions for each `case`:
|
||||
|
||||
```go
|
||||
age := 21
|
||||
|
||||
switch {
|
||||
case age > 20 && age < 30:
|
||||
// do something if age is between 20 and 30
|
||||
case age == 10:
|
||||
// do something if age is equal to 10
|
||||
default:
|
||||
// do something else for every other case
|
||||
}
|
||||
```
|
||||
|
||||
## Instructions
|
||||
|
||||
In this exercise we will simulate the first turn of a [Blackjack](https://en.wikipedia.org/wiki/Blackjack) game.
|
||||
|
||||
You will receive two cards and will be able to see the face up card of the dealer. All cards are represented using a string such as "ace", "king", "three", "two", etc. The values of each card are:
|
||||
|
||||
| card | value | card | value |
|
||||
| :---: | :---: | :---: | :---: |
|
||||
| ace | 11 | eight | 8 |
|
||||
| two | 2 | nine | 9 |
|
||||
| three | 3 | ten | 10 |
|
||||
| four | 4 | jack | 10 |
|
||||
| five | 5 | queen | 10 |
|
||||
| six | 6 | king | 10 |
|
||||
| seven | 7 | other | 0 |
|
||||
|
||||
**Note**: Commonly, aces can take the value of 1 or 11 but for simplicity we will assume that they can only take the value of 11.
|
||||
|
||||
Depending on your two cards and the card of the dealer, there is a strategy for the first turn of the game, in which you have the following options:
|
||||
|
||||
- Stand (S)
|
||||
- Hit (H)
|
||||
- Split (P)
|
||||
- Automatically win (W)
|
||||
|
||||
Although not optimal yet, you will follow the strategy your friend Alex has been developing, which is as follows:
|
||||
|
||||
Category: Large Hand
|
||||
|
||||
- If you have a pair of aces you must always split them.
|
||||
- If you have a Blackjack (two cards that sum up to a value of 21), and the dealer does not have an ace, a figure or a ten then you automatically win. If the dealer does have any of those cards then you'll have to stand and wait for the reveal of the other card.
|
||||
|
||||
Category: Small Hand
|
||||
|
||||
- If your cards sum up to 17 or higher you should always stand.
|
||||
- If your cards sum up to 11 or lower you should always hit.
|
||||
- If your cards sum up to a value within the range [12, 16] you should always stand unless the dealer has a 7 or higher, in which case you should always hit.
|
||||
|
||||
The overall logic has already been implemented. You have four tasks:
|
||||
|
||||
## 1. Calculate the score of any given card.
|
||||
|
||||
Implement a function to calculate the numerical value of a card given its name using conditionals.
|
||||
|
||||
```go
|
||||
value := ParseCard("ace")
|
||||
fmt.Println(value)
|
||||
// Output: 11
|
||||
```
|
||||
|
||||
## 2. Determine if two cards make up a Blackjack.
|
||||
|
||||
Implement a function that returns `true` if two cards form a Blackjack, `false` otherwise.
|
||||
|
||||
```go
|
||||
isBlackjack := IsBlackjack("queen", "ace")
|
||||
fmt.Println(isBlackjack)
|
||||
// Output: true
|
||||
```
|
||||
|
||||
## 3. Implement the decision logic for hand scores larger than 20 points.
|
||||
|
||||
Implement a function that returns the string representation of a decision given your cards. This function is only called if the `handScore` is larger than 20. It will receive 2 arguments: `isBlackJack` and `dealerScore`. It should implement the bulletpoints in the category "Large Hand" above.
|
||||
|
||||
```go
|
||||
isBlackJack := true
|
||||
dealerScore := 7
|
||||
choice := LargeHand(isBlackJack, dealerScore)
|
||||
fmt.Println(choice)
|
||||
// Output: "W"
|
||||
```
|
||||
|
||||
## 4. Implement the decision logic for hand scores with less than 21 points.
|
||||
|
||||
Implement a function that returns the string representation of a decision given your cards. This function is only called if the `handScore` is less than 21. It will receive 2 arguments: `handScore` and `dealerScore`. It should implement the bulletpoints in the category "Small Hand" above.
|
||||
|
||||
```go
|
||||
handScore := 15
|
||||
dealerScore := 12
|
||||
choice := SmallHand(handScore, dealerScore)
|
||||
fmt.Println(choice)
|
||||
// Output: "H"
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @andres-zartab
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @tehsphinx
|
||||
- @andrerfcsantos
|
55
go/blackjack/blackjack.go
Normal file
55
go/blackjack/blackjack.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package blackjack
|
||||
|
||||
// ParseCard returns the integer value of a card following blackjack ruleset.
|
||||
func ParseCard(card string) int {
|
||||
switch card {
|
||||
case "ace":
|
||||
return 11
|
||||
case "two":
|
||||
return 2
|
||||
case "three":
|
||||
return 3
|
||||
case "four":
|
||||
return 4
|
||||
case "five":
|
||||
return 5
|
||||
case "six":
|
||||
return 6
|
||||
case "seven":
|
||||
return 7
|
||||
case "eight":
|
||||
return 8
|
||||
case "nine":
|
||||
return 9
|
||||
case "ten", "jack", "queen", "king":
|
||||
return 10
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// IsBlackjack returns true if the player has a blackjack, false otherwise.
|
||||
func IsBlackjack(card1, card2 string) bool {
|
||||
return ParseCard(card1) + ParseCard(card2) == 21
|
||||
}
|
||||
|
||||
// LargeHand implements the decision tree for hand scores larger than 20 points.
|
||||
func LargeHand(isBlackjack bool, dealerScore int) string {
|
||||
if !isBlackjack {
|
||||
// Must have two aces
|
||||
return "P"
|
||||
} else if isBlackjack && dealerScore < 10 {
|
||||
return "W"
|
||||
}
|
||||
return "S"
|
||||
}
|
||||
|
||||
// SmallHand implements the decision tree for hand scores with less than 21 points.
|
||||
func SmallHand(handScore, dealerScore int) string {
|
||||
if handScore >= 17 {
|
||||
return "S"
|
||||
} else if handScore <= 11 || dealerScore >= 7 {
|
||||
return "H"
|
||||
}
|
||||
return "S"
|
||||
}
|
398
go/blackjack/blackjack_test.go
Normal file
398
go/blackjack/blackjack_test.go
Normal file
@@ -0,0 +1,398 @@
|
||||
package blackjack
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseCard(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
card string
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "parse ace",
|
||||
card: "ace",
|
||||
want: 11,
|
||||
},
|
||||
{
|
||||
name: "parse two",
|
||||
card: "two",
|
||||
want: 2,
|
||||
},
|
||||
{
|
||||
name: "parse three",
|
||||
card: "three",
|
||||
want: 3,
|
||||
},
|
||||
{
|
||||
name: "parse four",
|
||||
card: "four",
|
||||
want: 4,
|
||||
},
|
||||
{
|
||||
name: "parse five",
|
||||
card: "five",
|
||||
want: 5,
|
||||
},
|
||||
{
|
||||
name: "parse six",
|
||||
card: "six",
|
||||
want: 6,
|
||||
},
|
||||
{
|
||||
name: "parse seven",
|
||||
card: "seven",
|
||||
want: 7,
|
||||
},
|
||||
{
|
||||
name: "parse eight",
|
||||
card: "eight",
|
||||
want: 8,
|
||||
},
|
||||
{
|
||||
name: "parse nine",
|
||||
card: "nine",
|
||||
want: 9,
|
||||
},
|
||||
{
|
||||
name: "parse ten",
|
||||
card: "ten",
|
||||
want: 10,
|
||||
},
|
||||
{
|
||||
name: "parse jack",
|
||||
card: "jack",
|
||||
want: 10,
|
||||
},
|
||||
{
|
||||
name: "parse queen",
|
||||
card: "queen",
|
||||
want: 10,
|
||||
},
|
||||
{
|
||||
name: "parse king",
|
||||
card: "king",
|
||||
want: 10,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ParseCard(tt.card); got != tt.want {
|
||||
t.Errorf("ParseCard(%s) = %d, want %d", tt.card, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlackjack(t *testing.T) {
|
||||
type hand struct {
|
||||
card1, card2 string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
hand hand
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "blackjack with ten (ace first)",
|
||||
hand: hand{card1: "ace", card2: "ten"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "blackjack with jack (ace first)",
|
||||
hand: hand{card1: "ace", card2: "jack"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "blackjack with queen (ace first)",
|
||||
hand: hand{card1: "ace", card2: "queen"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "blackjack with king (ace first)",
|
||||
hand: hand{card1: "ace", card2: "king"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "blackjack with ten (ace second)",
|
||||
hand: hand{card2: "ace", card1: "ten"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "blackjack with jack (ace second)",
|
||||
hand: hand{card2: "ace", card1: "jack"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "blackjack with queen (ace second)",
|
||||
hand: hand{card2: "ace", card1: "queen"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "blackjack with king (ace second)",
|
||||
hand: hand{card2: "ace", card1: "king"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "no blackjack with ace and five",
|
||||
hand: hand{card2: "ace", card1: "five"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "no blackjack with ace and nine",
|
||||
hand: hand{card2: "ace", card1: "nine"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "no blackjack with two aces",
|
||||
hand: hand{card2: "ace", card1: "ace"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "no blackjack with two figures",
|
||||
hand: hand{card2: "queen", card1: "jack"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "no blackjack with king and five",
|
||||
hand: hand{card2: "king", card1: "five"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "no blackjack with eight and five",
|
||||
hand: hand{card2: "eight", card1: "five"},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := IsBlackjack(tt.hand.card1, tt.hand.card2); got != tt.want {
|
||||
t.Errorf("IsBlackjack(%s, %s) = %t, want %t", tt.hand.card1, tt.hand.card2, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFirstTurn(t *testing.T) {
|
||||
type hand struct {
|
||||
card1, card2 string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
hand hand
|
||||
dealer string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "pair of aces",
|
||||
hand: hand{card1: "ace", card2: "ace"},
|
||||
dealer: "ace",
|
||||
want: "P",
|
||||
},
|
||||
{
|
||||
name: "pair of jacks",
|
||||
hand: hand{card1: "jack", card2: "jack"},
|
||||
dealer: "ace",
|
||||
want: "S",
|
||||
},
|
||||
{
|
||||
name: "pair of kings",
|
||||
hand: hand{card1: "king", card2: "king"},
|
||||
dealer: "ace",
|
||||
want: "S",
|
||||
},
|
||||
{
|
||||
name: "pair of twos",
|
||||
hand: hand{card1: "two", card2: "two"},
|
||||
dealer: "ace",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "pair of fives",
|
||||
hand: hand{card1: "five", card2: "five"},
|
||||
dealer: "ace",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "blackjack with ace for dealer",
|
||||
hand: hand{card1: "ace", card2: "jack"},
|
||||
dealer: "ace",
|
||||
want: "S",
|
||||
},
|
||||
{
|
||||
name: "blackjack with queen for dealer",
|
||||
hand: hand{card1: "king", card2: "ace"},
|
||||
dealer: "queen",
|
||||
want: "S",
|
||||
},
|
||||
{
|
||||
name: "blackjack with five for dealer",
|
||||
hand: hand{card1: "ace", card2: "ten"},
|
||||
dealer: "five",
|
||||
want: "W",
|
||||
},
|
||||
{
|
||||
name: "blackjack with nine for dealer",
|
||||
hand: hand{card1: "ace", card2: "king"},
|
||||
dealer: "nine",
|
||||
want: "W",
|
||||
},
|
||||
{
|
||||
name: "score of 20",
|
||||
hand: hand{card1: "ten", card2: "king"},
|
||||
dealer: "ace",
|
||||
want: "S",
|
||||
},
|
||||
{
|
||||
name: "score of 19",
|
||||
hand: hand{card1: "ten", card2: "nine"},
|
||||
dealer: "ace",
|
||||
want: "S",
|
||||
},
|
||||
{
|
||||
name: "score of 18",
|
||||
hand: hand{card1: "ten", card2: "eight"},
|
||||
dealer: "ace",
|
||||
want: "S",
|
||||
},
|
||||
{
|
||||
name: "score of 17",
|
||||
hand: hand{card1: "seven", card2: "king"},
|
||||
dealer: "ace",
|
||||
want: "S",
|
||||
},
|
||||
{
|
||||
name: "score of 16 with six for dealer",
|
||||
hand: hand{card1: "ten", card2: "six"},
|
||||
dealer: "six",
|
||||
want: "S",
|
||||
},
|
||||
{
|
||||
name: "score of 16 with seven for dealer",
|
||||
hand: hand{card1: "ten", card2: "six"},
|
||||
dealer: "seven",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "score of 16 with ace for dealer",
|
||||
hand: hand{card1: "ten", card2: "six"},
|
||||
dealer: "ace",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "score of 15 with six for dealer",
|
||||
hand: hand{card1: "ten", card2: "five"},
|
||||
dealer: "six",
|
||||
want: "S",
|
||||
},
|
||||
{
|
||||
name: "score of 15 with seven for dealer",
|
||||
hand: hand{card1: "ten", card2: "five"},
|
||||
dealer: "seven",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "score of 15 with king for dealer",
|
||||
hand: hand{card1: "ten", card2: "five"},
|
||||
dealer: "king",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "score of 14 with six for dealer",
|
||||
hand: hand{card1: "ten", card2: "four"},
|
||||
dealer: "six",
|
||||
want: "S",
|
||||
},
|
||||
{
|
||||
name: "score of 14 with seven for dealer",
|
||||
hand: hand{card1: "ten", card2: "four"},
|
||||
dealer: "seven",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "score of 14 with queen for dealer",
|
||||
hand: hand{card1: "ten", card2: "four"},
|
||||
dealer: "queen",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "score of 13 with six for dealer",
|
||||
hand: hand{card1: "ten", card2: "three"},
|
||||
dealer: "six",
|
||||
want: "S",
|
||||
},
|
||||
{
|
||||
name: "score of 13 with seven for dealer",
|
||||
hand: hand{card1: "ten", card2: "three"},
|
||||
dealer: "seven",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "score of 13 with queen for dealer",
|
||||
hand: hand{card1: "ten", card2: "three"},
|
||||
dealer: "queen",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "score of 12 with six for dealer",
|
||||
hand: hand{card1: "ten", card2: "two"},
|
||||
dealer: "six",
|
||||
want: "S",
|
||||
},
|
||||
{
|
||||
name: "score of 12 with seven for dealer",
|
||||
hand: hand{card1: "ten", card2: "two"},
|
||||
dealer: "seven",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "score of 12 with queen for dealer",
|
||||
hand: hand{card1: "ten", card2: "two"},
|
||||
dealer: "queen",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "score of 11 with queen for dealer",
|
||||
hand: hand{card1: "nine", card2: "two"},
|
||||
dealer: "queen",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "score of 10 with two for dealer",
|
||||
hand: hand{card1: "eight", card2: "two"},
|
||||
dealer: "two",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "score of 5 with queen for dealer",
|
||||
hand: hand{card1: "three", card2: "two"},
|
||||
dealer: "queen",
|
||||
want: "H",
|
||||
},
|
||||
{
|
||||
name: "score of 4 with five for dealer",
|
||||
hand: hand{card1: "two", card2: "two"},
|
||||
dealer: "five",
|
||||
want: "H",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := FirstTurn(tt.hand.card1, tt.hand.card2, tt.dealer); got != tt.want {
|
||||
t.Errorf("FirstTurn(%s, %s, %s) = %s, want %s", tt.hand.card1, tt.hand.card2, tt.dealer, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// FirstTurn returns the semi-optimal decision for the first turn, given the cards of the player and the dealer.
|
||||
// This function is already implemented and does not need to be edited. It pulls the other functions together in a
|
||||
// complete decision tree for the first turn.
|
||||
func FirstTurn(card1, card2, dealerCard string) string {
|
||||
handScore := ParseCard(card1) + ParseCard(card2)
|
||||
dealerScore := ParseCard(dealerCard)
|
||||
|
||||
if 20 < handScore {
|
||||
return LargeHand(IsBlackjack(card1, card2), dealerScore)
|
||||
}
|
||||
return SmallHand(handScore, dealerScore)
|
||||
}
|
3
go/blackjack/go.mod
Normal file
3
go/blackjack/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module blackjack
|
||||
|
||||
go 1.13
|
22
go/chessboard/.exercism/config.json
Normal file
22
go/chessboard/.exercism/config.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"blurb": "Learn about iterating ranges by generating a chessboard.",
|
||||
"authors": [
|
||||
"brugnara",
|
||||
"tehsphinx"
|
||||
],
|
||||
"contributors": [],
|
||||
"forked_from": [
|
||||
"elixir/chessboard"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"chessboard.go"
|
||||
],
|
||||
"test": [
|
||||
"chessboard_test.go"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.go"
|
||||
]
|
||||
}
|
||||
}
|
1
go/chessboard/.exercism/metadata.json
Normal file
1
go/chessboard/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"chessboard","id":"72b4b3b0b72f42d8b27bc4cfa8f18090","url":"https://exercism.org/tracks/go/exercises/chessboard","handle":"halfdan","is_requester":true,"auto_approve":false}
|
40
go/chessboard/HELP.md
Normal file
40
go/chessboard/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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 chessboard.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)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- 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)
|
34
go/chessboard/HINTS.md
Normal file
34
go/chessboard/HINTS.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- An [integer value][integers] can be defined as one or more consecutive digits.
|
||||
- A [map value][maps] stores key-value data
|
||||
|
||||
## 1. Given a Chessboard and a Rank, count how many squares are occupied
|
||||
|
||||
- You can iterate a [map][maps]
|
||||
- Check if the value is true. If it is increment. This is to count pieces.
|
||||
- You have to [explicitly return an integer][return] from a function.
|
||||
|
||||
## 2. Given a Chessboard and a File, count how many squares are occupied
|
||||
|
||||
- You'll first need to check the file is within range.
|
||||
- Loop over the chessboard.
|
||||
- Add one if the square is occupied.
|
||||
|
||||
## 3. Count how many squares are present in the given chessboard
|
||||
|
||||
- There are many ways to solve this.
|
||||
- This should return how many squares are configured in a chess-board.
|
||||
|
||||
## 4. Count how many squares are occupied in the given chessboard
|
||||
|
||||
- Get the CountInRank for all ranks in the chessboard.
|
||||
|
||||
[functions]: https://golang.org/ref/spec#Function_declarations
|
||||
[return]: https://golang.org/ref/spec#Return_statements
|
||||
[operators]: https://golang.org/ref/spec#Operators
|
||||
[integers]: https://golang.org/ref/spec#Integer_literals
|
||||
[calls]: https://golang.org/ref/spec#Calls
|
||||
[maps]: /tracks/go/concepts/maps
|
187
go/chessboard/README.md
Normal file
187
go/chessboard/README.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# Chessboard
|
||||
|
||||
Welcome to Chessboard 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
|
||||
|
||||
In Go, you can iterate over a `slice` using `for` and an index, or you can use `range`.
|
||||
`range` also allows you to iterate over a `map`.
|
||||
|
||||
Every iteration returns two values: the index/key and a copy of the element at that index/key.
|
||||
|
||||
## Iterate over a slice
|
||||
|
||||
Easy as pie, loops over a slice, ordered as expected.
|
||||
|
||||
```go
|
||||
xi := []int{10, 20, 30}
|
||||
for i, x := range xi {
|
||||
fmt.Println(i, x)
|
||||
}
|
||||
// outputs:
|
||||
// 0, 10
|
||||
// 1, 20
|
||||
// 2, 30
|
||||
```
|
||||
|
||||
## Iterate over a map
|
||||
|
||||
Iterating over a map raises a new problem. The order is now random.
|
||||
|
||||
```go
|
||||
hash := map[int]int{9: 10, 99: 20, 999: 30}
|
||||
for k, v := range hash {
|
||||
fmt.Println(k, v)
|
||||
}
|
||||
// outputs, for example:
|
||||
// 99 20
|
||||
// 999 30
|
||||
// 9 10
|
||||
```
|
||||
|
||||
## Iteration omitting key or value
|
||||
|
||||
In Go an unused variable will raise an error at build time.
|
||||
Sometimes you only need the value, as per the first example:
|
||||
|
||||
```go
|
||||
xi := []int{10, 20, 30}
|
||||
for i, x := range xi {
|
||||
fmt.Println(x)
|
||||
}
|
||||
// Go build failed: i declared but not used
|
||||
```
|
||||
|
||||
You can replace the `i` with `_` which tells the compiler we don't use that value:
|
||||
|
||||
```go
|
||||
xi := []int{10, 20, 30}
|
||||
for _, x := range xi {
|
||||
fmt.Println(x)
|
||||
}
|
||||
// outputs:
|
||||
// 10
|
||||
// 20
|
||||
// 30
|
||||
```
|
||||
|
||||
If you want to only print the index, you can replace the `x` with `_`,
|
||||
or simply omit the declaration:
|
||||
|
||||
```go
|
||||
xi := []int{10, 20, 30}
|
||||
// for i, _ := range xi {
|
||||
for i := range xi {
|
||||
fmt.Println(i)
|
||||
}
|
||||
// outputs:
|
||||
// 0
|
||||
// 1
|
||||
// 2
|
||||
```
|
||||
|
||||
## Non-struct types
|
||||
|
||||
You've previously seen defining struct types.
|
||||
It is also possible to define non-struct types which you can use as an alias for a built-in type declaration, and you can define receiver functions on them to extend them in the same way as struct types.
|
||||
|
||||
```go
|
||||
type Name string
|
||||
func SayHello(n Name) {
|
||||
fmt.Printf("Hello %s\n", n)
|
||||
}
|
||||
n := Name("Fred")
|
||||
SayHello(n)
|
||||
// Output: Hello Fred
|
||||
```
|
||||
|
||||
You can also define non-struct types composed of arrays and maps.
|
||||
|
||||
```go
|
||||
type Names []string
|
||||
func SayHello(n Names) {
|
||||
for _, name := range n {
|
||||
fmt.Printf("Hello %s\n", name)
|
||||
}
|
||||
}
|
||||
n := Names([]string{"Fred", "Bill"})
|
||||
SayHello(n)
|
||||
// Output:
|
||||
// Hello Fred
|
||||
// Hello Bill
|
||||
```
|
||||
|
||||
## Instructions
|
||||
|
||||
As a chess enthusiast, you would like to write your own version of the game. Yes, there may be plenty of implementations of chess available online already, but yours will be unique!
|
||||
|
||||
Each square of the chessboard is identified by a letter-number pair:
|
||||
- The vertical columns of squares, called files, are numbered 1 through 8.
|
||||
- The horizontal rows of squares, called ranks, are labelled A through H.
|
||||
|
||||
```
|
||||
1 2 3 4 5 6 7 8
|
||||
A # _ # _ _ _ _ # A
|
||||
B _ _ _ _ # _ _ _ B
|
||||
C _ _ # _ _ _ _ _ C
|
||||
D _ _ _ _ _ _ _ _ D
|
||||
E _ _ _ _ _ # _ # E
|
||||
F _ _ _ _ _ _ _ _ F
|
||||
G _ _ _ # _ _ _ _ G
|
||||
H # # # # # # _ # H
|
||||
1 2 3 4 5 6 7 8
|
||||
```
|
||||
|
||||
## 1. Given a Chessboard and a Rank, count how many squares are occupied
|
||||
|
||||
Implement the `CountInRank(board Chessboard, rank string) int` function.
|
||||
It should count the total number of occupied squares by ranging over a map. Return an integer.
|
||||
Return a count of zero (`0`) if the given rank cannot be found in the map.
|
||||
|
||||
```go
|
||||
CountInRank(board, "A")
|
||||
// => 6
|
||||
```
|
||||
|
||||
## 2. Given a Chessboard and a File, count how many squares are occupied
|
||||
|
||||
Implement the `CountInFile(board Chessboard, file int) int` function.
|
||||
It should count the total number of occupied squares by ranging over the given file. Return an integer.
|
||||
Return a count of zero (`0`) if the given file is not a valid one (not between `1` and `8`, inclusive).
|
||||
|
||||
```go
|
||||
CountInFile(board, 2)
|
||||
// => 5
|
||||
```
|
||||
|
||||
## 3. Count how many squares are present in the given chessboard
|
||||
|
||||
Implement the `CountAll(board Chessboard) int` function.
|
||||
It should count how many squares are present in the chessboard and returns
|
||||
an integer. Since you don't need to check the content of the squares,
|
||||
consider using range omitting both `index` and `value`.
|
||||
|
||||
```go
|
||||
CountAll(board)
|
||||
// => 64
|
||||
```
|
||||
|
||||
## 4. Count how many squares are occupied in the given chessboard
|
||||
|
||||
Implement the `CountOccupied(board Chessboard) int` function.
|
||||
It should count how many squares are occupied in the chessboard.
|
||||
Return an integer.
|
||||
|
||||
```go
|
||||
CountOccupied(board)
|
||||
// => 15
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @brugnara
|
||||
- @tehsphinx
|
55
go/chessboard/chessboard.go
Normal file
55
go/chessboard/chessboard.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package chessboard
|
||||
|
||||
// Declare a type named Rank which stores if a square is occupied by a piece - this will be a slice of bools
|
||||
// Declare a type named Chessboard contains a map of eight Ranks, accessed with values from "A" to "H"
|
||||
type Rank []bool
|
||||
type Chessboard map[string]Rank
|
||||
// CountInRank returns how many squares are occupied in the chessboard,
|
||||
// within the given rank
|
||||
func CountInRank(cb Chessboard, rank string) int {
|
||||
sum := 0
|
||||
file, ok := cb[rank]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
for _, f := range file {
|
||||
if f {
|
||||
sum += 1
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
// CountInFile returns how many squares are occupied in the chessboard,
|
||||
// within the given file
|
||||
func CountInFile(cb Chessboard, file int) int {
|
||||
if file < 1 || file > 8 {
|
||||
return 0
|
||||
}
|
||||
sum := 0
|
||||
for _, rank := range cb {
|
||||
if rank[file-1] {
|
||||
sum += 1
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
// CountAll should count how many squares are present in the chessboard
|
||||
func CountAll(cb Chessboard) int {
|
||||
sum := 0
|
||||
for _, rank := range cb {
|
||||
sum += len(rank)
|
||||
}
|
||||
|
||||
return sum
|
||||
}
|
||||
|
||||
// CountOccupied returns how many squares are occupied in the chessboard
|
||||
func CountOccupied(cb Chessboard) int {
|
||||
sum := 0
|
||||
for name := range cb {
|
||||
sum += CountInRank(cb, name)
|
||||
}
|
||||
return sum
|
||||
}
|
100
go/chessboard/chessboard_test.go
Normal file
100
go/chessboard/chessboard_test.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package chessboard
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// newChessboard return a *Chessboard for tests
|
||||
//
|
||||
// 1 2 3 4 5 6 7 8
|
||||
// A # _ # _ _ _ _ # A
|
||||
// B _ _ _ _ # _ _ _ B
|
||||
// C _ _ # _ _ _ _ _ C
|
||||
// D _ _ _ _ _ _ _ _ D
|
||||
// E _ _ _ _ _ # _ # E
|
||||
// F _ _ _ _ _ _ _ _ F
|
||||
// G _ _ _ # _ _ _ _ G
|
||||
// H # # # # # # _ # H
|
||||
// 1 2 3 4 5 6 7 8
|
||||
func newChessboard() Chessboard {
|
||||
return Chessboard{
|
||||
"A": Rank{true, false, true, false, false, false, false, true},
|
||||
"B": Rank{false, false, false, false, true, false, false, false},
|
||||
"C": Rank{false, false, true, false, false, false, false, false},
|
||||
"D": Rank{false, false, false, false, false, false, false, false},
|
||||
"E": Rank{false, false, false, false, false, true, false, true},
|
||||
"F": Rank{false, false, false, false, false, false, false, false},
|
||||
"G": Rank{false, false, false, true, false, false, false, false},
|
||||
"H": Rank{true, true, true, true, true, true, false, true},
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountInRank(t *testing.T) {
|
||||
cb := newChessboard()
|
||||
for _, test := range []struct {
|
||||
in string
|
||||
out int
|
||||
}{
|
||||
{"A", 3},
|
||||
{"B", 1},
|
||||
{"C", 1},
|
||||
{"D", 0},
|
||||
{"E", 2},
|
||||
{"F", 0},
|
||||
{"G", 1},
|
||||
{"H", 7},
|
||||
{"Z", 0},
|
||||
} {
|
||||
if out := CountInRank(cb, test.in); out != test.out {
|
||||
t.Errorf(
|
||||
"CountInRank(chessboard, '%v') returned %v while %v was expected\n",
|
||||
test.in,
|
||||
out,
|
||||
test.out,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountInFile(t *testing.T) {
|
||||
cb := newChessboard()
|
||||
for _, test := range []struct {
|
||||
in int
|
||||
out int
|
||||
}{
|
||||
{1, 2},
|
||||
{2, 1},
|
||||
{3, 3},
|
||||
{4, 2},
|
||||
{5, 2},
|
||||
{6, 2},
|
||||
{7, 0},
|
||||
{8, 3},
|
||||
{100, 0},
|
||||
} {
|
||||
if out := CountInFile(cb, test.in); out != test.out {
|
||||
t.Errorf(
|
||||
"CountInFile(chessboard, %v) returned %v while %v was expected\n",
|
||||
test.in,
|
||||
out,
|
||||
test.out,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountAll(t *testing.T) {
|
||||
cb := newChessboard()
|
||||
wanted := 64
|
||||
if out := CountAll(cb); out != wanted {
|
||||
t.Errorf("CountAll(chessboard) returned %v while %v was expected", out, wanted)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountOccupied(t *testing.T) {
|
||||
cb := newChessboard()
|
||||
wanted := 15
|
||||
if out := CountOccupied(cb); out != wanted {
|
||||
t.Errorf("CountOccupied(chessboard) returned %v while %v was expected", out, wanted)
|
||||
}
|
||||
}
|
3
go/chessboard/go.mod
Normal file
3
go/chessboard/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module chessboard
|
||||
|
||||
go 1.16
|
21
go/election-day/.exercism/config.json
Normal file
21
go/election-day/.exercism/config.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"blurb": "Learn about pointers by creating a simple voting system.",
|
||||
"authors": [
|
||||
"andrerfcsantos"
|
||||
],
|
||||
"contributors": [],
|
||||
"files": {
|
||||
"solution": [
|
||||
"election_day.go"
|
||||
],
|
||||
"test": [
|
||||
"election_day_test.go"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.go"
|
||||
],
|
||||
"editor": [
|
||||
"election_result.go"
|
||||
]
|
||||
}
|
||||
}
|
1
go/election-day/.exercism/metadata.json
Normal file
1
go/election-day/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"election-day","id":"66656b96ab674bcca386330eb9bff31c","url":"https://exercism.org/tracks/go/exercises/election-day","handle":"halfdan","is_requester":true,"auto_approve":false}
|
40
go/election-day/HELP.md
Normal file
40
go/election-day/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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 election_day.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)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- 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)
|
54
go/election-day/HINTS.md
Normal file
54
go/election-day/HINTS.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- `*T` can be used to declared variables that are pointers to some type `T`, e.g `var i *int` declares a variable `i` that is a pointer to an `int`
|
||||
- You can get a pointer for a variable (its memory address) by using the `&` operator, e.g `mypointer := &anIntVariable`.
|
||||
- You can get the value stored in a pointer by using the `*` operator on a pointer, eg. `var i int = *aPointerToInt`. This is called dereferencing the pointer.
|
||||
- You check if a pointer is not `nil` before dereferencing it. Attempting to dereference a `nil` pointer will give you a runtime error.
|
||||
- If you are unsure how pointers work, try reading [Tour of Go: Pointers][go-tour-pointers] or [Go by Example: Pointers][go-by-example-pointers]
|
||||
|
||||
## 1. Create a vote counter
|
||||
|
||||
- You need to create a pointer to an `int`, in other words, a `*int`.
|
||||
- You can use the `&` operator on a variable to create a pointer to it, e.g `&myInt`
|
||||
- You can create a pointer to a new variable defined by you or you can use the variable of the function argument
|
||||
|
||||
## 2. Get number of votes from a counter
|
||||
|
||||
- You can use the `*` operator on a pointer to dereference it and get its value, e.g `*myPointer`
|
||||
- Dereferencing `nil` pointers will give you a runtime error. Always make sure a pointer is not `nil` before dereferencing it.
|
||||
|
||||
## 3. Increment the votes of a counter
|
||||
|
||||
- If you have a pointer `var myPointer *int`, you can assign to `*myPointer` to change the value pointed by `myPointer`
|
||||
- To get the current value of the pointer, you need to dereference it using the `*` operator, or call the function you made in the previous task.
|
||||
|
||||
## 4. Create the election results
|
||||
|
||||
- Create a new `ElectionResult` literal or variable with the fields `Name` and `Votes` filled with the values in the arguments of the function.
|
||||
- You can create a pointer from a variable or literal by using the `&` operator before the variable name/literal declaration, e.g `&myVariable` or `&ElectionResult{Name: "John", Votes: 1}`
|
||||
|
||||
## 5. Announce the results
|
||||
|
||||
- Although you are receiving a pointer to an `ElectionResult`, you can access its fields with the dot `.` notation, like if it wasn't a pointer!
|
||||
- Build the message by accessing the `Name` and `Value` fields on the struct.
|
||||
- Even though you are accessing fields from a pointer to a struct, you don't need to do any dereferencing. Go will automatically dereference the pointer for you, like in this example:
|
||||
|
||||
```go
|
||||
result := &ElectionResult{
|
||||
Name: "John",
|
||||
Votes: 32
|
||||
}
|
||||
|
||||
result.Name // "John" - Go will automatically dereference the pointer
|
||||
// and access the 'Name' field of the dereferenced struct
|
||||
```
|
||||
|
||||
## 6. Vote recounting
|
||||
|
||||
- You can think of maps as being pointers already. This means that changes you make to the map inside the function will be visible outside the function.
|
||||
- To increment the value of a key in a `var m map[string]int`, you have several options: `m["mykey"] = m["mykey"] + 1 `, `m["mykey"] += 1 ` or `m["mykey"]++ `
|
||||
|
||||
[go-tour-pointers]: https://tour.golang.org/moretypes/1
|
||||
[go-by-example-pointers]: https://gobyexample.com/pointers
|
301
go/election-day/README.md
Normal file
301
go/election-day/README.md
Normal file
@@ -0,0 +1,301 @@
|
||||
# Election Day
|
||||
|
||||
Welcome to Election Day 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
|
||||
|
||||
Like many other languages, Go has pointers.
|
||||
If you're new to pointers, they can feel a little mysterious but once you get used to them, they're quite straight-forward.
|
||||
They're a crucial part of Go, so take some time to really understand them.
|
||||
|
||||
Before digging into the details, it's worth understanding the use of pointers. Pointers are a way to share memory with other parts of our program, which is useful for two major reasons:
|
||||
1. When we have large amounts of data, making copies to pass between functions is very inefficient.
|
||||
By passing the memory location of where the data is stored instead, we can dramatically reduce the resource-footprint of our programs.
|
||||
2. By passing pointers between functions, we can access and modify the single copy of the data directly, meaning that any changes made by one function are immediately visible to other parts of the program when the function ends.
|
||||
|
||||
## Variables and Memory
|
||||
|
||||
Let's say we have a regular integer variable `a`:
|
||||
|
||||
```go
|
||||
var a int
|
||||
```
|
||||
|
||||
When we declare a variable, Go has to find a place in memory to store its value. This is largely abstracted from us — when we need to fetch the value stored in that piece of memory, we can just refer to it by the variable name.
|
||||
|
||||
For instance, when we write `a + 2`, we are effectively fetching the value stored in the memory associated with the variable `a` and adding 2 to it.
|
||||
|
||||
Similarly, when we need to change the value in the piece of memory of `a`, we can use the variable name to do an assignment:
|
||||
|
||||
```go
|
||||
a = 3
|
||||
```
|
||||
|
||||
The piece of memory that is associated with `a` will now be storing the value `3`.
|
||||
|
||||
## Pointers
|
||||
|
||||
While variables allow us to refer to values in memory, sometimes it's useful to know the **memory address** to which the variable is pointing. **Pointers** hold the memory addresses of those values. You declare a variable with a pointer type by prefixing the underlying type with an asterisk:
|
||||
|
||||
```go
|
||||
var p *int // 'p' contains the memory address of an integer
|
||||
```
|
||||
|
||||
Here we declare a variable `p` of type "pointer to int" (`*int`). This means that `p` will hold the memory address of an integer. The zero value of pointers is `nil` because a `nil` pointer holds no memory address.
|
||||
|
||||
### Getting a pointer to a variable
|
||||
|
||||
To find the memory address of the value of a variable, we can use the `&` operator.
|
||||
For example, if we want to find and store the memory address of variable `a` in the pointer `p`, we can do the following:
|
||||
|
||||
```go
|
||||
var a int
|
||||
a = 2
|
||||
|
||||
var p *int
|
||||
p = &a // the variable 'p' contains the memory address of 'a'
|
||||
```
|
||||
|
||||
### Accessing the value via a pointer (dereferencing)
|
||||
|
||||
When we have a pointer, we might want to know the value stored in the memory address the pointer represents. We can do this using the `*` operator:
|
||||
|
||||
```go
|
||||
var a int
|
||||
a = 2
|
||||
|
||||
var p *int
|
||||
p = &a // the variable 'p' contains the memory address of 'a'
|
||||
|
||||
var b int
|
||||
b = *p // b == 2
|
||||
```
|
||||
|
||||
The operation `*p` fetches the value stored at the memory address stored in `p`. This operation is often called "dereferencing".
|
||||
|
||||
We can also use the dereference operator to assign a new value to the memory address referenced by the pointer:
|
||||
|
||||
```go
|
||||
var a int
|
||||
a = 2 // declare int variable 'a' and assign it the value of 2
|
||||
|
||||
var pa *int
|
||||
pa = &a // 'pa' now contains to the memory address of 'a'
|
||||
*pa = *pa + 2 // increment by 2 the value at memory address 'pa'
|
||||
|
||||
fmt.Println(a) // Output: 4
|
||||
// 'a' will have the new value that was changed via the pointer!
|
||||
```
|
||||
|
||||
Assigning to `*pa` will change the value stored at the memory address `pa` holds. Since `pa` holds the memory address of `a`, by assigning to `*pa` we are effectively changing the value of `a`!
|
||||
|
||||
A note of caution however: always check if a pointer is not `nil` before dereferencing. Dereferencing a `nil` pointer will make the program crash at runtime!
|
||||
|
||||
```go
|
||||
var p *int // p is nil initially
|
||||
fmt.Println(*p)
|
||||
// panic: runtime error: invalid memory address or nil pointer dereference
|
||||
```
|
||||
|
||||
### Pointers to structs
|
||||
|
||||
So far we've only seen pointers to primitive values. We can also create pointers for structs:
|
||||
|
||||
```go
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
var peter Person
|
||||
peter = Person{Name: "Peter", Age: 22}
|
||||
|
||||
var p *Person
|
||||
p = &peter
|
||||
```
|
||||
|
||||
We could have also created a new `Person` and immediately stored a pointer to it:
|
||||
|
||||
```go
|
||||
var p *Person
|
||||
p = &Person{Name: "Peter", Age: 22}
|
||||
```
|
||||
|
||||
When we have a pointer to a struct, we don't need to dereference the pointer before accessing one of the fields:
|
||||
|
||||
```go
|
||||
var p *Person
|
||||
p = &Person{Name: "Peter", Age: 22}
|
||||
|
||||
fmt.Println(p.Name) // Output: "Peter"
|
||||
// Go automatically dereferences 'p' to allow
|
||||
// access to the 'Name' field
|
||||
```
|
||||
|
||||
## Slices and maps are already pointers
|
||||
|
||||
Slices and maps are special types because they already have pointers in their implementation. This means that more often that not, we don't need to create pointers for these types to share the memory address for their values. Imagine we have a function that increments the value of a key in a map:
|
||||
|
||||
|
||||
```go
|
||||
func incrementPeterAge(m map[string]int) {
|
||||
m["Peter"] += 1
|
||||
}
|
||||
```
|
||||
|
||||
If we create a map and call this function, the changes the function made to the map persist after the function ended. This is a similar behavior we get if we were using a pointer, but note how on this example we are not using any referencing/dereferencing or any of the pointer syntax:
|
||||
|
||||
```go
|
||||
ages := map[string]int{
|
||||
"Peter": 21
|
||||
}
|
||||
incrementPeterAge(ages)
|
||||
fmt.Println(ages)
|
||||
// Output: map[Peter:22]
|
||||
// The changes the function 'addPeterAge' made to the map are visible after the function ends!
|
||||
```
|
||||
|
||||
The same applies when changing an existing item in a slice.
|
||||
|
||||
However, actions that return a new slice like `append` are a special case and **might not** modify the slice outside of the function. This is due to the way slices work internally, but we won't cover this in detail in this exercise, as this is a more advanced topic. If you are really curious you can read more about this in [Go Blog: Mechanics of 'append'][mechanics-of-append]
|
||||
|
||||
[mechanics-of-append]: https://go.dev/blog/slices
|
||||
|
||||
## Instructions
|
||||
|
||||
A local school near you has a very active students' association.
|
||||
The students' association is managed by a president and once every 2 years,
|
||||
elections are run to elect a new president.
|
||||
|
||||
In this year's election, it was decided that a new digital system to
|
||||
count the votes was needed. The school needs your help building this new system.
|
||||
|
||||
## 1. Create a vote counter
|
||||
|
||||
One of the first things that the new voting system needs is a vote counter.
|
||||
This counter is a way to keep track of the votes a particular candidate has.
|
||||
|
||||
Create a function `NewVoteCounter` that accepts the number of initial votes for a candidate and returns a pointer referring to an `int`, initialized with the given number of initial votes.
|
||||
|
||||
```go
|
||||
var initialVotes int
|
||||
initialVotes = 2
|
||||
|
||||
var counter *int
|
||||
counter = NewVoteCounter(initialVotes)
|
||||
*counter == initialVotes // true
|
||||
```
|
||||
|
||||
## 2. Get number of votes from a counter
|
||||
|
||||
You now have a way to create new counters! But now you realize the new system will also need a way to get the number of votes from a counter.
|
||||
|
||||
Create a function `VoteCount` that will take a counter (`*int`) as an argument and will return the number of votes in the counter. If the counter is `nil` you should assume the counter has no votes:
|
||||
|
||||
```go
|
||||
var votes int
|
||||
votes = 3
|
||||
|
||||
var voteCounter *int
|
||||
voteCounter = &votes
|
||||
|
||||
VoteCount(voteCounter)
|
||||
// Output: 3
|
||||
|
||||
var nilVoteCounter *int
|
||||
VoteCount(nilVoteCounter)
|
||||
// Output: 0
|
||||
```
|
||||
|
||||
## 3. Increment the votes of a counter
|
||||
|
||||
It's finally the time to count the votes! Now you need a way to increment the votes in a counter.
|
||||
|
||||
Create a function `IncrementVoteCount` that will take a counter (`*int`) as an argument and a number of votes, and will increment the counter by that number of votes. You can assume the pointer passed will never be `nil`.
|
||||
|
||||
```go
|
||||
var votes int
|
||||
votes = 3
|
||||
|
||||
var voteCounter *int
|
||||
voteCounter = &votes
|
||||
|
||||
IncrementVoteCount(voteCounter, 2)
|
||||
|
||||
votes == 5 // true
|
||||
*voteCounter == 5 // true
|
||||
```
|
||||
|
||||
## 4. Create the election results
|
||||
|
||||
With all the votes now counted, it's time to prepare the result announcement to the whole school.
|
||||
For this, you notice that having only counters for the votes is insufficient.
|
||||
There needs to be a way to associate the number of votes with a particular candidate.
|
||||
|
||||
Create a function `NewElectionResult` that receives the name of a candidate and their number of votes and
|
||||
returns a new election result.
|
||||
|
||||
```go
|
||||
var result *ElectionResult
|
||||
result = NewElectionResult("Peter", 3)
|
||||
|
||||
result.Name == "Peter" // true
|
||||
result.Votes == 3 // true
|
||||
```
|
||||
|
||||
The election result struct is already created for you and it's defined as:
|
||||
|
||||
```go
|
||||
type ElectionResult struct {
|
||||
// Name of the candidate
|
||||
Name string
|
||||
// Number of votes the candidate had
|
||||
Votes int
|
||||
}
|
||||
```
|
||||
|
||||
## 5. Announce the results
|
||||
|
||||
It's time to announce the new president to the school!
|
||||
The president will be announced in the little digital message boards that the school has.
|
||||
The message should show the name of the new president and the votes it had, in the following format: `<candidate_name> (<votes>)`. This is an example of such message: `"Peter (51)"`.
|
||||
|
||||
Create a function `DisplayResult` that will receive an `*ElectionResult` as an argument and will return a string with the message to display.
|
||||
|
||||
|
||||
```go
|
||||
var result *ElectionResult
|
||||
result = &ElectionResult{
|
||||
Name: "John",
|
||||
Votes: 32,
|
||||
}
|
||||
|
||||
DisplayResult(result)
|
||||
// Output: John (32)
|
||||
```
|
||||
|
||||
## 6. Vote recounting
|
||||
|
||||
To make sure the final results were accurate, the votes were recounted. In the recount, it was found that the number votes for some of the candidates was off by one.
|
||||
|
||||
Create a function `DecrementVotesOfCandidate` that receives the final results and the name of a candidate for which you should decrement its vote count. The final results are given in the form of a `map[string]int`, where the keys are the names of the candidates and the values are its total votes.
|
||||
|
||||
```go
|
||||
var finalResults = map[string]int{
|
||||
"Mary": 10,
|
||||
"John": 51,
|
||||
}
|
||||
|
||||
DecrementVotesOfCandidate(finalResults, "Mary")
|
||||
|
||||
finalResults["Mary"]
|
||||
// Output: 9
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @andrerfcsantos
|
32
go/election-day/election_day.go
Normal file
32
go/election-day/election_day.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package electionday
|
||||
|
||||
// NewVoteCounter returns a new vote counter with
|
||||
// a given number of inital votes.
|
||||
func NewVoteCounter(initialVotes int) *int {
|
||||
panic("Please implement the NewVoteCounter() function")
|
||||
}
|
||||
|
||||
// VoteCount extracts the number of votes from a counter.
|
||||
func VoteCount(counter *int) int {
|
||||
panic("Please implement the VoteCount() function")
|
||||
}
|
||||
|
||||
// IncrementVoteCount increments the value in a vote counter
|
||||
func IncrementVoteCount(counter *int, increment int) {
|
||||
panic("Please implement the IncrementVoteCount() function")
|
||||
}
|
||||
|
||||
// NewElectionResult creates a new election result
|
||||
func NewElectionResult(candidateName string, votes int) *ElectionResult {
|
||||
panic("Please implement the NewElectionResult() function")
|
||||
}
|
||||
|
||||
// DisplayResult creates a message with the result to be displayed
|
||||
func DisplayResult(result *ElectionResult) string {
|
||||
panic("Please implement the DisplayResult() function")
|
||||
}
|
||||
|
||||
// DecrementVotesOfCandidate decrements by one the vote count of a candidate in a map
|
||||
func DecrementVotesOfCandidate(results map[string]int, candidate string) {
|
||||
panic("Please implement the DecrementVotesOfCandidate() function")
|
||||
}
|
183
go/election-day/election_day_test.go
Normal file
183
go/election-day/election_day_test.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package electionday
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewVoteCounter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
votes int
|
||||
}{
|
||||
{
|
||||
name: "Simple vote counter with 2 votes",
|
||||
votes: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := NewVoteCounter(tt.votes)
|
||||
if got == nil {
|
||||
t.Errorf("NewVoteCounter(%d) = %s, &%d", tt.votes, intPtrRepresentation(got), tt.votes)
|
||||
}
|
||||
if got != nil && *got != tt.votes {
|
||||
t.Errorf("NewVoteCounter(%d) = %s, &%d", tt.votes, intPtrRepresentation(got), tt.votes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVoteCount(t *testing.T) {
|
||||
twoVotes := 2
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
counter *int
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
name: "Call to VoteCount with a nil argument",
|
||||
counter: nil,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "Call to VoteCount with a pointer to an int with a value of 2",
|
||||
counter: &twoVotes,
|
||||
expected: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := VoteCount(tt.counter); got != tt.expected {
|
||||
t.Fatalf("VoteCount(%v) = %d, want %d", intPtrRepresentation(tt.counter), got, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncrementVoteCount(t *testing.T) {
|
||||
twoVotes := 2
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
counter *int
|
||||
increment int
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
name: "Call to IncrementVoteCount with a pointer to an int with a value of 2 and increment of 2",
|
||||
counter: &twoVotes,
|
||||
increment: 2,
|
||||
expected: 4,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
before := intPtrRepresentation(tt.counter)
|
||||
IncrementVoteCount(tt.counter, tt.increment)
|
||||
after := intPtrRepresentation(tt.counter)
|
||||
|
||||
if tt.counter == nil {
|
||||
t.Errorf("counter before: %s | counter after: %v | wanted: &%d", before, after, tt.expected)
|
||||
}
|
||||
|
||||
if tt.counter != nil && *tt.counter != tt.expected {
|
||||
t.Errorf("counter before: %s | counter after: %v | wanted: &%d", before, after, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewElectionResult(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
candidateName string
|
||||
votes int
|
||||
wanted ElectionResult
|
||||
}{
|
||||
{
|
||||
name: "Call to NewElectionResult for Peter with 2 votes",
|
||||
candidateName: "Peter",
|
||||
votes: 2,
|
||||
wanted: ElectionResult{
|
||||
Name: "Peter",
|
||||
Votes: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := NewElectionResult(tt.candidateName, tt.votes)
|
||||
|
||||
if result == nil || result.Name != tt.wanted.Name || result.Votes != tt.wanted.Votes {
|
||||
t.Errorf("NewElectionResult(\"%s\", %d) = %#v, wanted %#v",
|
||||
tt.candidateName, tt.votes, result, tt.wanted)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisplayResult(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
result *ElectionResult
|
||||
wanted string
|
||||
}{
|
||||
{
|
||||
name: "Call to DisplayResult for Jonh with 5 votes",
|
||||
result: &ElectionResult{
|
||||
Name: "John",
|
||||
Votes: 5,
|
||||
},
|
||||
wanted: "John (5)",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if result := DisplayResult(tt.result); result != tt.wanted {
|
||||
t.Errorf("DisplayResult(%#v) = %s, wanted %s", *tt.result, result, tt.wanted)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecrementVotesOfCandidate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
candidate string
|
||||
results map[string]int
|
||||
wanted int
|
||||
}{
|
||||
{
|
||||
name: "Call to DecrementVotesOfCandidate for Jonh with 5 votes",
|
||||
candidate: "John",
|
||||
results: map[string]int{
|
||||
"John": 3,
|
||||
},
|
||||
wanted: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
DecrementVotesOfCandidate(tt.results, tt.candidate)
|
||||
if votes, ok := tt.results[tt.candidate]; !ok || votes != tt.wanted {
|
||||
t.Errorf("DecrementVotesOfCandidate(%v) | wanted %d, got %d",
|
||||
tt.results, tt.wanted, votes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func intPtrRepresentation(p *int) string {
|
||||
if p == nil {
|
||||
return "nil"
|
||||
}
|
||||
return "&" + strconv.Itoa(*p)
|
||||
}
|
9
go/election-day/election_result.go
Normal file
9
go/election-day/election_result.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package electionday
|
||||
|
||||
// ElectionResult represents an election result
|
||||
type ElectionResult struct {
|
||||
// Name of the candidate
|
||||
Name string
|
||||
// Number of votes the candidate had
|
||||
Votes int
|
||||
}
|
3
go/election-day/go.mod
Normal file
3
go/election-day/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module electionday
|
||||
|
||||
go 1.16
|
31
go/forth/.exercism/config.json
Normal file
31
go/forth/.exercism/config.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"blurb": "Implement an evaluator for a very simple subset of Forth",
|
||||
"authors": [
|
||||
"leenipper"
|
||||
],
|
||||
"contributors": [
|
||||
"alebaffa",
|
||||
"bitfield",
|
||||
"da-edra",
|
||||
"ekingery",
|
||||
"ferhatelmas",
|
||||
"hilary",
|
||||
"ilmanzo",
|
||||
"robphoenix",
|
||||
"sebito91"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"forth.go"
|
||||
],
|
||||
"test": [
|
||||
"forth_test.go"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.go"
|
||||
],
|
||||
"editor": [
|
||||
"cases_test.go"
|
||||
]
|
||||
}
|
||||
}
|
1
go/forth/.exercism/metadata.json
Normal file
1
go/forth/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"forth","id":"7fb99fd3f78d43c6a59dd057cac693da","url":"https://exercism.org/tracks/go/exercises/forth","handle":"halfdan","is_requester":true,"auto_approve":false}
|
40
go/forth/HELP.md
Normal file
40
go/forth/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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 forth.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)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- 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)
|
49
go/forth/README.md
Normal file
49
go/forth/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Forth
|
||||
|
||||
Welcome to Forth on Exercism's Go Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
Implement an evaluator for a very simple subset of Forth.
|
||||
|
||||
[Forth](https://en.wikipedia.org/wiki/Forth_%28programming_language%29)
|
||||
is a stack-based programming language. Implement a very basic evaluator
|
||||
for a small subset of Forth.
|
||||
|
||||
Your evaluator has to support the following words:
|
||||
|
||||
- `+`, `-`, `*`, `/` (integer arithmetic)
|
||||
- `DUP`, `DROP`, `SWAP`, `OVER` (stack manipulation)
|
||||
|
||||
Your evaluator also has to support defining new words using the
|
||||
customary syntax: `: word-name definition ;`.
|
||||
|
||||
To keep things simple the only data type you need to support is signed
|
||||
integers of at least 16 bits size.
|
||||
|
||||
You should use the following rules for the syntax: a number is a
|
||||
sequence of one or more (ASCII) digits, a word is a sequence of one or
|
||||
more letters, digits, symbols or punctuation that is not a number.
|
||||
(Forth probably uses slightly different rules, but this is close
|
||||
enough.)
|
||||
|
||||
Words are case-insensitive.
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @leenipper
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @alebaffa
|
||||
- @bitfield
|
||||
- @da-edra
|
||||
- @ekingery
|
||||
- @ferhatelmas
|
||||
- @hilary
|
||||
- @ilmanzo
|
||||
- @robphoenix
|
||||
- @sebito91
|
309
go/forth/cases_test.go
Normal file
309
go/forth/cases_test.go
Normal file
@@ -0,0 +1,309 @@
|
||||
package forth
|
||||
|
||||
// Source: exercism/problem-specifications
|
||||
// Commit: 75f4c0a Corrected minor typos in the error msg expectation (doesn't match other similar error patterns and so breaks auto generated tests)
|
||||
// Problem Specifications Version: 1.7.1
|
||||
|
||||
type testGroup struct {
|
||||
group string
|
||||
tests []testCase
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
description string
|
||||
input []string
|
||||
expected []int // nil slice indicates error expected.
|
||||
}
|
||||
|
||||
var testGroups = []testGroup{
|
||||
{
|
||||
group: "parsing and numbers",
|
||||
tests: []testCase{
|
||||
{
|
||||
"numbers just get pushed onto the stack",
|
||||
[]string{"1 2 3 4 5"},
|
||||
[]int{1, 2, 3, 4, 5},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "addition",
|
||||
tests: []testCase{
|
||||
{
|
||||
"can add two numbers",
|
||||
[]string{"1 2 +"},
|
||||
[]int{3},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"+"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is only one value on the stack",
|
||||
[]string{"1 +"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "subtraction",
|
||||
tests: []testCase{
|
||||
{
|
||||
"can subtract two numbers",
|
||||
[]string{"3 4 -"},
|
||||
[]int{-1},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"-"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is only one value on the stack",
|
||||
[]string{"1 -"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "multiplication",
|
||||
tests: []testCase{
|
||||
{
|
||||
"can multiply two numbers",
|
||||
[]string{"2 4 *"},
|
||||
[]int{8},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"*"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is only one value on the stack",
|
||||
[]string{"1 *"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "division",
|
||||
tests: []testCase{
|
||||
{
|
||||
"can divide two numbers",
|
||||
[]string{"12 3 /"},
|
||||
[]int{4},
|
||||
},
|
||||
{
|
||||
"performs integer division",
|
||||
[]string{"8 3 /"},
|
||||
[]int{2},
|
||||
},
|
||||
{
|
||||
"errors if dividing by zero",
|
||||
[]string{"4 0 /"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"/"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is only one value on the stack",
|
||||
[]string{"1 /"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "combined arithmetic",
|
||||
tests: []testCase{
|
||||
{
|
||||
"addition and subtraction",
|
||||
[]string{"1 2 + 4 -"},
|
||||
[]int{-1},
|
||||
},
|
||||
{
|
||||
"multiplication and division",
|
||||
[]string{"2 4 * 3 /"},
|
||||
[]int{2},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "dup",
|
||||
tests: []testCase{
|
||||
{
|
||||
"copies a value on the stack",
|
||||
[]string{"1 dup"},
|
||||
[]int{1, 1},
|
||||
},
|
||||
{
|
||||
"copies the top value on the stack",
|
||||
[]string{"1 2 dup"},
|
||||
[]int{1, 2, 2},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"dup"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "drop",
|
||||
tests: []testCase{
|
||||
{
|
||||
"removes the top value on the stack if it is the only one",
|
||||
[]string{"1 drop"},
|
||||
[]int{},
|
||||
},
|
||||
{
|
||||
"removes the top value on the stack if it is not the only one",
|
||||
[]string{"1 2 drop"},
|
||||
[]int{1},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"drop"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "swap",
|
||||
tests: []testCase{
|
||||
{
|
||||
"swaps the top two values on the stack if they are the only ones",
|
||||
[]string{"1 2 swap"},
|
||||
[]int{2, 1},
|
||||
},
|
||||
{
|
||||
"swaps the top two values on the stack if they are not the only ones",
|
||||
[]string{"1 2 3 swap"},
|
||||
[]int{1, 3, 2},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"swap"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is only one value on the stack",
|
||||
[]string{"1 swap"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "over",
|
||||
tests: []testCase{
|
||||
{
|
||||
"copies the second element if there are only two",
|
||||
[]string{"1 2 over"},
|
||||
[]int{1, 2, 1},
|
||||
},
|
||||
{
|
||||
"copies the second element if there are more than two",
|
||||
[]string{"1 2 3 over"},
|
||||
[]int{1, 2, 3, 2},
|
||||
},
|
||||
{
|
||||
"errors if there is nothing on the stack",
|
||||
[]string{"over"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if there is only one value on the stack",
|
||||
[]string{"1 over"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "user-defined words",
|
||||
tests: []testCase{
|
||||
{
|
||||
"can consist of built-in words",
|
||||
[]string{": dup-twice dup dup ;", "1 dup-twice"},
|
||||
[]int{1, 1, 1},
|
||||
},
|
||||
{
|
||||
"execute in the right order",
|
||||
[]string{": countup 1 2 3 ;", "countup"},
|
||||
[]int{1, 2, 3},
|
||||
},
|
||||
{
|
||||
"can override other user-defined words",
|
||||
[]string{": foo dup ;", ": foo dup dup ;", "1 foo"},
|
||||
[]int{1, 1, 1},
|
||||
},
|
||||
{
|
||||
"can override built-in words",
|
||||
[]string{": swap dup ;", "1 swap"},
|
||||
[]int{1, 1},
|
||||
},
|
||||
{
|
||||
"can override built-in operators",
|
||||
[]string{": + * ;", "3 4 +"},
|
||||
[]int{12},
|
||||
},
|
||||
{
|
||||
"can use different words with the same name",
|
||||
[]string{": foo 5 ;", ": bar foo ;", ": foo 6 ;", "bar foo"},
|
||||
[]int{5, 6},
|
||||
},
|
||||
{
|
||||
"can define word that uses word with the same name",
|
||||
[]string{": foo 10 ;", ": foo foo 1 + ;", "foo"},
|
||||
[]int{11},
|
||||
},
|
||||
{
|
||||
"cannot redefine numbers",
|
||||
[]string{": 1 2 ;"},
|
||||
[]int(nil),
|
||||
},
|
||||
{
|
||||
"errors if executing a non-existent word",
|
||||
[]string{"foo"},
|
||||
[]int(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
group: "case-insensitivity",
|
||||
tests: []testCase{
|
||||
{
|
||||
"DUP is case-insensitive",
|
||||
[]string{"1 DUP Dup dup"},
|
||||
[]int{1, 1, 1, 1},
|
||||
},
|
||||
{
|
||||
"DROP is case-insensitive",
|
||||
[]string{"1 2 3 4 DROP Drop drop"},
|
||||
[]int{1},
|
||||
},
|
||||
{
|
||||
"SWAP is case-insensitive",
|
||||
[]string{"1 2 SWAP 3 Swap 4 swap"},
|
||||
[]int{2, 3, 4, 1},
|
||||
},
|
||||
{
|
||||
"OVER is case-insensitive",
|
||||
[]string{"1 2 OVER Over over"},
|
||||
[]int{1, 2, 1, 2, 1},
|
||||
},
|
||||
{
|
||||
"user-defined words are case-insensitive",
|
||||
[]string{": foo dup ;", "1 FOO Foo foo"},
|
||||
[]int{1, 1, 1, 1},
|
||||
},
|
||||
{
|
||||
"definitions are case-insensitive",
|
||||
[]string{": SWAP DUP Dup dup ;", "1 swap"},
|
||||
[]int{1, 1, 1, 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
5
go/forth/forth.go
Normal file
5
go/forth/forth.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package forth
|
||||
|
||||
func Forth(input []string) ([]int, error) {
|
||||
panic("Please implement the Forth function")
|
||||
}
|
40
go/forth/forth_test.go
Normal file
40
go/forth/forth_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package forth
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestForth(t *testing.T) {
|
||||
for _, tg := range testGroups {
|
||||
for _, tc := range tg.tests {
|
||||
if v, err := Forth(tc.input); err == nil {
|
||||
var _ error = err
|
||||
if tc.expected == nil {
|
||||
t.Fatalf("FAIL: %s | %s\n\tForth(%#v) expected an error, got %v",
|
||||
tg.group, tc.description, tc.input, v)
|
||||
} else if !reflect.DeepEqual(v, tc.expected) {
|
||||
t.Fatalf("FAIL: %s | %s\n\tForth(%#v) expected %v, got %v",
|
||||
tg.group, tc.description, tc.input, tc.expected, v)
|
||||
}
|
||||
} else if tc.expected != nil {
|
||||
t.Fatalf("FAIL: %s | %s\n\tForth(%#v) expected %v, got an error: %q",
|
||||
tg.group, tc.description, tc.input, tc.expected, err)
|
||||
}
|
||||
t.Logf("PASS: %s | %s", tg.group, tc.description)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkForth(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, tg := range testGroups {
|
||||
for _, tc := range tg.tests {
|
||||
Forth(tc.input)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
go/forth/go.mod
Normal file
3
go/forth/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module forth
|
||||
|
||||
go 1.16
|
24
go/forth/stack.go
Normal file
24
go/forth/stack.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package forth
|
||||
|
||||
import "fmt"
|
||||
|
||||
|
||||
|
||||
type Stack struct {
|
||||
stack []int
|
||||
ptr int
|
||||
}
|
||||
|
||||
func (s *Stack) Push(k int) {
|
||||
s.stack = append(s.stack, k)
|
||||
}
|
||||
|
||||
func (s *Stack) Pop() (int, error) {
|
||||
if len(s.stack) == 0 {
|
||||
return 0, fmt.Errorf("cannot pop empty stack")
|
||||
}
|
||||
|
||||
el := s.stack[len(s.stack)-1]
|
||||
s.stack = s.stack[:len(s.stack)-1]
|
||||
return el, nil
|
||||
}
|
40
go/grains/.exercism/config.json
Normal file
40
go/grains/.exercism/config.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"blurb": "Calculate the number of grains of wheat on a chessboard given that the number on each square doubles.",
|
||||
"authors": [
|
||||
"nathany"
|
||||
],
|
||||
"contributors": [
|
||||
"alebaffa",
|
||||
"bitfield",
|
||||
"da-edra",
|
||||
"dvrkps",
|
||||
"ekingery",
|
||||
"ferhatelmas",
|
||||
"hilary",
|
||||
"ilmanzo",
|
||||
"johngb",
|
||||
"kytrinyx",
|
||||
"leenipper",
|
||||
"petertseng",
|
||||
"robphoenix",
|
||||
"sebito91",
|
||||
"strangeman",
|
||||
"tleen"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"grains.go"
|
||||
],
|
||||
"test": [
|
||||
"grains_test.go"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.go"
|
||||
],
|
||||
"editor": [
|
||||
"cases_test.go"
|
||||
]
|
||||
},
|
||||
"source": "JavaRanch Cattle Drive, exercise 6",
|
||||
"source_url": "http://www.javaranch.com/grains.jsp"
|
||||
}
|
1
go/grains/.exercism/metadata.json
Normal file
1
go/grains/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"grains","id":"4d2b822b76a64e4790a85240381853a7","url":"https://exercism.org/tracks/go/exercises/grains","handle":"halfdan","is_requester":true,"auto_approve":false}
|
40
go/grains/HELP.md
Normal file
40
go/grains/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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 grains.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)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- 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)
|
62
go/grains/README.md
Normal file
62
go/grains/README.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Grains
|
||||
|
||||
Welcome to Grains on Exercism's Go Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
Calculate the number of grains of wheat on a chessboard given that the number
|
||||
on each square doubles.
|
||||
|
||||
There once was a wise servant who saved the life of a prince. The king
|
||||
promised to pay whatever the servant could dream up. Knowing that the
|
||||
king loved chess, the servant told the king he would like to have grains
|
||||
of wheat. One grain on the first square of a chess board, with the number
|
||||
of grains doubling on each successive square.
|
||||
|
||||
There are 64 squares on a chessboard (where square 1 has one grain, square 2 has two grains, and so on).
|
||||
|
||||
Write code that shows:
|
||||
|
||||
- how many grains were on a given square, and
|
||||
- the total number of grains on the chessboard
|
||||
|
||||
## For bonus points
|
||||
|
||||
Did you get the tests passing and the code clean? If you want to, these
|
||||
are some additional things you could try:
|
||||
|
||||
- Optimize for speed.
|
||||
- Optimize for readability.
|
||||
|
||||
Then please share your thoughts in a comment on the submission. Did this
|
||||
experiment make the code better? Worse? Did you learn anything from it?
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @nathany
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @alebaffa
|
||||
- @bitfield
|
||||
- @da-edra
|
||||
- @dvrkps
|
||||
- @ekingery
|
||||
- @ferhatelmas
|
||||
- @hilary
|
||||
- @ilmanzo
|
||||
- @johngb
|
||||
- @kytrinyx
|
||||
- @leenipper
|
||||
- @petertseng
|
||||
- @robphoenix
|
||||
- @sebito91
|
||||
- @strangeman
|
||||
- @tleen
|
||||
|
||||
### Based on
|
||||
|
||||
JavaRanch Cattle Drive, exercise 6 - http://www.javaranch.com/grains.jsp
|
64
go/grains/cases_test.go
Normal file
64
go/grains/cases_test.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package grains
|
||||
|
||||
// Source: exercism/problem-specifications
|
||||
// Commit: 2ec42ab Grains: Fixed canonical data to have standard error indicator (#1322)
|
||||
// Problem Specifications Version: 1.2.0
|
||||
|
||||
// returns the number of grains on the square
|
||||
var squareTests = []struct {
|
||||
description string
|
||||
input int
|
||||
expectedVal uint64
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
description: "1",
|
||||
input: 1,
|
||||
expectedVal: 1,
|
||||
},
|
||||
{
|
||||
description: "2",
|
||||
input: 2,
|
||||
expectedVal: 2,
|
||||
},
|
||||
{
|
||||
description: "3",
|
||||
input: 3,
|
||||
expectedVal: 4,
|
||||
},
|
||||
{
|
||||
description: "4",
|
||||
input: 4,
|
||||
expectedVal: 8,
|
||||
},
|
||||
{
|
||||
description: "16",
|
||||
input: 16,
|
||||
expectedVal: 32768,
|
||||
},
|
||||
{
|
||||
description: "32",
|
||||
input: 32,
|
||||
expectedVal: 2147483648,
|
||||
},
|
||||
{
|
||||
description: "64",
|
||||
input: 64,
|
||||
expectedVal: 9223372036854775808,
|
||||
},
|
||||
{
|
||||
description: "square 0 returns an error",
|
||||
input: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
description: "negative square returns an error",
|
||||
input: -1,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
description: "square greater than 64 returns an error",
|
||||
input: 65,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
3
go/grains/go.mod
Normal file
3
go/grains/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module grains
|
||||
|
||||
go 1.13
|
16
go/grains/grains.go
Normal file
16
go/grains/grains.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package grains
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Square(number int) (uint64, error) {
|
||||
if number < 1 || number > 64 {
|
||||
return 0, fmt.Errorf("bad square %d (must be between 1-64)", number)
|
||||
}
|
||||
return 1<<(number - 1), nil
|
||||
}
|
||||
|
||||
func Total() uint64 {
|
||||
return 1<<64 - 1
|
||||
}
|
57
go/grains/grains_test.go
Normal file
57
go/grains/grains_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package grains
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSquare(t *testing.T) {
|
||||
for _, test := range squareTests {
|
||||
actualVal, actualErr := Square(test.input)
|
||||
|
||||
// check actualVal only if no error expected
|
||||
if !test.expectError && actualVal != test.expectedVal {
|
||||
t.Fatalf("FAIL: %s\nSquare(%d) expected %d, Actual %d", test.description, test.input, test.expectedVal, actualVal)
|
||||
}
|
||||
|
||||
// if we expect an error and there isn't one
|
||||
if test.expectError && actualErr == nil {
|
||||
t.Fatalf("FAIL: %s\nSquare(%d) expected an error, but error is nil", test.description, test.input)
|
||||
}
|
||||
// if we don't expect an error and there is one
|
||||
if !test.expectError && actualErr != nil {
|
||||
var _ error = actualErr
|
||||
t.Fatalf("FAIL: %s\nSquare(%d) expected no error, but error is: %s", test.description, test.input, actualErr)
|
||||
}
|
||||
t.Logf("PASS: %s", test.description)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTotal(t *testing.T) {
|
||||
var expected uint64 = 18446744073709551615
|
||||
if actual := Total(); actual != expected {
|
||||
t.Errorf("Total() expected %d, Actual %d", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSquare(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
for _, test := range squareTests {
|
||||
Square(test.input)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTotal(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
Total()
|
||||
}
|
||||
}
|
20
go/gross-store/.exercism/config.json
Normal file
20
go/gross-store/.exercism/config.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"blurb": "Learn about maps by selling items by the dozen at the Gross Store.",
|
||||
"authors": [
|
||||
"chocopowwwa"
|
||||
],
|
||||
"contributors": [
|
||||
"MiroslavGatsanoga"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"gross_store.go"
|
||||
],
|
||||
"test": [
|
||||
"gross_store_test.go"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.go"
|
||||
]
|
||||
}
|
||||
}
|
1
go/gross-store/.exercism/metadata.json
Normal file
1
go/gross-store/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"gross-store","id":"7bfbc74502004cb3867b5bf345b7b1b0","url":"https://exercism.org/tracks/go/exercises/gross-store","handle":"halfdan","is_requester":true,"auto_approve":false}
|
40
go/gross-store/HELP.md
Normal file
40
go/gross-store/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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 gross_store.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)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- 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)
|
31
go/gross-store/HINTS.md
Normal file
31
go/gross-store/HINTS.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- [Go by example map][gobyexample-map]
|
||||
- [Go maps in action][goblog-map]
|
||||
|
||||
## 1. Store the unit of measurement in your program
|
||||
|
||||
- To store the measurement in your program, you can use map literal, see [go blog about map][goblog-map]
|
||||
|
||||
## 2. Create a new bill
|
||||
|
||||
- To create a new bill, you all you need to do is reinitialize the customer, see [go blog about map][goblog-map]
|
||||
|
||||
## 3. Add item to the customer bill
|
||||
|
||||
- To check whether the given unit of measurement is correct, you can test your measurement map for a key without retrieving a value, see [go blog about map][goblog-map]
|
||||
|
||||
## 4. Remove item from the customer bill
|
||||
|
||||
- To check whether the given item is in customer bill, you can test your measurement map for a key without retrieving a value, see [go blog about map][goblog-map]
|
||||
|
||||
- To check whether the given unit of measurement is correct, you can test your measurement map for a key without retrieving a value, see [go blog about map][goblog-map]
|
||||
|
||||
## 5. Return the number of specific item that is in the customer bill
|
||||
|
||||
- To check whether the given item is in customer bill, you can test your measurement map for a key without retrieving a value, see [go blog about map][goblog-map]
|
||||
|
||||
[gobyexample-map]: https://gobyexample.com/maps
|
||||
[goblog-map]: https://blog.golang.org/maps
|
159
go/gross-store/README.md
Normal file
159
go/gross-store/README.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# Gross Store
|
||||
|
||||
Welcome to Gross Store 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
|
||||
|
||||
In go, `map` is a built-in data type that maps keys to values. In other programming language, you might be familiar with the concept of `map` as a dictionary, hash table, key/value store or an associative array.
|
||||
|
||||
Syntactically, `map` looks like this:
|
||||
|
||||
```go
|
||||
map[KeyType]ElementType
|
||||
```
|
||||
|
||||
It is also important to know that each key is unique, meaning that assigning the same key twice will overwrite the value of the corresponding key.
|
||||
|
||||
To create a map, you can do:
|
||||
|
||||
```go
|
||||
// With map literal
|
||||
foo := map[string]int{}
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```go
|
||||
// or with make function
|
||||
foo := make(map[string]int)
|
||||
```
|
||||
|
||||
Here are some operations that you can do with a map
|
||||
|
||||
```go
|
||||
// Add a value in a map with the `=` operator:
|
||||
foo["bar"] = 42
|
||||
// Here we update the element of `bar`
|
||||
foo["bar"] = 73
|
||||
// To retrieve a map value, you can use
|
||||
baz := foo["bar"]
|
||||
// To delete an item from a map, you can use
|
||||
delete(foo, "bar")
|
||||
```
|
||||
|
||||
If you try to retrieve the value for a key which does not exist in the map, it will return the zero value of the value type.
|
||||
This can confuse you, especially if the default value of your `ElementType` (for example, 0 for an int), is a valid value.
|
||||
To check whether a key exists in your map, you can use
|
||||
|
||||
```go
|
||||
value, exists := foo["baz"]
|
||||
// If the key "baz" does not exist,
|
||||
// value: 0; exists: false
|
||||
```
|
||||
|
||||
## Instructions
|
||||
|
||||
A friend of yours has an old wholesale store called **Gross Store**.
|
||||
The name comes from the quantity of the item that the store sell: it's all in [gross unit][gross-unit].
|
||||
Your friend asked you to implement a point of sale (POS) system for his store.
|
||||
**First, you want to build a prototype for it.**
|
||||
**In your prototype, your system will only record the quantity.**
|
||||
Your friend gave you a list of measurements to help you:
|
||||
|
||||
| Unit | Score |
|
||||
| ------------------ | ----- |
|
||||
| quarter_of_a_dozen | 3 |
|
||||
| half_of_a_dozen | 6 |
|
||||
| dozen | 12 |
|
||||
| small_gross | 120 |
|
||||
| gross | 144 |
|
||||
| great_gross | 1728 |
|
||||
|
||||
## 1. Store the unit of measurement in your program
|
||||
|
||||
In order to use the measurement, you need to store the measurement in your program.
|
||||
|
||||
```go
|
||||
units := Units()
|
||||
fmt.Println(units)
|
||||
// Output: map[...] with entries like ("dozen": 12)
|
||||
```
|
||||
|
||||
## 2. Create a new customer bill
|
||||
|
||||
You need to implement a function that create a new (empty) bill for the customer.
|
||||
|
||||
```go
|
||||
bill := NewBill()
|
||||
fmt.Println(bill)
|
||||
// Output: map[]
|
||||
```
|
||||
|
||||
## 3. Add an item to the customer bill
|
||||
|
||||
To implement this, you'll need to:
|
||||
|
||||
- Return `false` if the given `unit` is not in the `units` map.
|
||||
- Otherwise add the item to the customer `bill`, indexed by the item name, then return `true`.
|
||||
|
||||
```go
|
||||
bill := NewBill()
|
||||
units := Units()
|
||||
ok := AddItem(bill, units, "carrot", "dozen")
|
||||
fmt.Println(ok)
|
||||
// Output: true (since dozen is a valid unit)
|
||||
```
|
||||
|
||||
> Note that the returned value is type `bool`.
|
||||
|
||||
## 4. Remove an item from the customer bill
|
||||
|
||||
To implement this, you'll need to:
|
||||
|
||||
- Return `false` if the given item is **not** in the bill
|
||||
- Return `false` if the given `unit` is not in the `units` map.
|
||||
- Return `false` if the new quantity would be less than 0.
|
||||
- If the new quantity is 0, completely remove the item from the `bill` then return `true`.
|
||||
- Otherwise, reduce the quantity of the item and return `true`.
|
||||
|
||||
```go
|
||||
bill := NewBill()
|
||||
units := Units()
|
||||
ok := RemoveItem(bill, units, "carrot", "dozen")
|
||||
fmt.Println(ok)
|
||||
// Output: false (because there are no carrots in the bill)
|
||||
```
|
||||
|
||||
> Note that the returned value is type `bool`.
|
||||
|
||||
## 5. Return the quantity of a specific item that is in the customer bill
|
||||
|
||||
To implement this, you'll need to:
|
||||
|
||||
- Return `0` and `false` if the `item` is not in the bill.
|
||||
- Otherwise, return the quantity of the item in the `bill` and `true`.
|
||||
|
||||
```go
|
||||
bill := map[string]int{"carrot", 12, "grapes", 3}
|
||||
qty, ok := GetItem(bill, "carrot")
|
||||
fmt.Println(qty)
|
||||
// Output: 12
|
||||
fmt.Println(ok)
|
||||
// Output: true
|
||||
```
|
||||
|
||||
> Note that the returned value are types `int` and `bool`.
|
||||
|
||||
[gross-unit]: https://en.wikipedia.org/wiki/Gross_(unit)
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @chocopowwwa
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @MiroslavGatsanoga
|
3
go/gross-store/go.mod
Normal file
3
go/gross-store/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module gross
|
||||
|
||||
go 1.16
|
59
go/gross-store/gross_store.go
Normal file
59
go/gross-store/gross_store.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package gross
|
||||
|
||||
// Units stores the Gross Store unit measurements.
|
||||
func Units() map[string]int {
|
||||
return map[string]int{
|
||||
"quarter_of_a_dozen": 3,
|
||||
"half_of_a_dozen": 6,
|
||||
"dozen": 12,
|
||||
"small_gross": 120,
|
||||
"gross": 144,
|
||||
"great_gross": 1728,
|
||||
}
|
||||
}
|
||||
|
||||
// NewBill creates a new bill.
|
||||
func NewBill() map[string]int {
|
||||
return map[string]int{}
|
||||
}
|
||||
|
||||
// AddItem adds an item to customer bill.
|
||||
func AddItem(bill, units map[string]int, item, unit string) bool {
|
||||
val, exists := units[unit]
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
|
||||
bill[item] += val
|
||||
return true
|
||||
}
|
||||
|
||||
// RemoveItem removes an item from customer bill.
|
||||
func RemoveItem(bill, units map[string]int, item, unit string) bool {
|
||||
qty, exists := bill[item]
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
|
||||
val, exists := units[unit]
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
|
||||
if qty - val < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if qty - val == 0 {
|
||||
delete(bill, item)
|
||||
} else {
|
||||
bill[item] -= val
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// GetItem returns the quantity of an item that the customer has in his/her bill.
|
||||
func GetItem(bill map[string]int, item string) (int, bool) {
|
||||
qty, exists := bill[item]
|
||||
return qty, exists
|
||||
}
|
255
go/gross-store/gross_store_test.go
Normal file
255
go/gross-store/gross_store_test.go
Normal file
@@ -0,0 +1,255 @@
|
||||
package gross
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type entry struct {
|
||||
name string
|
||||
unit string
|
||||
qty int
|
||||
}
|
||||
|
||||
func TestUnits(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
qty int
|
||||
}{
|
||||
{"quarter_of_a_dozen", 3},
|
||||
{"half_of_a_dozen", 6},
|
||||
{"dozen", 12},
|
||||
{"small_gross", 120},
|
||||
{"gross", 144},
|
||||
{"great_gross", 1728},
|
||||
}
|
||||
|
||||
units := Units()
|
||||
for _, tt := range tests {
|
||||
qty, ok := units[tt.name]
|
||||
|
||||
if !ok {
|
||||
t.Errorf(`Unit "%s" not found!`, tt.name)
|
||||
continue
|
||||
}
|
||||
|
||||
if qty != tt.qty {
|
||||
t.Errorf(`Unit "%s" should have quantity %d, found %d`, tt.name, tt.qty, qty)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
func TestAddItem(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
entry []entry
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
"Invalid measurement unit",
|
||||
[]entry{
|
||||
{"pasta", "", 0},
|
||||
{"onion", "quarter", 0},
|
||||
{"pasta", "pound", 0},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Valid measurement unit",
|
||||
[]entry{
|
||||
{"peas", "quarter_of_a_dozen", 3},
|
||||
{"tomato", "half_of_a_dozen", 6},
|
||||
{"chili", "dozen", 12},
|
||||
{"cucumber", "small_gross", 120},
|
||||
{"potato", "gross", 144},
|
||||
{"zucchini", "great_gross", 1728},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"check quantity of item added twice",
|
||||
[]entry{
|
||||
{"peas", "quarter_of_a_dozen", 3},
|
||||
{"peas", "quarter_of_a_dozen", 6},
|
||||
{"tomato", "half_of_a_dozen", 6},
|
||||
{"tomato", "quarter_of_a_dozen", 9},
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
units := Units()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bill := NewBill()
|
||||
for _, item := range tt.entry {
|
||||
ok := AddItem(bill, units, item.name, item.unit)
|
||||
if ok != tt.expected {
|
||||
t.Errorf("Expected %t from AddItem, found %t at %v", tt.expected, ok, item.name)
|
||||
}
|
||||
|
||||
itemQty, ok := bill[item.name]
|
||||
if ok != tt.expected {
|
||||
t.Errorf("Could not find item %s in customer bill", item.name)
|
||||
}
|
||||
|
||||
if itemQty != item.qty {
|
||||
t.Errorf("Expected %s to have quantity %d in customer bill, found %d", item.name, item.qty, itemQty)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveItem(t *testing.T) {
|
||||
type expectedItem struct {
|
||||
name string
|
||||
unit string
|
||||
qty int
|
||||
exists bool
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
remove []expectedItem
|
||||
expected bool
|
||||
}{
|
||||
{"Item Not found in bill",
|
||||
[]expectedItem{
|
||||
{"papaya", "gross", 0, false},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{"Invalid measurement unit",
|
||||
[]expectedItem{
|
||||
{"peas", "pound", 3, true},
|
||||
{"tomato", "kilogram", 6, true},
|
||||
{"cucumber", "stone", 120, true},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{"Resulted qty less than 0",
|
||||
[]expectedItem{
|
||||
{"peas", "half_of_a_dozen", 3, true},
|
||||
{"tomato", "dozen", 6, true},
|
||||
{"chili", "small_gross", 12, true},
|
||||
{"cucumber", "gross", 120, true},
|
||||
{"potato", "great_gross", 144, true},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{"Should delete the item if 0",
|
||||
[]expectedItem{
|
||||
{"peas", "quarter_of_a_dozen", 0, false},
|
||||
{"tomato", "half_of_a_dozen", 0, false},
|
||||
{"chili", "dozen", 0, false},
|
||||
{"cucumber", "small_gross", 0, false},
|
||||
{"potato", "gross", 0, false},
|
||||
{"zucchini", "great_gross", 0, false},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{"Should reduce the qty",
|
||||
[]expectedItem{
|
||||
{"chili", "half_of_a_dozen", 6, true},
|
||||
{"cucumber", "dozen", 108, true},
|
||||
{"zucchini", "gross", 1584, true},
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
units := Units()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bill := setupInitialBillData()
|
||||
for _, item := range tt.remove {
|
||||
ok := RemoveItem(bill, units, item.name, item.unit)
|
||||
if ok != tt.expected {
|
||||
t.Errorf("Expected %t from RemoveItem, found %t at %v", tt.expected, ok, item.name)
|
||||
}
|
||||
|
||||
itemQty, ok := bill[item.name]
|
||||
if ok != item.exists {
|
||||
t.Errorf("Could not find item %s in customer bill", item.name)
|
||||
}
|
||||
if itemQty != item.qty {
|
||||
t.Errorf("Expected %s to have quantity %d in customer bill, found %d", item.name, item.qty, itemQty)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewBill(t *testing.T) {
|
||||
// Success, zero out the bill
|
||||
t.Run("Should reset customerbill", func(t *testing.T) {
|
||||
bill := NewBill()
|
||||
|
||||
if len(bill) != 0 {
|
||||
t.Error("Customer bill must be empty")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetItem(t *testing.T) {
|
||||
type expectedItem struct {
|
||||
name string
|
||||
expected bool
|
||||
qty int
|
||||
}
|
||||
|
||||
test := []struct {
|
||||
name string
|
||||
getItem []expectedItem
|
||||
}{
|
||||
{
|
||||
"Item Not found in bill",
|
||||
[]expectedItem{
|
||||
{"grape", false, 0},
|
||||
},
|
||||
},
|
||||
{
|
||||
"Success",
|
||||
[]expectedItem{
|
||||
{"peas", true, 3},
|
||||
{"tomato", true, 6},
|
||||
{"chili", true, 12},
|
||||
{"cucumber", true, 120},
|
||||
{"potato", true, 144},
|
||||
{"zucchini", true, 1728},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range test {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bill := setupInitialBillData()
|
||||
for _, item := range tt.getItem {
|
||||
itemQty, ok := GetItem(bill, item.name)
|
||||
|
||||
if ok != item.expected {
|
||||
msg := "Could not find item %s in customer bill, expected %t"
|
||||
if item.expected == false {
|
||||
msg = "Found item %s in customer bill, expected %t"
|
||||
}
|
||||
|
||||
t.Errorf(msg, item.name, item.expected)
|
||||
}
|
||||
|
||||
if itemQty != item.qty {
|
||||
t.Errorf("Expected %s to have quantity %d in customer bill, found %d", item.name, item.qty, itemQty)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setupInitialBillData() map[string]int {
|
||||
bill := NewBill()
|
||||
bill["peas"] = 3
|
||||
bill["tomato"] = 6
|
||||
bill["chili"] = 12
|
||||
bill["cucumber"] = 120
|
||||
bill["potato"] = 144
|
||||
bill["zucchini"] = 1728
|
||||
return bill
|
||||
}
|
33
go/isbn-verifier/.exercism/config.json
Normal file
33
go/isbn-verifier/.exercism/config.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"blurb": "Check if a given string is a valid ISBN-10 number.",
|
||||
"authors": [
|
||||
"creaaa"
|
||||
],
|
||||
"contributors": [
|
||||
"bitfield",
|
||||
"ekingery",
|
||||
"ferhatelmas",
|
||||
"h311ion",
|
||||
"hilary",
|
||||
"kytrinyx",
|
||||
"leenipper",
|
||||
"sebito91",
|
||||
"tinsch"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"isbn_verifier.go"
|
||||
],
|
||||
"test": [
|
||||
"isbn_verifier_test.go"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.go"
|
||||
],
|
||||
"editor": [
|
||||
"cases_test.go"
|
||||
]
|
||||
},
|
||||
"source": "Converting a string into a number and some basic processing utilizing a relatable real world example.",
|
||||
"source_url": "https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation"
|
||||
}
|
1
go/isbn-verifier/.exercism/metadata.json
Normal file
1
go/isbn-verifier/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"isbn-verifier","id":"c2a9f9ef655f45b1b42a5530b24252d1","url":"https://exercism.org/tracks/go/exercises/isbn-verifier","handle":"halfdan","is_requester":true,"auto_approve":false}
|
40
go/isbn-verifier/HELP.md
Normal file
40
go/isbn-verifier/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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 isbn_verifier.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)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- 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)
|
69
go/isbn-verifier/README.md
Normal file
69
go/isbn-verifier/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Isbn Verifier
|
||||
|
||||
Welcome to Isbn Verifier on Exercism's Go Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
The [ISBN-10 verification process](https://en.wikipedia.org/wiki/International_Standard_Book_Number) is used to validate book identification
|
||||
numbers. These normally contain dashes and look like: `3-598-21508-8`
|
||||
|
||||
## ISBN
|
||||
|
||||
The ISBN-10 format is 9 digits (0 to 9) plus one check character (either a digit or an X only). In the case the check character is an X, this represents the value '10'. These may be communicated with or without hyphens, and can be checked for their validity by the following formula:
|
||||
|
||||
```text
|
||||
(x1 * 10 + x2 * 9 + x3 * 8 + x4 * 7 + x5 * 6 + x6 * 5 + x7 * 4 + x8 * 3 + x9 * 2 + x10 * 1) mod 11 == 0
|
||||
```
|
||||
|
||||
If the result is 0, then it is a valid ISBN-10, otherwise it is invalid.
|
||||
|
||||
## Example
|
||||
|
||||
Let's take the ISBN-10 `3-598-21508-8`. We plug it in to the formula, and get:
|
||||
|
||||
```text
|
||||
(3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 == 0
|
||||
```
|
||||
|
||||
Since the result is 0, this proves that our ISBN is valid.
|
||||
|
||||
## Task
|
||||
|
||||
Given a string the program should check if the provided string is a valid ISBN-10.
|
||||
Putting this into place requires some thinking about preprocessing/parsing of the string prior to calculating the check digit for the ISBN.
|
||||
|
||||
The program should be able to verify ISBN-10 both with and without separating dashes.
|
||||
|
||||
## Caveats
|
||||
|
||||
Converting from strings to numbers can be tricky in certain languages.
|
||||
Now, it's even trickier since the check digit of an ISBN-10 may be 'X' (representing '10'). For instance `3-598-21507-X` is a valid ISBN-10.
|
||||
|
||||
## Bonus tasks
|
||||
|
||||
* Generate a valid ISBN-13 from the input ISBN-10 (and maybe verify it again with a derived verifier).
|
||||
|
||||
* Generate valid ISBN, maybe even from a given starting ISBN.
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @creaaa
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @bitfield
|
||||
- @ekingery
|
||||
- @ferhatelmas
|
||||
- @h311ion
|
||||
- @hilary
|
||||
- @kytrinyx
|
||||
- @leenipper
|
||||
- @sebito91
|
||||
- @tinsch
|
||||
|
||||
### Based on
|
||||
|
||||
Converting a string into a number and some basic processing utilizing a relatable real world example. - https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation
|
29
go/isbn-verifier/cases_test.go
Normal file
29
go/isbn-verifier/cases_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package isbn
|
||||
|
||||
// Source: exercism/problem-specifications
|
||||
// Commit: 3134243 isbn-verifier: Crafted input to catch more incorrect algorithms (#1255)
|
||||
// Problem Specifications Version: 2.7.0
|
||||
|
||||
var testCases = []struct {
|
||||
isbn string
|
||||
expected bool
|
||||
description string
|
||||
}{
|
||||
{"3-598-21508-8", true, "valid isbn number"},
|
||||
{"3-598-21508-9", false, "invalid isbn check digit"},
|
||||
{"3-598-21507-X", true, "valid isbn number with a check digit of 10"},
|
||||
{"3-598-21507-A", false, "check digit is a character other than X"},
|
||||
{"3-598-P1581-X", false, "invalid character in isbn"},
|
||||
{"3-598-2X507-9", false, "X is only valid as a check digit"},
|
||||
{"3598215088", true, "valid isbn without separating dashes"},
|
||||
{"359821507X", true, "isbn without separating dashes and X as check digit"},
|
||||
{"359821507", false, "isbn without check digit and dashes"},
|
||||
{"3598215078X", false, "too long isbn and no dashes"},
|
||||
{"00", false, "too short isbn"},
|
||||
{"3-598-21507", false, "isbn without check digit"},
|
||||
{"3-598-21515-X", false, "check digit of X should not be used for 0"},
|
||||
{"", false, "empty isbn"},
|
||||
{"134456729", false, "input is 9 characters"},
|
||||
{"3132P34035", false, "invalid characters are not ignored"},
|
||||
{"98245726788", false, "input is too long but contains a valid isbn"},
|
||||
}
|
3
go/isbn-verifier/go.mod
Normal file
3
go/isbn-verifier/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module isbn
|
||||
|
||||
go 1.13
|
5
go/isbn-verifier/isbn_verifier.go
Normal file
5
go/isbn-verifier/isbn_verifier.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package isbn
|
||||
|
||||
func IsValidISBN(isbn string) bool {
|
||||
panic("Please implement the IsValidISBN function")
|
||||
}
|
28
go/isbn-verifier/isbn_verifier_test.go
Normal file
28
go/isbn-verifier/isbn_verifier_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package isbn
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsValidISBN(t *testing.T) {
|
||||
for _, test := range testCases {
|
||||
observed := IsValidISBN(test.isbn)
|
||||
if observed == test.expected {
|
||||
t.Logf("PASS: %s", test.description)
|
||||
} else {
|
||||
t.Errorf("FAIL: %s\nIsValidISBN(%q)\nExpected: %t, Actual: %t",
|
||||
test.description, test.isbn, test.expected, observed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIsValidISBN(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, n := range testCases {
|
||||
IsValidISBN(n.isbn)
|
||||
}
|
||||
}
|
||||
}
|
25
go/lasagna/.exercism/config.json
Normal file
25
go/lasagna/.exercism/config.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"blurb": "Learn about packages, functions, and variables by helping Gopher cook lasagna.",
|
||||
"authors": [
|
||||
"tehsphinx"
|
||||
],
|
||||
"contributors": [
|
||||
"ekingery",
|
||||
"andrerfcsantos",
|
||||
"bobtfish"
|
||||
],
|
||||
"forked_from": [
|
||||
"csharp/lucians-luscious-lasagna"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"lasagna.go"
|
||||
],
|
||||
"test": [
|
||||
"lasagna_test.go"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.go"
|
||||
]
|
||||
}
|
||||
}
|
1
go/lasagna/.exercism/metadata.json
Normal file
1
go/lasagna/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"lasagna","id":"e418a5b0bb904f5c9433c52db1a8bb0a","url":"https://exercism.org/tracks/go/exercises/lasagna","handle":"halfdan","is_requester":true,"auto_approve":false}
|
40
go/lasagna/HELP.md
Normal file
40
go/lasagna/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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 lasagna.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)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- 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)
|
44
go/lasagna/HINTS.md
Normal file
44
go/lasagna/HINTS.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- An [integer value][integers] can be defined as one or more consecutive digits.
|
||||
- If you see a `panic:` error when running the tests, this is because you have not implemented one of the functions (it should say which one) or you have left the boilerplate in place. You need to remove the `panic(...)` line from the supplied code and replace it with a real implementation.
|
||||
|
||||
## 1. Define the expected oven time in minutes
|
||||
|
||||
- You need to define a [constant][constants] and assign it the expected oven time in minutes.
|
||||
- If you see an `undefined: OvenTime` error then double check that you have the constant defined.
|
||||
- If you see an `invalid operation: got != tt.expected (mismatched types float64 and int)` error then you have likely put a decimal point into the `OvenTime` causing Go to infer the type as a floating point number. Remove the decimal and the type will be inferred as an `int`.
|
||||
- If you see a `syntax error: non-declaration statement outside function body` error then it is likely that you forgot the `const` keyword.
|
||||
- If you see a `syntax error: unexpected :=, expecting =` error then you are likely trying to assign the constant using `:=` like a variable; constants are assigned using `=` not `:=`.
|
||||
|
||||
## 2. Calculate the remaining oven time in minutes
|
||||
|
||||
- You need to define a [function][functions] with a single parameter.
|
||||
- You have to [explicitly return an integer][return] from a function.
|
||||
- The function's parameter is an [integer][integers].
|
||||
- You can [call][calls] one of the other functions you've defined previously.
|
||||
- You can use the [mathematical operator for subtraction][operators] to subtract values.
|
||||
|
||||
## 3. Calculate the preparation time in minutes
|
||||
|
||||
- You need to define a [function][functions] with a single parameter.
|
||||
- You have to [explicitly return an integer][return] from a function.
|
||||
- The function's parameter is an [integer][integers].
|
||||
- You can use the [mathematical operator for multiplication][operators] to multiply values.
|
||||
|
||||
## 4. Calculate the elapsed working time in minutes
|
||||
|
||||
- You need to define a [function][functions] with two parameters.
|
||||
- You have to [explicitly return an integer][return] from a function.
|
||||
- The function's parameter is an [integer][integers].
|
||||
- You can [call][calls] one of the other functions you've defined previously.
|
||||
- You can use the [mathematical operator for addition][operators] to add values.
|
||||
|
||||
[functions]: https://tour.golang.org/basics/4
|
||||
[return]: https://golang.org/ref/spec#Return_statements
|
||||
[operators]: https://golang.org/ref/spec#Operators
|
||||
[integers]: https://golang.org/ref/spec#Integer_literals
|
||||
[calls]: https://golang.org/ref/spec#Calls
|
||||
[constants]: https://tour.golang.org/basics/15
|
151
go/lasagna/README.md
Normal file
151
go/lasagna/README.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# Gopher's Gorgeous Lasagna
|
||||
|
||||
Welcome to Gopher's Gorgeous Lasagna 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
|
||||
|
||||
[Go](https://golang.org) is a statically typed, compiled programming language.
|
||||
This exercise introduces three major language features: Packages, Functions, and Variables.
|
||||
|
||||
## Packages
|
||||
|
||||
Go applications are organized in packages.
|
||||
A package is a collection of source files located in the same directory.
|
||||
All source files in a directory must share the same package name.
|
||||
When a package is imported, only entities (functions, types, variables, constants) who's name starts with a capital letter can be used / accessed.
|
||||
The recommended style of naming in Go is that identifiers will be named using `camelCase`, except for those meant to be accessible across packages which should be `CamelCase`.
|
||||
|
||||
```go
|
||||
package lasagna
|
||||
```
|
||||
|
||||
## Variables
|
||||
|
||||
Go is statically-typed, which means all variables [must have a defined type](https://en.wikipedia.org/wiki/Type_system) at compile-time.
|
||||
|
||||
Variables can be defined by explicitly specifying a type:
|
||||
|
||||
```go
|
||||
var explicit int // Explicitly typed
|
||||
```
|
||||
|
||||
You can also use an initializer, and the compiler will assign the variable type to match the type of the initializer.
|
||||
|
||||
```go
|
||||
implicit := 10 // Implicitly typed as an int
|
||||
```
|
||||
|
||||
Once declared, variables can be assigned values using the `=` operator.
|
||||
Once declared, a variable's type can never change.
|
||||
|
||||
```go
|
||||
count := 1 // Assign initial value
|
||||
count = 2 // Update to new value
|
||||
|
||||
count = false // This throws a compiler error due to assigning a non `int` type
|
||||
```
|
||||
|
||||
## Constants
|
||||
|
||||
Constants hold a piece of data just like variables, but their value cannot change during the execution of the program.
|
||||
|
||||
Constants are defined using the `const` keyword and can be numbers, characters, strings or booleans:
|
||||
|
||||
```go
|
||||
const Age = 21 // Defines a numeric constant 'Age' with the value of 21
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
Go functions accept zero or more parameters.
|
||||
Parameters must be explicitly typed, there is no type inference.
|
||||
|
||||
Values are returned from functions using the `return` keyword.
|
||||
|
||||
A function is invoked by specifying the function name and passing arguments for each of the function's parameters.
|
||||
|
||||
Note that Go supports two types of comments.
|
||||
Single line comments are preceded by `//` and multiline comments are inserted between `/*` and `*/`.
|
||||
|
||||
```go
|
||||
package greeting
|
||||
|
||||
// Hello is a public function
|
||||
func Hello (name string) string {
|
||||
return hi(name)
|
||||
}
|
||||
|
||||
// hi is a private function
|
||||
func hi (name string) string {
|
||||
return "hi " + name
|
||||
}
|
||||
```
|
||||
|
||||
## Instructions
|
||||
|
||||
In this exercise you're going to write some code to help you cook a brilliant lasagna from your favorite cooking book.
|
||||
|
||||
You have four tasks, all related to the time spent cooking the lasagna.
|
||||
|
||||
## 1. Define the expected oven time in minutes
|
||||
|
||||
Define the `OvenTime` constant with how many minutes the lasagna should be in the oven. According to the cooking book, the expected oven time in minutes is 40:
|
||||
|
||||
```go
|
||||
OvenTime
|
||||
// Output: 40
|
||||
```
|
||||
|
||||
## 2. Calculate the remaining oven time in minutes
|
||||
|
||||
Define the `RemainingOvenTime()` function that takes the actual minutes the lasagna has been in the oven as a parameter and returns how many minutes the lasagna still has to remain in the oven, based on the expected oven time in minutes from the previous task.
|
||||
|
||||
```go
|
||||
func RemainingOvenTime(actual int) int {
|
||||
// TODO
|
||||
}
|
||||
|
||||
RemainingOvenTime(30)
|
||||
// Output: 10
|
||||
```
|
||||
|
||||
## 3. Calculate the preparation time in minutes
|
||||
|
||||
Define the `PreparationTime` function that takes the number of layers you added to the lasagna as a parameter and returns how many minutes you spent preparing the lasagna, assuming each layer takes you 2 minutes to prepare.
|
||||
|
||||
```go
|
||||
func PreparationTime(numberOfLayers int) int {
|
||||
// TODO
|
||||
}
|
||||
|
||||
PreparationTime(2)
|
||||
// Output: 4
|
||||
```
|
||||
|
||||
## 4. Calculate the elapsed working time in minutes
|
||||
|
||||
Define the `ElapsedTime` function that takes two parameters: the first parameter is the number of layers you added to the lasagna, and the second parameter is the number of minutes the lasagna has been in the oven.
|
||||
The function should return how many minutes in total you've worked on cooking the lasagna, which is the sum of the preparation time in minutes, and the time in minutes the lasagna has spent in the oven at the moment.
|
||||
|
||||
```go
|
||||
func ElapsedTime(numberOfLayers, actualMinutesInOven int) int {
|
||||
// TODO
|
||||
}
|
||||
|
||||
ElapsedTime(3, 20)
|
||||
// Output: 26
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @tehsphinx
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @ekingery
|
||||
- @andrerfcsantos
|
||||
- @bobtfish
|
3
go/lasagna/go.mod
Normal file
3
go/lasagna/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module lasagna
|
||||
|
||||
go 1.14
|
17
go/lasagna/lasagna.go
Normal file
17
go/lasagna/lasagna.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package lasagna
|
||||
|
||||
const OvenTime = 40;
|
||||
// RemainingOvenTime returns the remaining minutes based on the `actual` minutes already in the oven.
|
||||
func RemainingOvenTime(actualMinutesInOven int) int {
|
||||
return OvenTime - actualMinutesInOven;
|
||||
}
|
||||
|
||||
// PreparationTime calculates the time needed to prepare the lasagna based on the amount of layers.
|
||||
func PreparationTime(numberOfLayers int) int {
|
||||
return numberOfLayers * 2
|
||||
}
|
||||
|
||||
// ElapsedTime calculates the total time needed to create and bake a lasagna.
|
||||
func ElapsedTime(numberOfLayers, actualMinutesInOven int) int {
|
||||
return PreparationTime(numberOfLayers) + actualMinutesInOven
|
||||
}
|
94
go/lasagna/lasagna_test.go
Normal file
94
go/lasagna/lasagna_test.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package lasagna
|
||||
|
||||
import "testing"
|
||||
|
||||
type lasagnaTests struct {
|
||||
name string
|
||||
layers, time, expected int
|
||||
}
|
||||
|
||||
func TestOvenTime(t *testing.T) {
|
||||
tests := []lasagnaTests{
|
||||
{
|
||||
name: "Calculates how many minutes the lasagna should be in the oven",
|
||||
layers: 0,
|
||||
time: 40,
|
||||
expected: 40,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := OvenTime; got != tt.expected {
|
||||
t.Errorf("OvenTime(%d) = %d; want %d", tt.expected, got, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemainingOvenTime(t *testing.T) {
|
||||
tests := []lasagnaTests{
|
||||
{
|
||||
name: "Remaining minutes in oven",
|
||||
layers: 0,
|
||||
time: 15,
|
||||
expected: 15,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := RemainingOvenTime(25); got != tt.time {
|
||||
t.Errorf("RemainingOvenTime(%d) = %d; want %d", tt.expected, got, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
func TestPreparationTime(t *testing.T) {
|
||||
tests := []lasagnaTests{
|
||||
{
|
||||
name: "Preparation time in minutes for one layer",
|
||||
layers: 1,
|
||||
time: 0,
|
||||
expected: 2,
|
||||
},
|
||||
{
|
||||
name: "Preparation time in minutes for multiple layer",
|
||||
layers: 4,
|
||||
time: 0,
|
||||
expected: 8,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := PreparationTime(tt.layers); got != tt.expected {
|
||||
t.Errorf("PreparationTime(%d) = %d; want %d", tt.layers, got, tt.expected)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestElapsedTime(t *testing.T) {
|
||||
tests := []lasagnaTests{
|
||||
{
|
||||
name: "Total time in minutes for one layer",
|
||||
layers: 1,
|
||||
time: 30,
|
||||
expected: 32,
|
||||
},
|
||||
{
|
||||
name: "Total time in minutes for multiple layer",
|
||||
layers: 4,
|
||||
time: 8,
|
||||
expected: 16,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ElapsedTime(tt.layers, tt.time); got != tt.expected {
|
||||
t.Errorf("ElapsedTime(%d, %d) = %d; want %d", tt.layers, tt.time, got, tt.expected)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
37
go/luhn/.exercism/config.json
Normal file
37
go/luhn/.exercism/config.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"blurb": "Given a number determine whether or not it is valid per the Luhn formula.",
|
||||
"authors": [
|
||||
"soniakeys"
|
||||
],
|
||||
"contributors": [
|
||||
"alebaffa",
|
||||
"bitfield",
|
||||
"da-edra",
|
||||
"ekingery",
|
||||
"ferhatelmas",
|
||||
"hilary",
|
||||
"kytrinyx",
|
||||
"leenipper",
|
||||
"michaelorr",
|
||||
"petertseng",
|
||||
"robphoenix",
|
||||
"sebito91",
|
||||
"tehsphinx"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"luhn.go"
|
||||
],
|
||||
"test": [
|
||||
"luhn_test.go"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.go"
|
||||
],
|
||||
"editor": [
|
||||
"cases_test.go"
|
||||
]
|
||||
},
|
||||
"source": "The Luhn Algorithm on Wikipedia",
|
||||
"source_url": "http://en.wikipedia.org/wiki/Luhn_algorithm"
|
||||
}
|
1
go/luhn/.exercism/metadata.json
Normal file
1
go/luhn/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"luhn","id":"848a867390e5471ab5f927ba5dad86a7","url":"https://exercism.org/tracks/go/exercises/luhn","handle":"halfdan","is_requester":true,"auto_approve":false}
|
2
go/luhn/.tool-versions
Normal file
2
go/luhn/.tool-versions
Normal file
@@ -0,0 +1,2 @@
|
||||
golang 1.17.2
|
||||
kubectx 0.9.3
|
40
go/luhn/HELP.md
Normal file
40
go/luhn/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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 luhn.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)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- 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)
|
95
go/luhn/README.md
Normal file
95
go/luhn/README.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Luhn
|
||||
|
||||
Welcome to Luhn on Exercism's Go Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
Given a number determine whether or not it is valid per the Luhn formula.
|
||||
|
||||
The [Luhn algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) is
|
||||
a simple checksum formula used to validate a variety of identification
|
||||
numbers, such as credit card numbers and Canadian Social Insurance
|
||||
Numbers.
|
||||
|
||||
The task is to check if a given string is valid.
|
||||
|
||||
## Validating a Number
|
||||
|
||||
Strings of length 1 or less are not valid. Spaces are allowed in the input,
|
||||
but they should be stripped before checking. All other non-digit characters
|
||||
are disallowed.
|
||||
|
||||
### Example 1: valid credit card number
|
||||
|
||||
```text
|
||||
4539 3195 0343 6467
|
||||
```
|
||||
|
||||
The first step of the Luhn algorithm is to double every second digit,
|
||||
starting from the right. We will be doubling
|
||||
|
||||
```text
|
||||
4_3_ 3_9_ 0_4_ 6_6_
|
||||
```
|
||||
|
||||
If doubling the number results in a number greater than 9 then subtract 9
|
||||
from the product. The results of our doubling:
|
||||
|
||||
```text
|
||||
8569 6195 0383 3437
|
||||
```
|
||||
|
||||
Then sum all of the digits:
|
||||
|
||||
```text
|
||||
8+5+6+9+6+1+9+5+0+3+8+3+3+4+3+7 = 80
|
||||
```
|
||||
|
||||
If the sum is evenly divisible by 10, then the number is valid. This number is valid!
|
||||
|
||||
### Example 2: invalid credit card number
|
||||
|
||||
```text
|
||||
8273 1232 7352 0569
|
||||
```
|
||||
|
||||
Double the second digits, starting from the right
|
||||
|
||||
```text
|
||||
7253 2262 5312 0539
|
||||
```
|
||||
|
||||
Sum the digits
|
||||
|
||||
```text
|
||||
7+2+5+3+2+2+6+2+5+3+1+2+0+5+3+9 = 57
|
||||
```
|
||||
|
||||
57 is not evenly divisible by 10, so this number is not valid.
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @soniakeys
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @alebaffa
|
||||
- @bitfield
|
||||
- @da-edra
|
||||
- @ekingery
|
||||
- @ferhatelmas
|
||||
- @hilary
|
||||
- @kytrinyx
|
||||
- @leenipper
|
||||
- @michaelorr
|
||||
- @petertseng
|
||||
- @robphoenix
|
||||
- @sebito91
|
||||
- @tehsphinx
|
||||
|
||||
### Based on
|
||||
|
||||
The Luhn Algorithm on Wikipedia - http://en.wikipedia.org/wiki/Luhn_algorithm
|
102
go/luhn/cases_test.go
Normal file
102
go/luhn/cases_test.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package luhn
|
||||
|
||||
// Source: exercism/problem-specifications
|
||||
// Commit: f375a46 luhn: Update non-ASCII test case
|
||||
// Problem Specifications Version: 1.6.1
|
||||
|
||||
var testCases = []struct {
|
||||
description string
|
||||
input string
|
||||
ok bool
|
||||
}{
|
||||
{
|
||||
"single digit strings can not be valid",
|
||||
"1",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"a single zero is invalid",
|
||||
"0",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"a simple valid SIN that remains valid if reversed",
|
||||
"059",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a simple valid SIN that becomes invalid if reversed",
|
||||
"59",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a valid Canadian SIN",
|
||||
"055 444 285",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid Canadian SIN",
|
||||
"055 444 286",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid credit card",
|
||||
"8273 1232 7352 0569",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid number with an even number of digits",
|
||||
"095 245 88",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"valid number with an odd number of spaces",
|
||||
"234 567 891 234",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"valid strings with a non-digit added at the end become invalid",
|
||||
"059a",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid strings with punctuation included become invalid",
|
||||
"055-444-285",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid strings with symbols included become invalid",
|
||||
"055# 444$ 285",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"single zero with space is invalid",
|
||||
" 0",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"more than a single zero is valid",
|
||||
"0000 0",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"input digit 9 is correctly converted to output digit 9",
|
||||
"091",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"valid number with an odd number of digits and non-zero first digit",
|
||||
"109",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"using ascii value for non-doubled non-digit isn't allowed",
|
||||
"055b 444 285",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"using ascii value for doubled non-digit isn't allowed",
|
||||
":9",
|
||||
false,
|
||||
},
|
||||
}
|
3
go/luhn/go.mod
Normal file
3
go/luhn/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module luhn
|
||||
|
||||
go 1.13
|
33
go/luhn/luhn.go
Normal file
33
go/luhn/luhn.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package luhn
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
||||
// Valid checks a given number for validity according to Luhn's formula
|
||||
func Valid(card string) bool {
|
||||
sum := 0
|
||||
card = strings.ReplaceAll(card, " ", "")
|
||||
if len(card) <= 1 {
|
||||
return false
|
||||
}
|
||||
for idx, char := range card {
|
||||
if !unicode.IsDigit(char) {
|
||||
return false
|
||||
}
|
||||
digit := int(char - '0')
|
||||
if (len(card)-idx-1)%2 == 0 {
|
||||
sum += digit
|
||||
} else {
|
||||
digit *= 2
|
||||
if digit > 9 {
|
||||
sum += digit - 9
|
||||
} else {
|
||||
sum += digit
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum%10 == 0
|
||||
}
|
20
go/luhn/luhn_test.go
Normal file
20
go/luhn/luhn_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package luhn
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValid(t *testing.T) {
|
||||
for _, test := range testCases {
|
||||
if ok := Valid(test.input); ok != test.ok {
|
||||
t.Fatalf("Valid(%s): %s\n\t Expected: %t\n\t Got: %t", test.input, test.description, test.ok, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValid(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
Valid("2323 2005 7766 3554")
|
||||
}
|
||||
}
|
30
go/protein-translation/.exercism/config.json
Normal file
30
go/protein-translation/.exercism/config.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"blurb": "Translate RNA sequences into proteins.",
|
||||
"authors": [
|
||||
"Akasurde"
|
||||
],
|
||||
"contributors": [
|
||||
"alebaffa",
|
||||
"bitfield",
|
||||
"ekingery",
|
||||
"ferhatelmas",
|
||||
"hilary",
|
||||
"kytrinyx",
|
||||
"leenipper",
|
||||
"robphoenix",
|
||||
"sebito91",
|
||||
"tleen"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"protein_translation.go"
|
||||
],
|
||||
"test": [
|
||||
"protein_translation_test.go"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.go"
|
||||
]
|
||||
},
|
||||
"source": "Tyler Long"
|
||||
}
|
1
go/protein-translation/.exercism/metadata.json
Normal file
1
go/protein-translation/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"protein-translation","id":"1806e002ad7e45bf87a980c11a1bdac1","url":"https://exercism.org/tracks/go/exercises/protein-translation","handle":"halfdan","is_requester":true,"auto_approve":false}
|
40
go/protein-translation/HELP.md
Normal file
40
go/protein-translation/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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 protein_translation.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)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- 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)
|
70
go/protein-translation/README.md
Normal file
70
go/protein-translation/README.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Protein Translation
|
||||
|
||||
Welcome to Protein Translation on Exercism's Go Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
Translate RNA sequences into proteins.
|
||||
|
||||
RNA can be broken into three nucleotide sequences called codons, and then translated to a polypeptide like so:
|
||||
|
||||
RNA: `"AUGUUUUCU"` => translates to
|
||||
|
||||
Codons: `"AUG", "UUU", "UCU"`
|
||||
=> which become a polypeptide with the following sequence =>
|
||||
|
||||
Protein: `"Methionine", "Phenylalanine", "Serine"`
|
||||
|
||||
There are 64 codons which in turn correspond to 20 amino acids; however, all of the codon sequences and resulting amino acids are not important in this exercise. If it works for one codon, the program should work for all of them.
|
||||
However, feel free to expand the list in the test suite to include them all.
|
||||
|
||||
There are also three terminating codons (also known as 'STOP' codons); if any of these codons are encountered (by the ribosome), all translation ends and the protein is terminated.
|
||||
|
||||
All subsequent codons after are ignored, like this:
|
||||
|
||||
RNA: `"AUGUUUUCUUAAAUG"` =>
|
||||
|
||||
Codons: `"AUG", "UUU", "UCU", "UAA", "AUG"` =>
|
||||
|
||||
Protein: `"Methionine", "Phenylalanine", "Serine"`
|
||||
|
||||
Note the stop codon `"UAA"` terminates the translation and the final methionine is not translated into the protein sequence.
|
||||
|
||||
Below are the codons and resulting Amino Acids needed for the exercise.
|
||||
|
||||
Codon | Protein
|
||||
:--- | :---
|
||||
AUG | Methionine
|
||||
UUU, UUC | Phenylalanine
|
||||
UUA, UUG | Leucine
|
||||
UCU, UCC, UCA, UCG | Serine
|
||||
UAU, UAC | Tyrosine
|
||||
UGU, UGC | Cysteine
|
||||
UGG | Tryptophan
|
||||
UAA, UAG, UGA | STOP
|
||||
|
||||
Learn more about [protein translation on Wikipedia](http://en.wikipedia.org/wiki/Translation_(biology))
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @Akasurde
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @alebaffa
|
||||
- @bitfield
|
||||
- @ekingery
|
||||
- @ferhatelmas
|
||||
- @hilary
|
||||
- @kytrinyx
|
||||
- @leenipper
|
||||
- @robphoenix
|
||||
- @sebito91
|
||||
- @tleen
|
||||
|
||||
### Based on
|
||||
|
||||
Tyler Long
|
3
go/protein-translation/go.mod
Normal file
3
go/protein-translation/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module protein
|
||||
|
||||
go 1.13
|
9
go/protein-translation/protein_translation.go
Normal file
9
go/protein-translation/protein_translation.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package protein
|
||||
|
||||
func FromRNA(rna string) ([]string, error) {
|
||||
panic("Please implement the FromRNA function")
|
||||
}
|
||||
|
||||
func FromCodon(codon string) (string, error) {
|
||||
panic("Please implement the FromCodon function")
|
||||
}
|
35
go/protein-translation/protein_translation_detailed_test.go
Normal file
35
go/protein-translation/protein_translation_detailed_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
//go:build detailed
|
||||
// +build detailed
|
||||
|
||||
package protein
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkCodonDetailed(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for _, test := range codonTestCases {
|
||||
b.Run(fmt.Sprintf("Codon%s", test.input), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
FromCodon(test.input)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkProteinDetailed(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for _, test := range proteinTestCases {
|
||||
b.Run(fmt.Sprintf("Protein%s", test.input), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
FromRNA(test.input)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
189
go/protein-translation/protein_translation_test.go
Normal file
189
go/protein-translation/protein_translation_test.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package protein
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestErrorsNotNil(t *testing.T) {
|
||||
if ErrStop == nil {
|
||||
t.Fatalf("FAIL: ErrStop cannot be nil")
|
||||
}
|
||||
if ErrInvalidBase == nil {
|
||||
t.Fatalf("FAIL: ErrInvalidBase cannot be nil")
|
||||
}
|
||||
}
|
||||
|
||||
type codonCase struct {
|
||||
input string
|
||||
expected string
|
||||
errorExpected error
|
||||
}
|
||||
|
||||
var codonTestCases = []codonCase{
|
||||
{
|
||||
"AUG",
|
||||
"Methionine",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"UUU",
|
||||
"Phenylalanine",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"UUC",
|
||||
"Phenylalanine",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"UUA",
|
||||
"Leucine",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"UUG",
|
||||
"Leucine",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"UCG",
|
||||
"Serine",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"UAU",
|
||||
"Tyrosine",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"UAC",
|
||||
"Tyrosine",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"UGU",
|
||||
"Cysteine",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"UGG",
|
||||
"Tryptophan",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"UAA",
|
||||
"",
|
||||
ErrStop,
|
||||
},
|
||||
{
|
||||
"UAG",
|
||||
"",
|
||||
ErrStop,
|
||||
},
|
||||
{
|
||||
"UGA",
|
||||
"",
|
||||
ErrStop,
|
||||
},
|
||||
{
|
||||
"ABC",
|
||||
"",
|
||||
ErrInvalidBase,
|
||||
},
|
||||
}
|
||||
|
||||
func TestCodon(t *testing.T) {
|
||||
for _, test := range codonTestCases {
|
||||
actual, err := FromCodon(test.input)
|
||||
if test.errorExpected != nil {
|
||||
if test.errorExpected != err {
|
||||
t.Fatalf("FAIL: Protein translation test: %s\nExpected error: %q\nActual error: %q",
|
||||
test.input, test.errorExpected, err)
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Fatalf("FAIL: Protein translation test: %s\nExpected: %s\nGot error: %q",
|
||||
test.input, test.expected, err)
|
||||
}
|
||||
if actual != test.expected {
|
||||
t.Fatalf("FAIL: Protein translation test: %s\nExpected: %s\nActual: %s",
|
||||
test.input, test.expected, actual)
|
||||
}
|
||||
t.Logf("PASS: Protein translation test: %s", test.input)
|
||||
}
|
||||
}
|
||||
|
||||
type rnaCase struct {
|
||||
input string
|
||||
expected []string
|
||||
errorExpected error
|
||||
}
|
||||
|
||||
var proteinTestCases = []rnaCase{
|
||||
{
|
||||
"AUGUUUUCUUAAAUG",
|
||||
[]string{"Methionine", "Phenylalanine", "Serine"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"AUGUUUUGG",
|
||||
[]string{"Methionine", "Phenylalanine", "Tryptophan"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"AUGUUUUAA",
|
||||
[]string{"Methionine", "Phenylalanine"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"UGGUGUUAUUAAUGGUUU",
|
||||
[]string{"Tryptophan", "Cysteine", "Tyrosine"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"UGGAGAAUUAAUGGUUU",
|
||||
[]string{"Tryptophan"},
|
||||
ErrInvalidBase,
|
||||
},
|
||||
}
|
||||
|
||||
func TestProtein(t *testing.T) {
|
||||
for _, test := range proteinTestCases {
|
||||
actual, err := FromRNA(test.input)
|
||||
if test.errorExpected != nil {
|
||||
if test.errorExpected != err {
|
||||
t.Fatalf("FAIL: RNA translation test: %s\nExpected error: %q\nActual error: %q",
|
||||
test.input, test.errorExpected, err)
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Fatalf("FAIL: RNA translation test: %s\nExpected: %s\nGot error: %q",
|
||||
test.input, test.expected, err)
|
||||
}
|
||||
if !reflect.DeepEqual(actual, test.expected) {
|
||||
t.Fatalf("FAIL: RNA Translation test: %s\nExpected: %q\nActual %q", test.input, test.expected, actual)
|
||||
}
|
||||
t.Logf("PASS: RNA translation test: %s", test.input)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCodon(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for _, test := range codonTestCases {
|
||||
for i := 0; i < b.N; i++ {
|
||||
FromCodon(test.input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkProtein(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for _, test := range proteinTestCases {
|
||||
for i := 0; i < b.N; i++ {
|
||||
FromRNA(test.input)
|
||||
}
|
||||
}
|
||||
}
|
32
go/pythagorean-triplet/.exercism/config.json
Normal file
32
go/pythagorean-triplet/.exercism/config.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"blurb": "There exists exactly one Pythagorean triplet for which a + b + c = 1000. Find the product a * b * c.",
|
||||
"authors": [
|
||||
"soniakeys"
|
||||
],
|
||||
"contributors": [
|
||||
"alebaffa",
|
||||
"bitfield",
|
||||
"ekingery",
|
||||
"ferhatelmas",
|
||||
"hilary",
|
||||
"kytrinyx",
|
||||
"leenipper",
|
||||
"petertseng",
|
||||
"robphoenix",
|
||||
"sebito91",
|
||||
"tleen"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"pythagorean_triplet.go"
|
||||
],
|
||||
"test": [
|
||||
"pythagorean_triplet_test.go"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.go"
|
||||
]
|
||||
},
|
||||
"source": "Problem 9 at Project Euler",
|
||||
"source_url": "http://projecteuler.net/problem=9"
|
||||
}
|
1
go/pythagorean-triplet/.exercism/metadata.json
Normal file
1
go/pythagorean-triplet/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"pythagorean-triplet","id":"0e897c0fbe50436d8a7e3f121f06ac6d","url":"https://exercism.org/tracks/go/exercises/pythagorean-triplet","handle":"halfdan","is_requester":true,"auto_approve":false}
|
40
go/pythagorean-triplet/HELP.md
Normal file
40
go/pythagorean-triplet/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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 pythagorean_triplet.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)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- 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)
|
53
go/pythagorean-triplet/README.md
Normal file
53
go/pythagorean-triplet/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Pythagorean Triplet
|
||||
|
||||
Welcome to Pythagorean Triplet on Exercism's Go Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
A Pythagorean triplet is a set of three natural numbers, {a, b, c}, for
|
||||
which,
|
||||
|
||||
```text
|
||||
a² + b² = c²
|
||||
```
|
||||
|
||||
and such that,
|
||||
|
||||
```text
|
||||
a < b < c
|
||||
```
|
||||
|
||||
For example,
|
||||
|
||||
```text
|
||||
3² + 4² = 9 + 16 = 25 = 5².
|
||||
```
|
||||
|
||||
Given an input integer N, find all Pythagorean triplets for which `a + b + c = N`.
|
||||
|
||||
For example, with N = 1000, there is exactly one Pythagorean triplet for which `a + b + c = 1000`: `{200, 375, 425}`.
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @soniakeys
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @alebaffa
|
||||
- @bitfield
|
||||
- @ekingery
|
||||
- @ferhatelmas
|
||||
- @hilary
|
||||
- @kytrinyx
|
||||
- @leenipper
|
||||
- @petertseng
|
||||
- @robphoenix
|
||||
- @sebito91
|
||||
- @tleen
|
||||
|
||||
### Based on
|
||||
|
||||
Problem 9 at Project Euler - http://projecteuler.net/problem=9
|
3
go/pythagorean-triplet/go.mod
Normal file
3
go/pythagorean-triplet/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module pythagorean
|
||||
|
||||
go 1.13
|
39
go/pythagorean-triplet/pythagorean_triplet.go
Normal file
39
go/pythagorean-triplet/pythagorean_triplet.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package pythagorean
|
||||
|
||||
import "math"
|
||||
|
||||
type Triplet [3]int
|
||||
|
||||
func isTriplet(a, b, c int) bool {
|
||||
return a*a + b*b == c*c && a < b && b < c
|
||||
}
|
||||
|
||||
func Range(min, max int) []Triplet {
|
||||
var triplets = []Triplet{}
|
||||
for c := min; c <= max; c++ {
|
||||
for b := min; b <= c - 1; b++ {
|
||||
a := int(math.Sqrt(float64(c*c - b*b)))
|
||||
|
||||
if isTriplet(a, b, c) && a >= min {
|
||||
triplets = append(triplets, Triplet{a,b,c})
|
||||
}
|
||||
}
|
||||
}
|
||||
return triplets
|
||||
}
|
||||
|
||||
func Sum(p int) []Triplet {
|
||||
temp_triplets := Range(1, p / 2)
|
||||
triplets := make([]Triplet, 0, len(temp_triplets))
|
||||
|
||||
for _, triplet := range temp_triplets {
|
||||
if triplet[0] + triplet[1] + triplet[2] == p {
|
||||
triplets = append(triplets, triplet)
|
||||
}
|
||||
}
|
||||
// Reverse slice..
|
||||
for i, j := 0, len(triplets)-1; i < j; i, j = i+1, j-1 {
|
||||
triplets[i], triplets[j] = triplets[j], triplets[i]
|
||||
}
|
||||
return triplets
|
||||
}
|
60
go/pythagorean-triplet/pythagorean_triplet_test.go
Normal file
60
go/pythagorean-triplet/pythagorean_triplet_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package pythagorean
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var rangeTests = []struct {
|
||||
min, max int
|
||||
ts []Triplet
|
||||
}{
|
||||
{1, 10, []Triplet{{3, 4, 5}, {6, 8, 10}}},
|
||||
{11, 20, []Triplet{{12, 16, 20}}},
|
||||
}
|
||||
|
||||
func TestRange(t *testing.T) {
|
||||
for _, test := range rangeTests {
|
||||
ts := Range(test.min, test.max)
|
||||
if !reflect.DeepEqual(ts, test.ts) {
|
||||
t.Fatalf("Range(%d, %d) = %v, want %v",
|
||||
test.min, test.max, ts, test.ts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sumTests = []struct {
|
||||
sum int
|
||||
ts []Triplet
|
||||
}{
|
||||
{180, []Triplet{{18, 80, 82}, {30, 72, 78}, {45, 60, 75}}},
|
||||
{1000, []Triplet{{200, 375, 425}}},
|
||||
}
|
||||
|
||||
func TestSum(t *testing.T) {
|
||||
for _, test := range sumTests {
|
||||
ts := Sum(test.sum)
|
||||
if !reflect.DeepEqual(ts, test.ts) {
|
||||
t.Fatalf("Sum(%d) = %v, want %v",
|
||||
test.sum, ts, test.ts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRange(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
Range(1, 100)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSum(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
Sum(1000)
|
||||
}
|
||||
}
|
31
go/run-length-encoding/.exercism/config.json
Normal file
31
go/run-length-encoding/.exercism/config.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"blurb": "Implement run-length encoding and decoding.",
|
||||
"authors": [
|
||||
"MatForsberg"
|
||||
],
|
||||
"contributors": [
|
||||
"bitfield",
|
||||
"ekingery",
|
||||
"ferhatelmas",
|
||||
"hilary",
|
||||
"ilmanzo",
|
||||
"leenipper",
|
||||
"sebito91"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"run_length_encoding.go"
|
||||
],
|
||||
"test": [
|
||||
"run_length_encoding_test.go"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.go"
|
||||
],
|
||||
"editor": [
|
||||
"cases_test.go"
|
||||
]
|
||||
},
|
||||
"source": "Wikipedia",
|
||||
"source_url": "https://en.wikipedia.org/wiki/Run-length_encoding"
|
||||
}
|
1
go/run-length-encoding/.exercism/metadata.json
Normal file
1
go/run-length-encoding/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"run-length-encoding","id":"c65b116f8aba4eaf9baa3b129a348992","url":"https://exercism.org/tracks/go/exercises/run-length-encoding","handle":"halfdan","is_requester":true,"auto_approve":false}
|
40
go/run-length-encoding/HELP.md
Normal file
40
go/run-length-encoding/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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 run_length_encoding.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)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- 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)
|
49
go/run-length-encoding/README.md
Normal file
49
go/run-length-encoding/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Run Length Encoding
|
||||
|
||||
Welcome to Run Length Encoding on Exercism's Go Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
Implement run-length encoding and decoding.
|
||||
|
||||
Run-length encoding (RLE) is a simple form of data compression, where runs
|
||||
(consecutive data elements) are replaced by just one data value and count.
|
||||
|
||||
For example we can represent the original 53 characters with only 13.
|
||||
|
||||
```text
|
||||
"WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" -> "12WB12W3B24WB"
|
||||
```
|
||||
|
||||
RLE allows the original data to be perfectly reconstructed from
|
||||
the compressed data, which makes it a lossless data compression.
|
||||
|
||||
```text
|
||||
"AABCCCDEEEE" -> "2AB3CD4E" -> "AABCCCDEEEE"
|
||||
```
|
||||
|
||||
For simplicity, you can assume that the unencoded string will only contain
|
||||
the letters A through Z (either lower or upper case) and whitespace. This way
|
||||
data to be encoded will never contain any numbers and numbers inside data to
|
||||
be decoded always represent the count for the following character.
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @MatForsberg
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @bitfield
|
||||
- @ekingery
|
||||
- @ferhatelmas
|
||||
- @hilary
|
||||
- @ilmanzo
|
||||
- @leenipper
|
||||
- @sebito91
|
||||
|
||||
### Based on
|
||||
|
||||
Wikipedia - https://en.wikipedia.org/wiki/Run-length_encoding
|
42
go/run-length-encoding/cases_test.go
Normal file
42
go/run-length-encoding/cases_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package encode
|
||||
|
||||
// Source: exercism/problem-specifications
|
||||
// Commit: 1b7900e run-length-encoding: apply "input" policy
|
||||
// Problem Specifications Version: 1.1.0
|
||||
|
||||
// run-length encode a string
|
||||
var encodeTests = []struct {
|
||||
input string
|
||||
expected string
|
||||
description string
|
||||
}{
|
||||
{"", "", "empty string"},
|
||||
{"XYZ", "XYZ", "single characters only are encoded without count"},
|
||||
{"AABBBCCCC", "2A3B4C", "string with no single characters"},
|
||||
{"WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB", "12WB12W3B24WB", "single characters mixed with repeated characters"},
|
||||
{" hsqq qww ", "2 hs2q q2w2 ", "multiple whitespace mixed in string"},
|
||||
{"aabbbcccc", "2a3b4c", "lowercase characters"},
|
||||
}
|
||||
|
||||
// run-length decode a string
|
||||
var decodeTests = []struct {
|
||||
input string
|
||||
expected string
|
||||
description string
|
||||
}{
|
||||
{"", "", "empty string"},
|
||||
{"XYZ", "XYZ", "single characters only"},
|
||||
{"2A3B4C", "AABBBCCCC", "string with no single characters"},
|
||||
{"12WB12W3B24WB", "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB", "single characters with repeated characters"},
|
||||
{"2 hs2q q2w2 ", " hsqq qww ", "multiple whitespace mixed in string"},
|
||||
{"2a3b4c", "aabbbcccc", "lower case string"},
|
||||
}
|
||||
|
||||
// encode and then decode
|
||||
var encodeDecodeTests = []struct {
|
||||
input string
|
||||
expected string
|
||||
description string
|
||||
}{
|
||||
{"zzz ZZ zZ", "zzz ZZ zZ", "encode followed by decode gives original string"},
|
||||
}
|
3
go/run-length-encoding/go.mod
Normal file
3
go/run-length-encoding/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module encode
|
||||
|
||||
go 1.13
|
9
go/run-length-encoding/run_length_encoding.go
Normal file
9
go/run-length-encoding/run_length_encoding.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package encode
|
||||
|
||||
func RunLengthEncode(input string) string {
|
||||
panic("Please implement the RunLengthEncode function")
|
||||
}
|
||||
|
||||
func RunLengthDecode(input string) string {
|
||||
panic("Please implement the RunLengthDecode function")
|
||||
}
|
31
go/run-length-encoding/run_length_encoding_test.go
Normal file
31
go/run-length-encoding/run_length_encoding_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package encode
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRunLengthEncode(t *testing.T) {
|
||||
for _, test := range encodeTests {
|
||||
if actual := RunLengthEncode(test.input); actual != test.expected {
|
||||
t.Errorf("FAIL %s - RunLengthEncode(%s) = %q, expected %q.",
|
||||
test.description, test.input, actual, test.expected)
|
||||
}
|
||||
t.Logf("PASS RunLengthEncode - %s", test.description)
|
||||
}
|
||||
}
|
||||
func TestRunLengthDecode(t *testing.T) {
|
||||
for _, test := range decodeTests {
|
||||
if actual := RunLengthDecode(test.input); actual != test.expected {
|
||||
t.Errorf("FAIL %s - RunLengthDecode(%s) = %q, expected %q.",
|
||||
test.description, test.input, actual, test.expected)
|
||||
}
|
||||
t.Logf("PASS RunLengthDecode - %s", test.description)
|
||||
}
|
||||
}
|
||||
func TestRunLengthEncodeDecode(t *testing.T) {
|
||||
for _, test := range encodeDecodeTests {
|
||||
if actual := RunLengthDecode(RunLengthEncode(test.input)); actual != test.expected {
|
||||
t.Errorf("FAIL %s - RunLengthDecode(RunLengthEncode(%s)) = %q, expected %q.",
|
||||
test.description, test.input, actual, test.expected)
|
||||
}
|
||||
t.Logf("PASS %s", test.description)
|
||||
}
|
||||
}
|
32
go/tournament/.exercism/config.json
Normal file
32
go/tournament/.exercism/config.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"blurb": "Tally the results of a small football competition.",
|
||||
"authors": [
|
||||
"pminten"
|
||||
],
|
||||
"contributors": [
|
||||
"alebaffa",
|
||||
"bitfield",
|
||||
"ekingery",
|
||||
"ferhatelmas",
|
||||
"hilary",
|
||||
"kytrinyx",
|
||||
"leenipper",
|
||||
"manavo",
|
||||
"petertseng",
|
||||
"robphoenix",
|
||||
"sebito91",
|
||||
"soniakeys",
|
||||
"tleen"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"tournament.go"
|
||||
],
|
||||
"test": [
|
||||
"tournament_test.go"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.go"
|
||||
]
|
||||
}
|
||||
}
|
1
go/tournament/.exercism/metadata.json
Normal file
1
go/tournament/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"tournament","id":"399ebb8ad6844f9f986ef2e70737b8fa","url":"https://exercism.org/tracks/go/exercises/tournament","handle":"halfdan","is_requester":true,"auto_approve":false}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user