Skip to content
This repository was archived by the owner on Sep 24, 2022. It is now read-only.

Commit ec710da

Browse files
committed
Add initial version
0 parents  commit ec710da

26 files changed

+1233
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Jetbrain IDE
2+
.idea

README.md

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Candy
2+
3+
Simple 2D multi-player online game.
4+
5+
## Prerequisites
6+
7+
- [Go v1.15.6](https://golang.org/doc/install)
8+
9+
## Getting Started
10+
11+
```bash
12+
go run main.go
13+
```
14+
15+
![](docs/preview.png)
16+
17+
## Usage
18+
19+
| Action | Function |
20+
| ------ | -------- |
21+
| Up arrow key | Move player upward |
22+
| Donw arrow key | Move player downward |
23+
| Left arrow key| Move player to the left |
24+
| Right arrow key| Move player to the right |
25+
26+
## Testing
27+
28+
```bash
29+
go test ./...
30+
```
31+
32+
## Tools
33+
34+
### Format code
35+
36+
```bash
37+
goimports -w .
38+
```
39+
40+
## Author
41+
42+
- Harry - *Initial works*
43+
44+
## Contributors
45+
46+
## License
47+
48+
This project is maintained under MIT license.

assets.sketch

256 KB
Binary file not shown.

assets/assets.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package assets
2+
3+
import (
4+
"image"
5+
_ "image/png"
6+
"os"
7+
"path/filepath"
8+
)
9+
10+
var images = []string{
11+
"map/default.png",
12+
"sprite_sheet.png",
13+
}
14+
15+
type Assets struct {
16+
imageMap map[string]image.Image
17+
}
18+
19+
func (a Assets) GetImage(imageName string) image.Image {
20+
return a.imageMap[imageName]
21+
}
22+
23+
func LoadAssets(assetRootDir string) (Assets, error) {
24+
imageMap := make(map[string]image.Image)
25+
26+
for _, imgPath := range images {
27+
file, err := os.Open(filepath.Join(assetRootDir, imgPath))
28+
if err != nil {
29+
return Assets{}, err
30+
}
31+
32+
img, _, err := image.Decode(file)
33+
if err != nil {
34+
_ = file.Close()
35+
return Assets{}, err
36+
}
37+
_ = file.Close()
38+
imageMap[imgPath] = img
39+
}
40+
41+
return Assets{imageMap: imageMap}, nil
42+
}

assets/map/default.png

40.3 KB
Loading

assets/sprite_sheet.png

157 KB
Loading

collison.sketch

236 KB
Binary file not shown.

docs/preview.png

254 KB
Loading

game/cell/cell.go

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package cell
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
type Cell struct {
8+
Row int
9+
Col int
10+
}
11+
12+
func (c Cell) String() string {
13+
return fmt.Sprintf("[Row:%d,Col:%d]", c.Row, c.Col)
14+
}

game/cell/corners.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package cell
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
type CornerCells struct {
8+
TopLeft Cell
9+
TopRight Cell
10+
BottomLeft Cell
11+
BottomRight Cell
12+
}
13+
14+
func (c CornerCells) String() string {
15+
return fmt.Sprintf("[TopLeft:%v,TopRight:%v,BottomLeft:%v,BottomRight:%v]", c.TopLeft, c.TopRight, c.BottomLeft, c.BottomRight)
16+
}
17+
18+
// Detailed analysis: https://docs.google.com/document/d/1FF49lQrCCNLEuqInmXRPnbl-jvDLLtqor6Q4OgGUIy4/edit
19+
func GetCornerCells(
20+
bottomLeftX int,
21+
bottomLeftY int,
22+
objectWidth int,
23+
objectHeight int,
24+
cellWidth int,
25+
cellHeight int,
26+
) CornerCells {
27+
topLeft := getTopLeftCell(bottomLeftX, bottomLeftY, objectHeight, cellWidth, cellHeight)
28+
topRight := getTopRightCell(bottomLeftX, bottomLeftY, objectWidth, objectHeight, cellWidth, cellHeight)
29+
bottomLeft := Cell{
30+
Row: bottomLeftY / cellHeight,
31+
Col: bottomLeftX / cellWidth,
32+
}
33+
bottomRight := getBottomRightCell(bottomLeftX, bottomLeftY, objectWidth, cellWidth, cellHeight)
34+
return CornerCells{
35+
TopLeft: topLeft,
36+
TopRight: topRight,
37+
BottomLeft: bottomLeft,
38+
BottomRight: bottomRight,
39+
}
40+
}
41+
42+
func getTopLeftCell(bottomLeftX int, bottomLeftY int, objectHeight int, cellWidth int, cellHeight int) Cell {
43+
topY := bottomLeftY + objectHeight
44+
row := topY / cellHeight
45+
if topY%cellHeight == 0 {
46+
row--
47+
}
48+
column := bottomLeftX / cellWidth
49+
return Cell{
50+
Row: row,
51+
Col: column,
52+
}
53+
}
54+
55+
func getTopRightCell(bottomLeftX int, bottomLeftY int, objectWidth int, objectHeight int, cellWidth int, cellHeight int) Cell {
56+
topY := bottomLeftY + objectHeight
57+
row := topY / cellHeight
58+
if topY%cellHeight == 0 {
59+
row--
60+
}
61+
rightX := bottomLeftX + objectWidth
62+
column := rightX / cellWidth
63+
if rightX%cellWidth == 0 {
64+
column--
65+
}
66+
return Cell{
67+
Row: row,
68+
Col: column,
69+
}
70+
}
71+
72+
func getBottomRightCell(bottomLeftX int, bottomLeftY int, objectWidth int, cellWidth int, cellHeight int) Cell {
73+
row := bottomLeftY / cellHeight
74+
rightX := bottomLeftX + objectWidth
75+
column := rightX / cellWidth
76+
if rightX%cellWidth == 0 {
77+
column--
78+
}
79+
return Cell{
80+
Row: row,
81+
Col: column,
82+
}
83+
}

game/cell/corners_test.go

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package cell
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestGetCornerCells(t *testing.T) {
10+
testCases := []struct {
11+
name string
12+
bottomLeftX int
13+
bottomLeftY int
14+
objectWidth int
15+
objectHeight int
16+
cellWidth int
17+
cellHeight int
18+
expectedCornerCells CornerCells
19+
}{
20+
{
21+
name: "Object corners overlap with grid corners",
22+
bottomLeftX: 4, bottomLeftY: 2, objectWidth: 2, objectHeight: 2,
23+
cellWidth: 2, cellHeight: 2,
24+
expectedCornerCells: CornerCells{
25+
TopLeft: Cell{Row: 1, Col: 2},
26+
TopRight: Cell{Row: 1, Col: 2},
27+
BottomLeft: Cell{Row: 1, Col: 2},
28+
BottomRight: Cell{Row: 1, Col: 2},
29+
},
30+
},
31+
{
32+
name: "Object corners on vertical grid lines",
33+
bottomLeftX: 8, bottomLeftY: 3, objectWidth: 2, objectHeight: 2,
34+
cellWidth: 2, cellHeight: 2,
35+
expectedCornerCells: CornerCells{
36+
TopLeft: Cell{Row: 2, Col: 4},
37+
TopRight: Cell{Row: 2, Col: 4},
38+
BottomLeft: Cell{Row: 1, Col: 4},
39+
BottomRight: Cell{Row: 1, Col: 4},
40+
},
41+
},
42+
{
43+
name: "Object corners on horizontal grid lines",
44+
bottomLeftX: 5, bottomLeftY: 2, objectWidth: 2, objectHeight: 2,
45+
cellWidth: 2, cellHeight: 2,
46+
expectedCornerCells: CornerCells{
47+
TopLeft: Cell{Row: 1, Col: 2},
48+
TopRight: Cell{Row: 1, Col: 3},
49+
BottomLeft: Cell{Row: 1, Col: 2},
50+
BottomRight: Cell{Row: 1, Col: 3},
51+
},
52+
},
53+
{
54+
name: "Object corners not on grid lines",
55+
bottomLeftX: 9, bottomLeftY: 3, objectWidth: 2, objectHeight: 2,
56+
cellWidth: 2, cellHeight: 2,
57+
expectedCornerCells: CornerCells{
58+
TopLeft: Cell{Row: 2, Col: 4},
59+
TopRight: Cell{Row: 2, Col: 5},
60+
BottomLeft: Cell{Row: 1, Col: 4},
61+
BottomRight: Cell{Row: 1, Col: 5},
62+
},
63+
},
64+
{
65+
name: "Object width occupy multiple columns and height occupy multiple columns",
66+
bottomLeftX: 4, bottomLeftY: 2, objectWidth: 4, objectHeight: 6,
67+
cellWidth: 2, cellHeight: 2,
68+
expectedCornerCells: CornerCells{
69+
TopLeft: Cell{Row: 3, Col: 2},
70+
TopRight: Cell{Row: 3, Col: 3},
71+
BottomLeft: Cell{Row: 1, Col: 2},
72+
BottomRight: Cell{Row: 1, Col: 3},
73+
},
74+
},
75+
}
76+
77+
for _, testCase := range testCases {
78+
t.Run(testCase.name, func(t *testing.T) {
79+
cornerCells := GetCornerCells(
80+
testCase.bottomLeftX, testCase.bottomLeftY, testCase.objectWidth, testCase.objectHeight,
81+
testCase.cellWidth, testCase.cellHeight,
82+
)
83+
assert.Equal(t, testCase.expectedCornerCells, cornerCells)
84+
})
85+
}
86+
}

game/cell/neighbors.go

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package cell
2+
3+
func GetLeftNeighborCells(cornerCells CornerCells, minCol int) []Cell {
4+
neighborCells := make([]Cell, 0)
5+
if cornerCells.TopLeft.Col-1 >= minCol {
6+
// Add left col
7+
for row := cornerCells.BottomLeft.Row; row <= cornerCells.TopLeft.Row; row++ {
8+
cell := Cell{
9+
Row: row,
10+
Col: cornerCells.TopLeft.Col - 1,
11+
}
12+
neighborCells = append(neighborCells, cell)
13+
}
14+
}
15+
return neighborCells
16+
}
17+
18+
func GetRightNeighborCells(cornerCells CornerCells, maxCol int) []Cell {
19+
neighborCells := make([]Cell, 0)
20+
if cornerCells.BottomRight.Col+1 <= maxCol {
21+
// Add right col
22+
for row := cornerCells.BottomLeft.Row; row <= cornerCells.TopLeft.Row; row++ {
23+
cell := Cell{
24+
Row: row,
25+
Col: cornerCells.BottomRight.Col + 1,
26+
}
27+
neighborCells = append(neighborCells, cell)
28+
}
29+
}
30+
return neighborCells
31+
}
32+
33+
func GetTopNeighborCells(cornerCells CornerCells, maxRow int) []Cell {
34+
neighborCells := make([]Cell, 0)
35+
if cornerCells.TopLeft.Row+1 <= maxRow {
36+
// Add top row
37+
for col := cornerCells.TopLeft.Col; col <= cornerCells.TopRight.Col; col++ {
38+
cell := Cell{
39+
Row: cornerCells.TopLeft.Row + 1,
40+
Col: col,
41+
}
42+
neighborCells = append(neighborCells, cell)
43+
}
44+
}
45+
return neighborCells
46+
}
47+
48+
func GetBottomNeighborCells(cornerCells CornerCells, minRow int) []Cell {
49+
neighborCells := make([]Cell, 0)
50+
if cornerCells.BottomRight.Row-1 >= minRow {
51+
// Add bottom row
52+
for col := cornerCells.BottomLeft.Col; col <= cornerCells.BottomRight.Col; col++ {
53+
cell := Cell{
54+
Row: cornerCells.BottomRight.Row - 1,
55+
Col: col,
56+
}
57+
neighborCells = append(neighborCells, cell)
58+
}
59+
}
60+
return neighborCells
61+
}

game/direction/direction.go

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package direction
2+
3+
type Direction int
4+
5+
const (
6+
Up Direction = iota
7+
Right
8+
Left
9+
Down
10+
)

0 commit comments

Comments
 (0)