diff --git a/game/commands.go b/game/commands.go index df470aa..b195d0e 100644 --- a/game/commands.go +++ b/game/commands.go @@ -13,7 +13,7 @@ const ( right ) -func doCommand(cmd command, s State, teamID int) State { +func doCommand(cmd command, s State, sOld State, teamID int) State { da := 1 da += rand.Intn(3) - 1 @@ -35,5 +35,5 @@ func doCommand(cmd command, s State, teamID int) State { b.Lane-- } - return updateBot(s, teamID, *b) + return updateBot(s, sOld, teamID, *b) } diff --git a/game/game.go b/game/game.go index dfc7417..100a190 100644 --- a/game/game.go +++ b/game/game.go @@ -1,8 +1,98 @@ package game -import ( - "log" -) +func UpdateState(s State, sOld State) State { + for i := range s.Teams { + s = doCommand(chooseCommand(s, i), s, sOld, i) + if b := activeBot(s.Teams[i]); b != nil { + s = moveBot(s, i, *b) + } + s = maybePassBaton(s, i) + } + + for _, t := range s.Teams { + if b := activeBot(t); b != nil && won(*b, s) { + s.GameOver = true + } + } + + return s +} + +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 { + if h.ID >= b.ID || h.Lane != b.Lane { + continue + } + if abs(b.Pos-h.Pos) <= passDistance { + h.v = 0 + h.a = 0 + s = updateBot(s, s, teamID, *h) + newH := t.Bots[i] + newH.a = baseAccel + t.Baton.HolderID = newH.ID + s = updateTeam(s, t) + return updateBot(s, 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, sOld State, teamID int, b Bot) State { + t := s.Teams[teamID] + for i, bb := range t.Bots { + if bb.ID == b.ID { + bots := append([]Bot{}, t.Bots[:i]...) + bots = append(bots, b) + bots = append(bots, t.Bots[i+1:]...) + t.Bots = bots + break + } + } + + s = updateTeam(s, t) + return s +} + +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 { + return b.Pos >= Steps +} + +func gameOver(s State) bool { + for _, t := range s.Teams { + if t.won { + return true + } + } + return false +} + +func abs(n int) int { + if n < 0 { + return -n + } + return n +} type State struct { Teams []Team @@ -10,6 +100,40 @@ type State struct { GameOver bool } +type Team struct { + id int + Bots []Bot + Baton Baton + won bool + 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 { + ID int + Lane int + Pos int + v int + a int +} + +type Baton struct { + HolderID int +} + +type Obstacle struct { + Lane int + Pos int +} + func NewState() State { var teams []Team for i := 0; i < NumTeams; i++ { @@ -53,124 +177,6 @@ func NewState() State { } } -type Team struct { - id int - Bots []Bot - Baton Baton - won bool - 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 { - ID int - Lane int - Pos int - v int - a int -} - -type Baton struct { - HolderID int -} - -type Obstacle struct { - Lane int - Pos int -} - -func UpdateState(s State) State { - for i, t := range s.Teams { - s = doCommand(chooseCommand(s, i), s, i) - if b := activeBot(t); b != nil { - s = moveBot(s, i, *b) - } - s = maybePassBaton(s, i) - } - - for _, t := range s.Teams { - if b := activeBot(t); b != nil && won(*b, s) { - s.GameOver = true - } - } - - return s -} - -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 { - if h.ID >= b.ID || h.Lane != b.Lane { - continue - } - if abs(b.Pos-h.Pos) <= passDistance { - log.Printf("team %v pass from %v to %v!", teamID, h.ID, b.ID) - h.v = 0 - h.a = 0 - s = updateBot(s, teamID, *h) - newH := t.Bots[i] - 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 { - return b.Pos >= Steps -} - -func gameOver(s State) bool { - for _, t := range s.Teams { - if t.won { - return true - } - } - return false -} - const ( Steps = 50 numBots = 5 @@ -179,10 +185,3 @@ const ( maxA = 3 maxV = 10 ) - -func abs(n int) int { - if n < 0 { - return -n - } - return n -} diff --git a/game/physics.go b/game/physics.go index 2f1478d..ed6da9d 100644 --- a/game/physics.go +++ b/game/physics.go @@ -26,7 +26,8 @@ func moveBot(s State, teamID int, b Bot) State { } } - return updateBot(s, teamID, b) + s = updateBot(s, s, teamID, b) + return s } func collide(pos, lane int, s State) bool { diff --git a/gfx/gfx.go b/gfx/gfx.go index 998defd..886cab4 100644 --- a/gfx/gfx.go +++ b/gfx/gfx.go @@ -17,32 +17,50 @@ type RenderState struct { frame int } -func Render(rs RenderState, s game.State, w *pixelgl.Window, d time.Duration) RenderState { - //tween := float64(rs.frame) / float64(rs.Frames) +func Render(rs RenderState, sOld, sNew game.State, w *pixelgl.Window, d time.Duration) RenderState { + //log.Println("render") + w.Clear(colornames.Peru) - colors := teamColors(s.Teams) - renderBots(s, w, d, colors) - renderObstacles(s, w) + tween := float64(rs.frame) / float64(rs.Frames) + + colors := teamColors(sNew.Teams) + renderBots(sOld, sNew, tween, w, d, colors) + renderObstacles(sNew, w) rs.frame++ + //log.Println("frame", rs.frame) if rs.frame >= rs.Frames { rs.Animating = false } return rs } -func renderBots(s game.State, w *pixelgl.Window, d time.Duration, colors map[*game.Team]pixel.RGBA) { +func renderBots(sOld, sNew game.State, tween float64, w *pixelgl.Window, d time.Duration, colors map[*game.Team]pixel.RGBA) { bounds := w.Bounds() im := imdraw.New(nil) - for i, t := range s.Teams { + //log.Println("sOld.Teams:", sOld.Teams) + + for i, t := range sNew.Teams { for j, bot := range t.Bots { - c := colors[&s.Teams[i]] + c := colors[&sNew.Teams[i]] c.R += 0.2 * float64(j) c.G -= 0.1 * float64(j) im.Color = c - pos := lanePos(bot.Pos, bot.Lane, botWidth, bounds) + oldBot := sOld.Teams[i].Bots[j] + // log.Println("oldBot:", oldBot) + // log.Println("bot:", bot) + oldPos := lanePos(oldBot.Pos, oldBot.Lane, botWidth, bounds) + newPos := lanePos(bot.Pos, bot.Lane, botWidth, bounds) + + // log.Println("oldPos:", oldPos) + // log.Println("newPos:", newPos) + + pos := pixel.Vec{ + X: oldPos.X + tween*(newPos.X-oldPos.X), + Y: oldPos.Y + tween*(newPos.Y-oldPos.Y), + } im.Push(pos) @@ -53,6 +71,7 @@ func renderBots(s game.State, w *pixelgl.Window, d time.Duration, colors map[*ga if t.Bots[j].ID == t.Baton.HolderID { renderBaton(pos, w) } + //log.Println("sOld.Teams[i].Bots[j]:", sOld.Teams[i].Bots[j]) } } } diff --git a/main.go b/main.go index a0ac06f..501fa84 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "log" "math/rand" "relay/game" "relay/gfx" @@ -29,23 +30,26 @@ func run() { start := time.Now() + w.Clear(colornames.Peachpuff) for !w.Closed() && !s.GameOver { - w.Clear(colornames.Peru) + sOld := s - //sOld := s + rs := gfx.RenderState{ + Animating: false, + Frames: 3, + } switch { case w.JustPressed(pixelgl.KeyQ): return case w.JustPressed(pixelgl.KeySpace): - s = game.UpdateState(s) - } - rs := gfx.RenderState{ - Animating: true, - Frames: 10, + rs.Animating = true + s = game.UpdateState(s, sOld) } for rs.Animating { - rs = gfx.Render(rs, s, w, time.Since(start)) + log.Println("anim loop") + rs = gfx.Render(rs, sOld, s, w, time.Since(start)) + w.Update() } w.Update() }