Skip to content

Commit

Permalink
1. Add Makefile to make development easier.
Browse files Browse the repository at this point in the history
2. Add new method FieldIteratorCallback to Game interface.
  • Loading branch information
messi-yang committed Apr 10, 2022
1 parent f618971 commit a6cf688
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 18 deletions.
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
test:
go test -v

demo:
go run example/*

setup-pre-commit:
brew install pre-commit
pre-commit install

19 changes: 8 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,31 +172,28 @@ The rain of code in the movie Matrix.

## Development

### Run Tests
We use Makefile to setup develop environments.

### Run Unit Tests

```bash
go test -v
make test
```

### Setup Pre-commit Hook

```bash
brew install pre-commit
pre-commit install
make setup-pre-commit
```

### Run The Code To Build Sample GIFs
### Build Sample GIFs As Demo

You can refer to sample code in [here](./example/) to build GIFs of your custom games.

```bash
git clone https://github.com/DumDumGeniuss/ggol.git
cd ggol
go mod tidy
go run example/*
make demo
```


## License

[MIT](./LICENSE)
[MIT License](./LICENSE)
2 changes: 1 addition & 1 deletion develop_tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func areTwoAreasHavingLiveCellForTestEqual(a areasHavingLiveCellForTest, b areas
return true
}

func convertAreaForTestMatrixToAreasHavingLiveCellForTest(g *[]*[]*areaForTest) *areasHavingLiveCellForTest {
func convertAreaForTestMatrixToAreasHavingLiveCellForTest(g *Field[areaForTest]) *areasHavingLiveCellForTest {
gMap := make(areasHavingLiveCellForTest, 0)
for x := 0; x < len(*g); x++ {
gMap = append(gMap, []bool{})
Expand Down
23 changes: 17 additions & 6 deletions ggol.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,23 @@ type Game[T any] interface {
// you passed in SetNextAreaGenerator.
GenerateNextField()
// Set NextAreaGenerator, which tells the game how you want to generate next area of the given area.
SetNextAreaGenerator(iterator NextAreaGenerator[T])
SetNextAreaGenerator(nextAreaGenerator NextAreaGenerator[T])
// Set the status of the area at the given coordinate.
SetArea(coord *Coordinate, area *T) (err error)
// Get the size of the field.
GetFieldSize() (fieldSize *FieldSize)
// Get the status of the area at the given coordinate.
GetArea(coord *Coordinate) (area *T, err error)
// Get the field of the area, it's a matrix that contains all areas in the game.
GetField() (field *[]*[]*T)
GetField() (field *Field[T])
// Iterate through entire field
IterateField(callback FieldIteratorCallback[T])
}

type gameInfo[T any] struct {
fieldSize *FieldSize
initialArea *T
field *[]*[]*T
field *Field[T]
areaIterator NextAreaGenerator[T]
locker sync.RWMutex
}
Expand Down Expand Up @@ -55,8 +57,8 @@ func NewGame[T any](
return &newG, nil
}

func createField[T any](fieldSize *FieldSize, initialArea *T) *[]*[]*T {
field := make([]*[]*T, fieldSize.Width)
func createField[T any](fieldSize *FieldSize, initialArea *T) *Field[T] {
field := make(Field[T], fieldSize.Width)
for x := 0; x < fieldSize.Width; x++ {
newRowOfField := make([]*T, fieldSize.Height)
field[x] = &newRowOfField
Expand Down Expand Up @@ -163,9 +165,18 @@ func (g *gameInfo[T]) GetArea(c *Coordinate) (*T, error) {
}

// Get the entire genetation, which is a matrix that contains all areas.
func (g *gameInfo[T]) GetField() *[]*[]*T {
func (g *gameInfo[T]) GetField() *Field[T] {
g.locker.RLock()
defer g.locker.RUnlock()

return g.field
}

// We will iterate field and call the callback func that you passes in.
func (g *gameInfo[T]) IterateField(callback FieldIteratorCallback[T]) {
for x := 0; x < g.fieldSize.Width; x++ {
for y := 0; y < g.fieldSize.Height; y++ {
callback(&Coordinate{X: x, Y: y}, (*(*g.field)[x])[y])
}
}
}
5 changes: 5 additions & 0 deletions ggol_structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ type FieldSize struct {
Height int
}

type Field[T any] []*[]*T

// This function will be passed into NextAreaGenerator, this is how you can adajcent areas in NextAreaGenerator.
// Also, 2nd argument "isCrossBorder" tells you if the adjacent area is on ohter side of the field.
type AdjacentAreaGetter[T any] func(originCoord *Coordinate, relativeCoord *Coordinate) (area *T, isCrossBorder bool)

// NextAreaGenerator tells the game how you're gonna generate next status of the given area.
type NextAreaGenerator[T any] func(coord *Coordinate, area *T, getAdjacentArea AdjacentAreaGetter[T]) (nextArea *T)

// FieldIteratorCallback will be called when iterating through field.
type FieldIteratorCallback[T any] func(coord *Coordinate, area *T)
31 changes: 31 additions & 0 deletions ggol_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,3 +412,34 @@ func testGetFieldCaseOne(t *testing.T) {
func TestGetField(t *testing.T) {
testGetFieldCaseOne(t)
}

func testIterateFieldCaseOne(t *testing.T) {
width := 3
height := 3
fieldSize := FieldSize{Width: width, Height: height}
g, _ := NewGame(&fieldSize, &initialAreaForTest)
g.SetArea(&Coordinate{X: 1, Y: 0}, &areaForTest{hasLiveCell: true})
g.SetArea(&Coordinate{X: 1, Y: 1}, &areaForTest{hasLiveCell: true})
g.SetArea(&Coordinate{X: 1, Y: 2}, &areaForTest{hasLiveCell: true})
sumsOfXCoord := 0
sumsOfYCoord := 0
aliveCellCount := 0

g.IterateField(func(c *Coordinate, area *areaForTest) {
sumsOfXCoord += c.X
sumsOfYCoord += c.Y
if area.hasLiveCell {
aliveCellCount += 1
}
})

if sumsOfXCoord == 9 && sumsOfYCoord == 9 && aliveCellCount == 3 {
t.Log("Passed")
} else {
t.Fatalf("Did not iterate through field correctly.")
}
}

func TestIterateField(t *testing.T) {
testIterateFieldCaseOne(t)
}

0 comments on commit a6cf688

Please sign in to comment.