Handle game state immutably
This commit is contained in:
parent
54398b4e17
commit
f62e9833db
|
@ -2,8 +2,9 @@ package game
|
||||||
|
|
||||||
import "log"
|
import "log"
|
||||||
|
|
||||||
func chooseCommand(t Team, s State) command {
|
func chooseCommand(s State, teamID int) command {
|
||||||
h := t.Baton.Holder
|
t := s.Teams[teamID]
|
||||||
|
h := t.BatonHolder()
|
||||||
if collide(h.Pos+1, h.Lane, s) {
|
if collide(h.Pos+1, h.Lane, s) {
|
||||||
if h.Lane <= t.Lane {
|
if h.Lane <= t.Lane {
|
||||||
return left
|
return left
|
||||||
|
@ -13,7 +14,7 @@ func chooseCommand(t Team, s State) command {
|
||||||
|
|
||||||
var nextBot *Bot
|
var nextBot *Bot
|
||||||
for i, b := range t.Bots {
|
for i, b := range t.Bots {
|
||||||
if b.id == h.id+1 {
|
if b.ID == h.ID+1 {
|
||||||
nextBot = &t.Bots[i]
|
nextBot = &t.Bots[i]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,8 @@
|
||||||
package game
|
package game
|
||||||
|
|
||||||
import "math/rand"
|
import (
|
||||||
|
"math/rand"
|
||||||
func doCommand(cmd command, b *Bot) {
|
)
|
||||||
da := 1
|
|
||||||
da += rand.Intn(3) - 1
|
|
||||||
|
|
||||||
switch cmd {
|
|
||||||
case speedUp:
|
|
||||||
b.a += da
|
|
||||||
accelerate(b)
|
|
||||||
case slowDown:
|
|
||||||
b.a -= da
|
|
||||||
accelerate(b)
|
|
||||||
case left:
|
|
||||||
b.Lane++
|
|
||||||
case right:
|
|
||||||
b.Lane--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type command int
|
type command int
|
||||||
|
|
||||||
|
@ -28,3 +12,28 @@ const (
|
||||||
left
|
left
|
||||||
right
|
right
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func doCommand(cmd command, s State, teamID int) State {
|
||||||
|
da := 1
|
||||||
|
da += rand.Intn(3) - 1
|
||||||
|
|
||||||
|
b := activeBot(s.Teams[teamID])
|
||||||
|
if b == nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
switch cmd {
|
||||||
|
case speedUp:
|
||||||
|
b.a += da
|
||||||
|
*b = accelerate(*b)
|
||||||
|
case slowDown:
|
||||||
|
b.a -= da
|
||||||
|
*b = accelerate(*b)
|
||||||
|
case left:
|
||||||
|
b.Lane++
|
||||||
|
case right:
|
||||||
|
b.Lane--
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateBot(s, teamID, *b)
|
||||||
|
}
|
||||||
|
|
85
game/game.go
85
game/game.go
|
@ -16,7 +16,7 @@ func NewState() State {
|
||||||
var bots []Bot
|
var bots []Bot
|
||||||
for j := 0; j < numBots; j++ {
|
for j := 0; j < numBots; j++ {
|
||||||
b := Bot{
|
b := Bot{
|
||||||
id: i*NumTeams + j,
|
ID: i*NumTeams + j,
|
||||||
Lane: i,
|
Lane: i,
|
||||||
Pos: j * (Steps / numBots),
|
Pos: j * (Steps / numBots),
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ func NewState() State {
|
||||||
teams = append(teams, Team{
|
teams = append(teams, Team{
|
||||||
id: i,
|
id: i,
|
||||||
Bots: bots,
|
Bots: bots,
|
||||||
Baton: Baton{Holder: &bots[0]},
|
Baton: Baton{HolderID: i * NumTeams},
|
||||||
Lane: i,
|
Lane: i,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -61,8 +61,17 @@ type Team struct {
|
||||||
Lane int
|
Lane int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t Team) BatonHolder() *Bot {
|
||||||
|
for _, b := range t.Bots {
|
||||||
|
if b.ID == t.Baton.HolderID {
|
||||||
|
return &b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Bot struct {
|
type Bot struct {
|
||||||
id int
|
ID int
|
||||||
Lane int
|
Lane int
|
||||||
Pos int
|
Pos int
|
||||||
v int
|
v int
|
||||||
|
@ -70,7 +79,7 @@ type Bot struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Baton struct {
|
type Baton struct {
|
||||||
Holder *Bot
|
HolderID int
|
||||||
}
|
}
|
||||||
|
|
||||||
type Obstacle struct {
|
type Obstacle struct {
|
||||||
|
@ -78,17 +87,17 @@ type Obstacle struct {
|
||||||
Pos int
|
Pos int
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateState(sOld State) State {
|
func UpdateState(s State) State {
|
||||||
s := sOld
|
|
||||||
|
|
||||||
for i, t := range s.Teams {
|
for i, t := range s.Teams {
|
||||||
doCommand(chooseCommand(t, sOld), t.Baton.Holder)
|
s = doCommand(chooseCommand(s, i), s, i)
|
||||||
moveBot(t.Baton.Holder, sOld)
|
if b := activeBot(t); b != nil {
|
||||||
maybePassBaton(&s.Teams[i])
|
s = moveBot(s, i, *b)
|
||||||
|
}
|
||||||
|
s = maybePassBaton(s, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range s.Teams {
|
for _, t := range s.Teams {
|
||||||
if won(*t.Baton.Holder, s) {
|
if b := activeBot(t); b != nil && won(*b, s) {
|
||||||
s.GameOver = true
|
s.GameOver = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,21 +105,57 @@ func UpdateState(sOld State) State {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func maybePassBaton(t *Team) {
|
func maybePassBaton(s State, teamID int) State {
|
||||||
|
t := s.Teams[teamID]
|
||||||
|
h := activeBot(t)
|
||||||
|
if h == nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
for i, b := range t.Bots {
|
for i, b := range t.Bots {
|
||||||
h := t.Baton.Holder
|
if h.ID >= b.ID || h.Lane != b.Lane {
|
||||||
if h.id >= b.id || h.Lane != b.Lane {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if abs(b.Pos-h.Pos) <= passDistance {
|
if abs(b.Pos-h.Pos) <= passDistance {
|
||||||
log.Printf("team %v pass from %v to %v!", t.id, h.id, b.id)
|
log.Printf("team %v pass from %v to %v!", teamID, h.ID, b.ID)
|
||||||
t.Baton.Holder.v = 0
|
h.v = 0
|
||||||
t.Baton.Holder.a = 0
|
h.a = 0
|
||||||
t.Baton.Holder = &t.Bots[i]
|
s = updateBot(s, teamID, *h)
|
||||||
t.Bots[i].a = baseAccel
|
newH := t.Bots[i]
|
||||||
return
|
newH.a = baseAccel
|
||||||
|
t.Baton.HolderID = newH.ID
|
||||||
|
s = updateTeam(s, t)
|
||||||
|
return updateBot(s, teamID, newH)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func activeBot(t Team) *Bot {
|
||||||
|
for _, b := range t.Bots {
|
||||||
|
if b.ID == t.Baton.HolderID {
|
||||||
|
return &b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateBot(s State, teamID int, b Bot) State {
|
||||||
|
t := s.Teams[teamID]
|
||||||
|
for i, bb := range t.Bots {
|
||||||
|
if bb.ID == b.ID {
|
||||||
|
t.Bots = append(t.Bots[:i], append([]Bot{b}, t.Bots[i+1:]...)...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateTeam(s, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateTeam(s State, t Team) State {
|
||||||
|
s.Teams = append(s.Teams[:t.id], append([]Team{t}, s.Teams[t.id+1:]...)...)
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func won(b Bot, s State) bool {
|
func won(b Bot, s State) bool {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package game
|
package game
|
||||||
|
|
||||||
func accelerate(b *Bot) {
|
func accelerate(b Bot) Bot {
|
||||||
if b.a < -maxA {
|
if b.a < -maxA {
|
||||||
b.a = -maxA
|
b.a = -maxA
|
||||||
}
|
}
|
||||||
|
@ -15,14 +15,18 @@ func accelerate(b *Bot) {
|
||||||
if b.v < -maxV {
|
if b.v < -maxV {
|
||||||
b.v = -maxV
|
b.v = -maxV
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func moveBot(b *Bot, s State) {
|
func moveBot(s State, teamID int, b Bot) State {
|
||||||
for i := 0; i < b.v; i++ {
|
for i := 0; i < b.v; i++ {
|
||||||
if !collide(b.Pos+1, b.Lane, s) {
|
if !collide(b.Pos+1, b.Lane, s) {
|
||||||
b.Pos++
|
b.Pos++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return updateBot(s, teamID, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func collide(pos, lane int, s State) bool {
|
func collide(pos, lane int, s State) bool {
|
||||||
|
|
|
@ -50,7 +50,7 @@ func renderBots(s game.State, w *pixelgl.Window, d time.Duration, colors map[*ga
|
||||||
im.Circle(botWidth, 0)
|
im.Circle(botWidth, 0)
|
||||||
|
|
||||||
im.Draw(w)
|
im.Draw(w)
|
||||||
if &t.Bots[j] == t.Baton.Holder {
|
if t.Bots[j].ID == t.Baton.HolderID {
|
||||||
renderBaton(pos, w)
|
renderBaton(pos, w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue