commands A, W
This commit is contained in:
+8
-16
@@ -3,29 +3,21 @@ package game
|
|||||||
import "github.com/iliadenisov/galaxy/pkg/model/game"
|
import "github.com/iliadenisov/galaxy/pkg/model/game"
|
||||||
|
|
||||||
func DeclareWar(configure func(*Param), from, to string) (err error) {
|
func DeclareWar(configure func(*Param), from, to string) (err error) {
|
||||||
control(configure, func(c *ctrl) { c.execute(func(r Repo) { err = updateRelation(r, from, to, game.RelationWar) }) })
|
control(configure, func(c *ctrl) {
|
||||||
|
c.execute(func(r Repo, g game.Game) { err = updateRelation(r, g, from, to, game.RelationWar) })
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeclarePeace(configure func(*Param), from, to string) (err error) {
|
func DeclarePeace(configure func(*Param), from, to string) (err error) {
|
||||||
control(configure, func(c *ctrl) { c.execute(func(r Repo) { err = updateRelation(r, from, to, game.RelationPeace) }) })
|
control(configure, func(c *ctrl) {
|
||||||
|
c.execute(func(r Repo, g game.Game) { err = updateRelation(r, g, from, to, game.RelationPeace) })
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateRelation(r Repo, hostRace, opponentRace string, rel game.Relation) error {
|
func updateRelation(r Repo, g game.Game, hostRace, opponentRace string, rel game.Relation) error {
|
||||||
g, err := r.LoadState()
|
if err := g.UpdateRelation(hostRace, opponentRace, rel); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hostID, err := g.HostRaceID(hostRace)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
opponentID, err := g.OpponentRaceID(opponentRace)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := g.UpdateRelation(hostID, opponentID, rel); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return r.SaveState(g)
|
return r.SaveState(g)
|
||||||
|
|||||||
+58
-14
@@ -1,26 +1,70 @@
|
|||||||
package game_test
|
package game_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/iliadenisov/galaxy/pkg/game"
|
"github.com/iliadenisov/galaxy/pkg/game"
|
||||||
"github.com/iliadenisov/galaxy/pkg/util"
|
mg "github.com/iliadenisov/galaxy/pkg/model/game"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRelation(t *testing.T) {
|
func TestDeclarePeaceAndWarSingle(t *testing.T) {
|
||||||
root, cleanup := util.CreateWorkDir(t)
|
g(t, func(f func(*game.Param), g func() mg.Game) {
|
||||||
defer cleanup()
|
hostRace := "race_05"
|
||||||
players := 20
|
opponentRace := "race_01"
|
||||||
races := make([]string, players)
|
|
||||||
for i := range players {
|
|
||||||
races[i] = fmt.Sprintf("race_%02d", i)
|
|
||||||
}
|
|
||||||
_, err := game.ComposeGame(func(p *game.Param) { p.StoragePath = root }, races)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = game.DeclarePeace(func(p *game.Param) { p.StoragePath = root }, "race_05", "race_01")
|
r, err := g().Relation(hostRace, opponentRace)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// TODO: check relation state changed
|
assert.Equal(t, mg.RelationWar, r.Relation)
|
||||||
|
|
||||||
|
assert.NoError(t, game.DeclarePeace(f, hostRace, opponentRace))
|
||||||
|
r, err = g().Relation(hostRace, opponentRace)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, mg.RelationPeace, r.Relation)
|
||||||
|
|
||||||
|
assert.NoError(t, game.DeclareWar(f, hostRace, opponentRace))
|
||||||
|
r, err = g().Relation(hostRace, opponentRace)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, mg.RelationWar, r.Relation)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeclarePeaceAndWarAll(t *testing.T) {
|
||||||
|
g(t, func(f func(*game.Param), g func() mg.Game) {
|
||||||
|
hostRace := "race_07"
|
||||||
|
|
||||||
|
for i := range testRaceCount {
|
||||||
|
opponentRace := raceNum(i)
|
||||||
|
if opponentRace == hostRace {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r, err := g().Relation(hostRace, opponentRace)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, mg.RelationWar, r.Relation)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NoError(t, game.DeclarePeace(f, hostRace, hostRace))
|
||||||
|
|
||||||
|
for i := range testRaceCount {
|
||||||
|
opponentRace := raceNum(i)
|
||||||
|
if opponentRace == hostRace {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r, err := g().Relation(hostRace, opponentRace)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, mg.RelationPeace, r.Relation)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NoError(t, game.DeclareWar(f, hostRace, hostRace))
|
||||||
|
|
||||||
|
for i := range testRaceCount {
|
||||||
|
opponentRace := raceNum(i)
|
||||||
|
if opponentRace == hostRace {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r, err := g().Relation(hostRace, opponentRace)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, mg.RelationWar, r.Relation)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-3
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/iliadenisov/galaxy/pkg/model/game"
|
||||||
"github.com/iliadenisov/galaxy/pkg/repo"
|
"github.com/iliadenisov/galaxy/pkg/repo"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,8 +17,13 @@ type Param struct {
|
|||||||
StoragePath string
|
StoragePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func ComposeGame(configure func(*Param), races []string) (gameID uuid.UUID, err error) {
|
func LoadState(configure func(*Param)) (g game.Game, err error) {
|
||||||
control(configure, func(c *ctrl) { c.execute(func(r Repo) { gameID, err = newGame(r, races) }) })
|
control(configure, func(c *ctrl) { c.executeInit(func(r Repo) { g, err = c.repo.LoadState() }) })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateGame(configure func(*Param), races []string) (gameID uuid.UUID, err error) {
|
||||||
|
control(configure, func(c *ctrl) { c.executeInit(func(r Repo) { gameID, err = newGame(r, races) }) })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,10 +53,22 @@ func control(configure func(*Param), consumer func(*ctrl)) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ctrl) execute(consumer func(Repo)) error {
|
func (c *ctrl) executeInit(consumer func(Repo)) error {
|
||||||
if err := c.repo.Lock(); err != nil {
|
if err := c.repo.Lock(); err != nil {
|
||||||
return fmt.Errorf("execute: lock failed: %s", err)
|
return fmt.Errorf("execute: lock failed: %s", err)
|
||||||
}
|
}
|
||||||
consumer(c.repo)
|
consumer(c.repo)
|
||||||
return c.repo.Release()
|
return c.repo.Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ctrl) execute(consumer func(Repo, game.Game)) error {
|
||||||
|
if err := c.repo.Lock(); err != nil {
|
||||||
|
return fmt.Errorf("execute: lock failed: %s", err)
|
||||||
|
}
|
||||||
|
g, err := c.repo.LoadState()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
consumer(c.repo, g)
|
||||||
|
return c.repo.Release()
|
||||||
|
}
|
||||||
|
|||||||
+36
-10
@@ -5,21 +5,47 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/iliadenisov/galaxy/pkg/game"
|
"github.com/iliadenisov/galaxy/pkg/game"
|
||||||
|
mg "github.com/iliadenisov/galaxy/pkg/model/game"
|
||||||
"github.com/iliadenisov/galaxy/pkg/util"
|
"github.com/iliadenisov/galaxy/pkg/util"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testRaceCount = 20
|
||||||
|
)
|
||||||
|
|
||||||
|
func raceNum(i int) string {
|
||||||
|
return fmt.Sprintf("race_%02d", i)
|
||||||
|
}
|
||||||
|
|
||||||
func TestComposeGame(t *testing.T) {
|
func TestComposeGame(t *testing.T) {
|
||||||
root, cleanup := util.CreateWorkDir(t)
|
g(t, func(p func(*game.Param), g func() mg.Game) {
|
||||||
defer cleanup()
|
_, err := game.GenerateGame(p, []string{"r1", "r2"})
|
||||||
players := 20
|
|
||||||
races := make([]string, players)
|
|
||||||
for i := range players {
|
|
||||||
races[i] = fmt.Sprintf("race_%02d", i)
|
|
||||||
}
|
|
||||||
_, err := game.ComposeGame(func(p *game.Param) { p.StoragePath = root }, races)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
_, err = game.ComposeGame(func(p *game.Param) { p.StoragePath = root }, races)
|
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.ErrorContains(t, err, "state for turn 0 already saved")
|
assert.ErrorContains(t, err, "state for turn 0 already saved")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func g(t *testing.T, f func(func(*game.Param), func() mg.Game)) {
|
||||||
|
root, cleanup := util.CreateWorkDir(t)
|
||||||
|
defer cleanup()
|
||||||
|
races := make([]string, testRaceCount)
|
||||||
|
for i := range testRaceCount {
|
||||||
|
races[i] = raceNum(i)
|
||||||
|
}
|
||||||
|
p := func(p *game.Param) { p.StoragePath = root }
|
||||||
|
_, err := game.GenerateGame(p, races)
|
||||||
|
if err != nil {
|
||||||
|
assert.FailNow(t, "g: ComposeGame", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g := func() mg.Game {
|
||||||
|
g, err := game.LoadState(p)
|
||||||
|
if err != nil {
|
||||||
|
assert.FailNow(t, "g: LoadState", err)
|
||||||
|
return mg.Game{}
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
f(p, g)
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-4
@@ -25,14 +25,14 @@ func (g Game) Votes(raceID uuid.UUID) float64 {
|
|||||||
return pop / 1000.
|
return pop / 1000.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Game) HostRaceID(name string) (uuid.UUID, error) {
|
func (g Game) hostRaceID(name string) (uuid.UUID, error) {
|
||||||
if v, ok := g.raceID(name); ok {
|
if v, ok := g.raceID(name); ok {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
return uuid.Nil, e.NewHostRaceUnknownError(name)
|
return uuid.Nil, e.NewHostRaceUnknownError(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Game) OpponentRaceID(name string) (uuid.UUID, error) {
|
func (g Game) opponentRaceID(name string) (uuid.UUID, error) {
|
||||||
if v, ok := g.raceID(name); ok {
|
if v, ok := g.raceID(name); ok {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
@@ -48,19 +48,67 @@ func (g Game) raceID(raceName string) (uuid.UUID, bool) {
|
|||||||
return uuid.Nil, false
|
return uuid.Nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Game) UpdateRelation(hostID, opponentID uuid.UUID, rel Relation) error {
|
func (g Game) UpdateRelation(hostRace, opponentRace string, rel Relation) error {
|
||||||
|
hostID, err := g.hostRaceID(hostRace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var opponentID uuid.UUID
|
||||||
|
if hostRace == opponentRace {
|
||||||
|
opponentID = hostID
|
||||||
|
} else if opponentID, err = g.opponentRaceID(opponentRace); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return g.updateRelationInternal(hostID, opponentID, rel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Game) updateRelationInternal(hostID, opponentID uuid.UUID, rel Relation) error {
|
||||||
for r := range g.Race {
|
for r := range g.Race {
|
||||||
if g.Race[r].ID == hostID {
|
if g.Race[r].ID == hostID {
|
||||||
for o := range g.Race[r].Relations {
|
for o := range g.Race[r].Relations {
|
||||||
if g.Race[r].Relations[o].RaceID == opponentID {
|
switch {
|
||||||
|
case hostID == opponentID:
|
||||||
|
g.Race[r].Relations[o].Relation = rel
|
||||||
|
case g.Race[r].Relations[o].RaceID == opponentID:
|
||||||
g.Race[r].Relations[o].Relation = rel
|
g.Race[r].Relations[o].Relation = rel
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if hostID != opponentID {
|
||||||
return e.NewGameStateError("UpdateRelation: opponent not found")
|
return e.NewGameStateError("UpdateRelation: opponent not found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if hostID != opponentID {
|
||||||
return e.NewGameStateError("UpdateRelation: host %v not found", hostID)
|
return e.NewGameStateError("UpdateRelation: host %v not found", hostID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Game) Relation(hostRace, opponentRace string) (RaceRelation, error) {
|
||||||
|
hostID, err := g.hostRaceID(hostRace)
|
||||||
|
if err != nil {
|
||||||
|
return RaceRelation{}, err
|
||||||
|
}
|
||||||
|
opponentID, err := g.opponentRaceID(opponentRace)
|
||||||
|
if err != nil {
|
||||||
|
return RaceRelation{}, err
|
||||||
|
}
|
||||||
|
return g.relationInternal(hostID, opponentID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Game) relationInternal(hostID, opponentID uuid.UUID) (RaceRelation, error) {
|
||||||
|
for r := range g.Race {
|
||||||
|
if g.Race[r].ID == hostID {
|
||||||
|
for o := range g.Race[r].Relations {
|
||||||
|
if g.Race[r].Relations[o].RaceID == opponentID {
|
||||||
|
return g.Race[r].Relations[o], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RaceRelation{}, e.NewGameStateError("Relation: opponent not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RaceRelation{}, e.NewGameStateError("Relation: host %v not found", hostID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Game) MarshalBinary() (data []byte, err error) {
|
func (g Game) MarshalBinary() (data []byte, err error) {
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ type Race struct {
|
|||||||
type Relation string
|
type Relation string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RelationWar = "War"
|
RelationWar Relation = "War"
|
||||||
RelationPeace = "Peace"
|
RelationPeace Relation = "Peace"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RaceRelation struct {
|
type RaceRelation struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user