Resync
This commit is contained in:
77
go/kindergarten-garden/README.md
Normal file
77
go/kindergarten-garden/README.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Kindergarten Garden
|
||||
|
||||
Write a program that, given a diagram, can tell you which plants each child in the kindergarten class is responsible for.
|
||||
|
||||
The kindergarten class is learning about growing plants. The teachers
|
||||
thought it would be a good idea to give them actual seeds, plant them in
|
||||
actual dirt, and grow actual plants.
|
||||
|
||||
They've chosen to grow grass, clover, radishes, and violets.
|
||||
|
||||
To this end, they've put little styrofoam cups along the window sills,
|
||||
and planted one type of plant in each cup, choosing randomly from the
|
||||
available types of seeds.
|
||||
|
||||
```plain
|
||||
[window][window][window]
|
||||
........................ # each dot represents a styrofoam cup
|
||||
........................
|
||||
```
|
||||
|
||||
There are 12 children in the class:
|
||||
|
||||
- Alice, Bob, Charlie, David,
|
||||
- Eve, Fred, Ginny, Harriet,
|
||||
- Ileana, Joseph, Kincaid, and Larry.
|
||||
|
||||
Each child gets 4 cups, two on each row. The children are assigned to
|
||||
cups in alphabetical order.
|
||||
|
||||
The following diagram represents Alice's plants:
|
||||
|
||||
```plain
|
||||
[window][window][window]
|
||||
VR......................
|
||||
RG......................
|
||||
```
|
||||
|
||||
So in the row nearest the window, she has a violet and a radish; in the
|
||||
row behind that, she has a radish and some grass.
|
||||
|
||||
Your program will be given the plants from left-to-right starting with
|
||||
the row nearest the windows. From this, it should be able to determine
|
||||
which plants belong to which students.
|
||||
|
||||
For example, if it's told that the garden looks like so:
|
||||
|
||||
```plain
|
||||
[window][window][window]
|
||||
VRCGVVRVCGGCCGVRGCVCGCGV
|
||||
VRCCCGCRRGVCGCRVVCVGCGCV
|
||||
```
|
||||
|
||||
Then if asked for Alice's plants, it should provide:
|
||||
|
||||
- Violets, radishes, violets, radishes
|
||||
|
||||
While asking for Bob's plants would yield:
|
||||
|
||||
- Clover, grass, clover, clover
|
||||
|
||||
To run the tests simply run the command `go test` in the exercise directory.
|
||||
|
||||
If the test suite contains benchmarks, you can run these with the `-bench`
|
||||
flag:
|
||||
|
||||
go test -bench .
|
||||
|
||||
For more detailed info about the Go track see the [help
|
||||
page](http://exercism.io/languages/go).
|
||||
|
||||
## Source
|
||||
|
||||
Random musings during airplane trip. [http://jumpstartlab.com](http://jumpstartlab.com)
|
||||
|
||||
## Submitting Incomplete Problems
|
||||
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
||||
|
93
go/kindergarten-garden/kindergarten.go
Normal file
93
go/kindergarten-garden/kindergarten.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package kindergarten
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Garden struct {
|
||||
children []string
|
||||
rows []string
|
||||
}
|
||||
|
||||
func NewGarden(diagram string, children []string) (*Garden, error) {
|
||||
var err error
|
||||
g := new(Garden)
|
||||
g.rows = strings.Split(diagram[1:], "\n")
|
||||
g.children = children
|
||||
if diagram[0] != '\n' {
|
||||
err = errors.New("Wrong diagram format")
|
||||
} else if len(g.rows) == 0 {
|
||||
err = errors.New("No rows")
|
||||
} else if len(g.children) == 0 {
|
||||
err = errors.New("No children")
|
||||
} else if len(g.rows[0]) != len(g.rows[1]) {
|
||||
err = errors.New("Mismatched rows")
|
||||
} else {
|
||||
for i := range g.rows {
|
||||
if len(g.rows[i])%2 != 0 {
|
||||
err = errors.New("Odd number of cups")
|
||||
break
|
||||
}
|
||||
for j := range g.rows[i] {
|
||||
tst := g.rows[i][j]
|
||||
switch tst {
|
||||
case 'R':
|
||||
case 'C':
|
||||
case 'G':
|
||||
case 'V':
|
||||
default:
|
||||
err = errors.New("Invalid Cup Codes")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
for i := range g.children {
|
||||
if i > 0 {
|
||||
if g.children[i] == g.children[i-1] {
|
||||
err = errors.New("Duplicate Children")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return g, err
|
||||
}
|
||||
|
||||
func (g *Garden) Plants(child string) ([]string, bool) {
|
||||
var i int
|
||||
var ret []string
|
||||
var found bool
|
||||
c := g.children
|
||||
sort.Strings(c)
|
||||
for i = 0; i < len(c); i++ {
|
||||
if c[i] == child {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found || len(g.rows) == 0 || len(g.rows[0]) <= 2*i {
|
||||
return ret, found
|
||||
}
|
||||
for j := range []int{0, 1} {
|
||||
for k := range []int{0, 1} {
|
||||
switch g.rows[j][(i*2 + k)] {
|
||||
case 'G':
|
||||
ret = append(ret, "grass")
|
||||
case 'C':
|
||||
ret = append(ret, "clover")
|
||||
case 'R':
|
||||
ret = append(ret, "radishes")
|
||||
case 'V':
|
||||
ret = append(ret, "violets")
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret, found
|
||||
}
|
||||
|
||||
// kindergarten_garden_test.go:140: Garden 6 lookup Patricia =
|
||||
// [radishes radishes grass clover], want [violets clover radishes violets].
|
211
go/kindergarten-garden/kindergarten_garden_test.go
Normal file
211
go/kindergarten-garden/kindergarten_garden_test.go
Normal file
@@ -0,0 +1,211 @@
|
||||
// Kindergarten garden
|
||||
//
|
||||
// You must define a type Garden with constructor
|
||||
//
|
||||
// func NewGarden(diagram string, children []string) (*Garden, error)
|
||||
//
|
||||
// and method
|
||||
//
|
||||
// func (g *Garden) Plants(child string) ([]string, bool)
|
||||
//
|
||||
// The diagram argument starts each row with a '\n'. This allows Go's
|
||||
// raw string literals to present diagrams in source code nicely as two
|
||||
// rows flush left, for example,
|
||||
//
|
||||
// diagram := `
|
||||
// VVCCGG
|
||||
// VVCCGG`
|
||||
|
||||
package kindergarten
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type lookup struct {
|
||||
child string
|
||||
plants []string
|
||||
ok bool
|
||||
}
|
||||
|
||||
type gardenTest struct {
|
||||
number int
|
||||
diagram string
|
||||
children []string
|
||||
ok bool
|
||||
lookups []lookup
|
||||
}
|
||||
|
||||
var tests = []gardenTest{
|
||||
{1, `
|
||||
RC
|
||||
GG`, []string{"Alice"}, true, []lookup{
|
||||
{"Alice", []string{"radishes", "clover", "grass", "grass"}, true},
|
||||
}},
|
||||
{2, `
|
||||
VC
|
||||
RC`, []string{"Alice"}, true, []lookup{
|
||||
{"Alice", []string{"violets", "clover", "radishes", "clover"}, true},
|
||||
}},
|
||||
{3, `
|
||||
VVCG
|
||||
VVRC`, []string{"Alice", "Bob"}, true, []lookup{
|
||||
{"Bob", []string{"clover", "grass", "radishes", "clover"}, true},
|
||||
}},
|
||||
{4, `
|
||||
VVCCGG
|
||||
VVCCGG`, []string{"Alice", "Bob", "Charlie"}, true, []lookup{
|
||||
{"Bob", []string{"clover", "clover", "clover", "clover"}, true},
|
||||
{"Charlie", []string{"grass", "grass", "grass", "grass"}, true},
|
||||
}},
|
||||
test5, // full garden test
|
||||
test6, // out of order names test
|
||||
|
||||
// failure tests
|
||||
{7, "RC\nGG", []string{"Alice"}, false, nil}, // wrong diagram format
|
||||
{8, `
|
||||
RCCC
|
||||
GG`, []string{""}, false, nil}, // mismatched rows
|
||||
{9, `
|
||||
RCC
|
||||
GGC`, []string{"Alice"}, false, nil}, // odd number of cups
|
||||
{10, `
|
||||
RCCC
|
||||
GGCC`, []string{"Alice", "Alice"}, false, nil}, // duplicate name
|
||||
{11, `
|
||||
rc
|
||||
gg`, []string{"Alice"}, false, nil}, // invaid cup codes
|
||||
{12, `
|
||||
RC
|
||||
GG`, []string{"Alice"}, true, []lookup{ // lookup invalid name
|
||||
{"Bob", []string{"radishes", "clover", "grass", "grass"}, false},
|
||||
}},
|
||||
}
|
||||
|
||||
// full garden test
|
||||
var test5 = gardenTest{5, `
|
||||
VRCGVVRVCGGCCGVRGCVCGCGV
|
||||
VRCCCGCRRGVCGCRVVCVGCGCV`, []string{
|
||||
"Alice", "Bob", "Charlie", "David", "Eve", "Fred",
|
||||
"Ginny", "Harriet", "Ileana", "Joseph", "Kincaid", "Larry"}, true, []lookup{
|
||||
{"Alice", []string{"violets", "radishes", "violets", "radishes"}, true},
|
||||
{"Bob", []string{"clover", "grass", "clover", "clover"}, true},
|
||||
{"Charlie", []string{"violets", "violets", "clover", "grass"}, true},
|
||||
{"David", []string{"radishes", "violets", "clover", "radishes"}, true},
|
||||
{"Eve", []string{"clover", "grass", "radishes", "grass"}, true},
|
||||
{"Fred", []string{"grass", "clover", "violets", "clover"}, true},
|
||||
{"Ginny", []string{"clover", "grass", "grass", "clover"}, true},
|
||||
{"Harriet", []string{"violets", "radishes", "radishes", "violets"}, true},
|
||||
{"Ileana", []string{"grass", "clover", "violets", "clover"}, true},
|
||||
{"Joseph", []string{"violets", "clover", "violets", "grass"}, true},
|
||||
{"Kincaid", []string{"grass", "clover", "clover", "grass"}, true},
|
||||
{"Larry", []string{"grass", "violets", "clover", "violets"}, true},
|
||||
}}
|
||||
|
||||
// out of order names test
|
||||
var (
|
||||
test6names = []string{"Samantha", "Patricia", "Xander", "Roger"}
|
||||
test6 = gardenTest{6, `
|
||||
VCRRGVRG
|
||||
RVGCCGCV`, append([]string{}, test6names...), true, []lookup{
|
||||
{"Patricia", []string{"violets", "clover", "radishes", "violets"}, true},
|
||||
{"Roger", []string{"radishes", "radishes", "grass", "clover"}, true},
|
||||
{"Samantha", []string{"grass", "violets", "clover", "grass"}, true},
|
||||
{"Xander", []string{"radishes", "grass", "clover", "violets"}, true},
|
||||
}}
|
||||
)
|
||||
|
||||
func TestGarden(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
g, err := NewGarden(test.diagram, test.children)
|
||||
switch {
|
||||
case err != nil:
|
||||
if test.ok {
|
||||
t.Fatalf("NewGarden test %d returned error %q. Error not expected.",
|
||||
test.number, err)
|
||||
}
|
||||
case !test.ok:
|
||||
t.Fatalf("NewGarden test %d error = %v. Expected error.",
|
||||
test.number, err)
|
||||
}
|
||||
for _, l := range test.lookups {
|
||||
switch plants, ok := g.Plants(l.child); {
|
||||
case ok != l.ok:
|
||||
t.Fatalf("Garden %d lookup %s returned ok = %t, want %t.",
|
||||
test.number, l.child, ok, l.ok)
|
||||
case ok && !reflect.DeepEqual(plants, l.plants):
|
||||
t.Fatalf("Garden %d lookup %s = %v, want %v.",
|
||||
test.number, l.child, plants, l.plants)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The lazy way to meet the alphabetizing requirement is with sort.Strings
|
||||
// on the argument slice. That's an in-place sort though and it's bad practice
|
||||
// to have a side effect.
|
||||
func TestNamesNotModified(t *testing.T) {
|
||||
cp := append([]string{}, test6names...)
|
||||
_, err := NewGarden(test6.diagram, cp)
|
||||
if err != nil {
|
||||
t.Skip("TestNamesNotModified requires valid garden")
|
||||
}
|
||||
if !reflect.DeepEqual(cp, test6names) {
|
||||
t.Fatalf("NewGarden modified children argment. " +
|
||||
"Arguments should not be modified.")
|
||||
}
|
||||
sort.Strings(cp)
|
||||
if reflect.DeepEqual(cp, test6names) {
|
||||
t.Skip("TestNamesNotModified requires names out of order")
|
||||
}
|
||||
}
|
||||
|
||||
// A test taken from the Ruby tests. It checks that Garden objects
|
||||
// are self-contained and do not rely on package variables.
|
||||
func TestTwoGardens(t *testing.T) {
|
||||
diagram := `
|
||||
VCRRGVRG
|
||||
RVGCCGCV`
|
||||
g1, err1 := NewGarden(diagram, []string{"Alice", "Bob", "Charlie", "Dan"})
|
||||
g2, err2 := NewGarden(diagram, []string{"Bob", "Charlie", "Dan", "Erin"})
|
||||
if err1 != nil || err2 != nil {
|
||||
t.Skip("Two garden test needs valid gardens")
|
||||
}
|
||||
tf := func(g *Garden, n int, child string, expPlants []string) {
|
||||
switch plants, ok := g.Plants(child); {
|
||||
case !ok:
|
||||
t.Skip("Garden %d lookup %s returned ok = false, want true.",
|
||||
n, child)
|
||||
case !reflect.DeepEqual(plants, expPlants):
|
||||
t.Fatalf("Garden %d lookup %s = %v, want %v.",
|
||||
n, child, plants, expPlants)
|
||||
}
|
||||
}
|
||||
tf(g1, 1, "Bob", []string{"radishes", "radishes", "grass", "clover"})
|
||||
tf(g2, 2, "Bob", []string{"violets", "clover", "radishes", "violets"})
|
||||
tf(g1, 1, "Charlie", []string{"grass", "violets", "clover", "grass"})
|
||||
tf(g2, 2, "Charlie", []string{"radishes", "radishes", "grass", "clover"})
|
||||
}
|
||||
|
||||
func BenchmarkNewGarden(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, test := range tests {
|
||||
NewGarden(test.diagram, test.children)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGarden_Plants(b *testing.B) {
|
||||
g, err := NewGarden(test5.diagram, test5.children)
|
||||
if err != nil {
|
||||
b.Skip("BenchmarkGarden_Plants requires valid garden")
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, l := range test5.lookups {
|
||||
g.Plants(l.child)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user