feat: add service and model
This commit is contained in:
5
storage/model/cage.go
Normal file
5
storage/model/cage.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package model
|
||||
|
||||
type Cage struct {
|
||||
Hamsters []*Hamster
|
||||
}
|
||||
8
storage/model/game.go
Normal file
8
storage/model/game.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package model
|
||||
|
||||
type Game struct {
|
||||
ID string
|
||||
server Server
|
||||
Cages []*Cage
|
||||
SelledHamster []*Hamster
|
||||
}
|
||||
155
storage/model/hamster.go
Normal file
155
storage/model/hamster.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"hamster-tycoon/randomizer"
|
||||
)
|
||||
|
||||
const (
|
||||
Full = iota
|
||||
AlmostFull
|
||||
Middle
|
||||
AlmostEmpty
|
||||
Empty
|
||||
)
|
||||
|
||||
const (
|
||||
TotalGestationPeriod = 15
|
||||
MinChild = 5
|
||||
MaxChild = 9
|
||||
GestationCooldown = (3 * 7) + 2
|
||||
GestationMinAge = 10 * 6
|
||||
MaxAge = 365 * 2
|
||||
GestationLuck = 70
|
||||
FEMALE = "F"
|
||||
MALE = "M"
|
||||
)
|
||||
|
||||
var GlobalHamsterNumber = 1
|
||||
|
||||
type Hamster struct {
|
||||
ID string
|
||||
Name string
|
||||
Number int
|
||||
Sexe string
|
||||
Age int // in days
|
||||
Father *Hamster
|
||||
Mother *Hamster
|
||||
HungerLevel int8
|
||||
ThirstLevel int8
|
||||
Weight float64
|
||||
Height float64
|
||||
Alive bool
|
||||
Selled bool
|
||||
Gestation bool
|
||||
GestationPeriod int8
|
||||
GestationCooldown int8
|
||||
GestationFather *Hamster
|
||||
Child []*Hamster
|
||||
}
|
||||
|
||||
func (h *Hamster) Die() {
|
||||
h.Alive = false
|
||||
}
|
||||
|
||||
func (h *Hamster) Grow() {
|
||||
if h.Age >= MaxAge {
|
||||
if randNumber(h.Age-MaxAge, 365) > 364 {
|
||||
h.Die()
|
||||
return
|
||||
}
|
||||
}
|
||||
h.Age++
|
||||
if h.Gestation {
|
||||
h.GestationPeriod++
|
||||
} else {
|
||||
if h.GestationCooldown > 0 {
|
||||
h.GestationCooldown--
|
||||
}
|
||||
}
|
||||
fmt.Println(h.GestationCooldown)
|
||||
}
|
||||
|
||||
func (h *Hamster) Fuck(another *Hamster) (bool, error) {
|
||||
if h.Sexe == another.Sexe {
|
||||
return false, errors.New("can't fuck together")
|
||||
}
|
||||
|
||||
if h.Age < GestationMinAge || another.Age < GestationMinAge {
|
||||
return false, errors.New("one of the hamster is too young")
|
||||
}
|
||||
|
||||
rand := randNumber(1, 100)
|
||||
|
||||
if rand <= GestationLuck {
|
||||
female, male := func() (*Hamster, *Hamster) {
|
||||
if h.Sexe == FEMALE {
|
||||
return h, another
|
||||
} else {
|
||||
return another, h
|
||||
}
|
||||
}()
|
||||
female.GestationFather = male
|
||||
female.Gestation = true
|
||||
female.GestationPeriod = 0
|
||||
female.GestationCooldown = TotalGestationPeriod + GestationCooldown
|
||||
}
|
||||
return rand <= GestationLuck, nil
|
||||
}
|
||||
|
||||
func Born(father *Hamster, mother *Hamster) ([]*Hamster, error) {
|
||||
if !mother.Alive || mother.Selled {
|
||||
return nil, errors.New("the mother is not here")
|
||||
}
|
||||
if mother.GestationPeriod != TotalGestationPeriod {
|
||||
return nil, errors.New("not the time to make child")
|
||||
}
|
||||
|
||||
mother.GestationCooldown = 0
|
||||
mother.Gestation = false
|
||||
mother.GestationPeriod = 0
|
||||
|
||||
numberOfChild := randNumberChild()
|
||||
child := make([]*Hamster, numberOfChild)
|
||||
for i := 0; i < numberOfChild; i++ {
|
||||
child[i] = &Hamster{
|
||||
ID: uuid.NewV4().String(),
|
||||
Name: fmt.Sprintf("Hamster %d", GlobalHamsterNumber),
|
||||
Number: GlobalHamsterNumber,
|
||||
Age: 1,
|
||||
Sexe: randSexe(),
|
||||
Father: father,
|
||||
Mother: mother,
|
||||
HungerLevel: Full,
|
||||
ThirstLevel: Full,
|
||||
Alive: true,
|
||||
Selled: false,
|
||||
Gestation: false,
|
||||
GestationPeriod: 0,
|
||||
GestationCooldown: 30,
|
||||
}
|
||||
}
|
||||
mother.Child = append(mother.Child, child...)
|
||||
father.Child = append(father.Child, child...)
|
||||
return child, nil
|
||||
}
|
||||
|
||||
func randNumberChild() int {
|
||||
return randNumber(MinChild, MaxChild)
|
||||
}
|
||||
|
||||
func randSexe() string {
|
||||
return func() string {
|
||||
if randNumber(1, 2) == 2 {
|
||||
return MALE
|
||||
} else {
|
||||
return FEMALE
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func randNumber(min int, max int) int {
|
||||
return min + randomizer.Rand(max)
|
||||
}
|
||||
333
storage/model/hamster_test.go
Normal file
333
storage/model/hamster_test.go
Normal file
@@ -0,0 +1,333 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"hamster-tycoon/randomizer"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHamster_Die(t *testing.T) {
|
||||
testCases := []struct {
|
||||
caseName string
|
||||
hamster *Hamster
|
||||
expectedAlive bool
|
||||
}{
|
||||
{
|
||||
caseName: "Should die",
|
||||
hamster: &Hamster{
|
||||
Alive: true,
|
||||
},
|
||||
expectedAlive: false,
|
||||
},
|
||||
{
|
||||
caseName: "Already die",
|
||||
hamster: &Hamster{
|
||||
Alive: false,
|
||||
},
|
||||
expectedAlive: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.caseName, func(t *testing.T) {
|
||||
tc.hamster.Die()
|
||||
if tc.hamster.Alive != tc.expectedAlive {
|
||||
t.Errorf("Die result does not match expectation. \n Got : %t \n Get : %t", tc.expectedAlive, tc.hamster.Alive)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHamster_Grow(t *testing.T) {
|
||||
testCases := []struct {
|
||||
caseName string
|
||||
hamster *Hamster
|
||||
expectedHamster *Hamster
|
||||
}{
|
||||
{
|
||||
caseName: "Age increment",
|
||||
hamster: &Hamster{
|
||||
Age: 0,
|
||||
Alive: true,
|
||||
},
|
||||
expectedHamster: &Hamster{
|
||||
Age: 1,
|
||||
Alive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
caseName: "Gestation decrement",
|
||||
hamster: &Hamster{
|
||||
Age: 0,
|
||||
Gestation: true,
|
||||
GestationPeriod: 1,
|
||||
Alive: true,
|
||||
},
|
||||
expectedHamster: &Hamster{
|
||||
Age: 1,
|
||||
Gestation: true,
|
||||
GestationPeriod: 2,
|
||||
Alive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
caseName: "Gestation Cooldown doesn't decrement",
|
||||
hamster: &Hamster{
|
||||
Age: 0,
|
||||
Gestation: true,
|
||||
GestationCooldown: 17,
|
||||
GestationPeriod: 1,
|
||||
Alive: true,
|
||||
},
|
||||
expectedHamster: &Hamster{
|
||||
Age: 1,
|
||||
GestationCooldown: 17,
|
||||
Gestation: true,
|
||||
GestationPeriod: 2,
|
||||
Alive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
caseName: "Gestation Cooldown decrement",
|
||||
hamster: &Hamster{
|
||||
Age: 0,
|
||||
Gestation: false,
|
||||
GestationCooldown: 17,
|
||||
GestationPeriod: 0,
|
||||
Alive: true,
|
||||
},
|
||||
expectedHamster: &Hamster{
|
||||
Age: 1,
|
||||
GestationCooldown: 16,
|
||||
Gestation: false,
|
||||
GestationPeriod: 0,
|
||||
Alive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
caseName: "Hamster die",
|
||||
hamster: &Hamster{
|
||||
Age: 365 * 3,
|
||||
Gestation: false,
|
||||
GestationCooldown: 17,
|
||||
GestationPeriod: 0,
|
||||
Alive: true,
|
||||
},
|
||||
expectedHamster: &Hamster{
|
||||
Age: 365 * 3,
|
||||
Alive: false,
|
||||
GestationCooldown: 17,
|
||||
Gestation: false,
|
||||
GestationPeriod: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.caseName, func(t *testing.T) {
|
||||
if tc.hamster.Alive == !tc.expectedHamster.Alive {
|
||||
randomizer.FakeRandomizer(364)
|
||||
}
|
||||
tc.hamster.Grow()
|
||||
if !reflect.DeepEqual(tc.hamster, tc.expectedHamster) {
|
||||
t.Errorf("Got %v expect %v", tc.hamster, tc.expectedHamster)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHamster_Fuck(t *testing.T) {
|
||||
testCases := []struct {
|
||||
caseName string
|
||||
hamster1 *Hamster
|
||||
hamster2 *Hamster
|
||||
expectedResult bool
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
caseName: "Same sexe",
|
||||
hamster1: &Hamster{
|
||||
Alive: true,
|
||||
Sexe: MALE,
|
||||
Age: GestationMinAge - 1,
|
||||
},
|
||||
hamster2: &Hamster{
|
||||
Alive: true,
|
||||
Sexe: MALE,
|
||||
Age: GestationMinAge + 1,
|
||||
},
|
||||
expectedResult: false,
|
||||
expectedError: errors.New("can't fuck together"),
|
||||
},
|
||||
{
|
||||
caseName: "Hamster 1 too young",
|
||||
hamster1: &Hamster{
|
||||
Alive: true,
|
||||
Sexe: MALE,
|
||||
Age: GestationMinAge - 1,
|
||||
},
|
||||
hamster2: &Hamster{
|
||||
Alive: true,
|
||||
Sexe: FEMALE,
|
||||
Age: GestationMinAge + 1,
|
||||
},
|
||||
expectedResult: false,
|
||||
expectedError: errors.New("one of the hamster is too young"),
|
||||
},
|
||||
{
|
||||
caseName: "Hamster 2 too young",
|
||||
hamster1: &Hamster{
|
||||
Alive: false,
|
||||
Sexe: MALE,
|
||||
Age: GestationMinAge + 1,
|
||||
},
|
||||
hamster2: &Hamster{
|
||||
Alive: true,
|
||||
Sexe: FEMALE,
|
||||
Age: GestationMinAge - 1,
|
||||
},
|
||||
expectedResult: false,
|
||||
expectedError: errors.New("one of the hamster is too young"),
|
||||
},
|
||||
{
|
||||
caseName: "Can have sexe and success",
|
||||
hamster1: &Hamster{
|
||||
Alive: false,
|
||||
Sexe: MALE,
|
||||
Age: GestationMinAge,
|
||||
},
|
||||
hamster2: &Hamster{
|
||||
Alive: true,
|
||||
Sexe: FEMALE,
|
||||
Age: GestationMinAge,
|
||||
},
|
||||
expectedResult: true,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
caseName: "Can have sexe and fail",
|
||||
hamster1: &Hamster{
|
||||
Alive: false,
|
||||
Sexe: MALE,
|
||||
Age: GestationMinAge,
|
||||
},
|
||||
hamster2: &Hamster{
|
||||
Alive: true,
|
||||
Sexe: FEMALE,
|
||||
Age: GestationMinAge,
|
||||
},
|
||||
expectedResult: false,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
caseName: "Can have sexe and success. Inverted female and male",
|
||||
hamster2: &Hamster{
|
||||
Alive: false,
|
||||
Sexe: MALE,
|
||||
Age: GestationMinAge,
|
||||
},
|
||||
hamster1: &Hamster{
|
||||
Alive: true,
|
||||
Sexe: FEMALE,
|
||||
Age: GestationMinAge,
|
||||
},
|
||||
expectedResult: true,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.caseName, func(t *testing.T) {
|
||||
if tc.expectedResult {
|
||||
randomizer.FakeRandomizer(60)
|
||||
} else {
|
||||
randomizer.FakeRandomizer(80)
|
||||
}
|
||||
bool, err := tc.hamster1.Fuck(tc.hamster2)
|
||||
if tc.expectedResult != bool {
|
||||
t.Errorf("Fuck result does not match expectation. \n Got : %t \n Get : %t", tc.expectedResult, bool)
|
||||
}
|
||||
if tc.expectedError != nil && err != nil && tc.expectedError.Error() != err.Error() {
|
||||
t.Errorf("Fuck result does not match expectation. \n Got : %s \n Get : %s", tc.expectedError, err)
|
||||
}
|
||||
|
||||
if tc.expectedError == nil && err != nil {
|
||||
t.Errorf("Fuck result does not match expectation. \n Got error : %s ", err)
|
||||
}
|
||||
|
||||
if tc.expectedResult {
|
||||
var female *Hamster
|
||||
if FEMALE == tc.hamster1.Sexe {
|
||||
female = tc.hamster1
|
||||
} else {
|
||||
female = tc.hamster2
|
||||
}
|
||||
if !female.Gestation {
|
||||
t.Errorf("Female is not in gestation")
|
||||
}
|
||||
if female.GestationPeriod != 0 {
|
||||
t.Errorf("Gestation period doesn't have been reseted")
|
||||
}
|
||||
if female.GestationCooldown != TotalGestationPeriod+GestationCooldown {
|
||||
t.Errorf("Gestation period doesn't have been reseted")
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_randSexe(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want string
|
||||
fakeGen int
|
||||
}{
|
||||
{
|
||||
name: "Should be a male",
|
||||
fakeGen: 1,
|
||||
want: MALE,
|
||||
},
|
||||
{
|
||||
name: "Should be a female",
|
||||
fakeGen: 0,
|
||||
want: FEMALE,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
randomizer.FakeRandomizer(tt.fakeGen)
|
||||
if got := randSexe(); got != tt.want {
|
||||
t.Errorf("%q. randSexe() = %v, want %v", tt.name, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_randNumber(t *testing.T) {
|
||||
type args struct {
|
||||
min int
|
||||
max int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
fakeGen int
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "Should add min to result",
|
||||
args: args{
|
||||
min: 1,
|
||||
max: 2,
|
||||
},
|
||||
fakeGen: 0,
|
||||
want: 1,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
randomizer.FakeRandomizer(tt.fakeGen)
|
||||
if got := randNumber(tt.args.min, tt.args.max); got != tt.want {
|
||||
t.Errorf("%q. randNumber() = %v, want %v", tt.name, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
5
storage/model/server.go
Normal file
5
storage/model/server.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package model
|
||||
|
||||
type Server struct {
|
||||
ID string
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
UserEditable
|
||||
@@ -8,6 +10,7 @@ type User struct {
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt *time.Time `json:"updatedAt"`
|
||||
GoogleId string `json:"-"`
|
||||
Games []Game
|
||||
}
|
||||
|
||||
type UserEditable struct {
|
||||
|
||||
Reference in New Issue
Block a user