diff --git a/game.go b/game.go deleted file mode 100644 index 7445c04..0000000 --- a/game.go +++ /dev/null @@ -1,146 +0,0 @@ -package main - -import ( - "log" - "math/rand" -) - -type state struct { - teams []team - obstacles []obstacle - gameOver bool -} - -func newState() state { - var teams []team - for i := 0; i < numTeams; i++ { - var bots []bot - for j := 0; j < numBots; j++ { - b := bot{ - id: i*numTeams + j, - pos: j * (steps / numBots), - } - bots = append(bots, b) - } - teams = append(teams, team{ - bots: bots, - baton: baton{holder: &bots[0]}, - }) - } - - return state{ - teams: teams, - obstacles: []obstacle{ - { - lane: 0, - pos: steps / 3, - }, - }, - } -} - -type team struct { - bots []bot - baton baton - won bool -} - -type bot struct { - id int - pos int - v int - a int -} - -type baton struct { - holder *bot -} - -type obstacle struct { - lane int - pos int -} - -func updateState(sOld state) state { - s := sOld - - for i, t := range s.teams { - moveBot(t.baton.holder) - maybePassBaton(&s.teams[i]) - } - - for _, t := range s.teams { - if won(*t.baton.holder, s) { - s.gameOver = true - } - } - - return s -} - -func moveBot(b *bot) { - if b.a == 0 { - b.a = 1 - } - b.a += rand.Intn(3) - 1 - if b.a < -maxA { - b.a = -maxA - } - if b.a > maxA { - b.a = maxA - } - - b.v += b.a - if b.v > maxV { - b.v = maxV - } - if b.v < -maxV { - b.v = -maxV - } - b.pos += b.v -} - -func maybePassBaton(t *team) { - for i, b := range t.bots { - h := t.baton.holder - if h.id >= b.id { - continue - } - if abs(b.pos-h.pos) <= 10 { - log.Printf("pass from %v to %v!", h.id, b.id) - t.baton.holder.v = 0 - t.baton.holder.a = 0 - t.baton.holder = &t.bots[i] - t.bots[i].a = 10 - return - } - } -} - -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 = 400 - numBots = 10 - numTeams = 2 - maxA = 3 - maxV = 20 -) - -func abs(n int) int { - if n < 0 { - return -n - } - return n -} diff --git a/game/game.go b/game/game.go new file mode 100644 index 0000000..027d2e0 --- /dev/null +++ b/game/game.go @@ -0,0 +1,123 @@ +package game + +import ( + "log" +) + +type State struct { + Teams []Team + Obstacles []Obstacle + GameOver bool +} + +func NewState() State { + var teams []Team + for i := 0; i < NumTeams; i++ { + var bots []Bot + for j := 0; j < numBots; j++ { + b := Bot{ + id: i*NumTeams + j, + Pos: j * (Steps / numBots), + } + bots = append(bots, b) + } + teams = append(teams, Team{ + Bots: bots, + Baton: Baton{Holder: &bots[0]}, + }) + } + + return State{ + Teams: teams, + Obstacles: []Obstacle{ + { + Lane: 0, + Pos: Steps / 3, + }, + }, + } +} + +type Team struct { + Bots []Bot + Baton Baton + won bool +} + +type Bot struct { + id int + Pos int + v int + a int +} + +type Baton struct { + Holder *Bot +} + +type Obstacle struct { + Lane int + Pos int +} + +func UpdateState(sOld State) State { + s := sOld + + for i, t := range s.Teams { + moveBot(t.Baton.Holder) + maybePassBaton(&s.Teams[i]) + } + + for _, t := range s.Teams { + if won(*t.Baton.Holder, s) { + s.GameOver = true + } + } + + return s +} + +func maybePassBaton(t *Team) { + for i, b := range t.Bots { + h := t.Baton.Holder + if h.id >= b.id { + continue + } + if abs(b.Pos-h.Pos) <= passDistance { + log.Printf("pass from %v to %v!", h.id, b.id) + t.Baton.Holder.v = 0 + t.Baton.Holder.a = 0 + t.Baton.Holder = &t.Bots[i] + t.Bots[i].a = baseAccel + return + } + } +} + +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 = 400 + numBots = 10 + NumTeams = 2 + maxA = 3 + maxV = 20 +) + +func abs(n int) int { + if n < 0 { + return -n + } + return n +} diff --git a/game/physics.go b/game/physics.go new file mode 100644 index 0000000..e178bc5 --- /dev/null +++ b/game/physics.go @@ -0,0 +1,30 @@ +package game + +import "math/rand" + +func moveBot(b *Bot) { + if b.a == 0 { + b.a = 1 + } + b.a += rand.Intn(3) - 1 + if b.a < -maxA { + b.a = -maxA + } + if b.a > maxA { + b.a = maxA + } + + b.v += b.a + if b.v > maxV { + b.v = maxV + } + if b.v < -maxV { + b.v = -maxV + } + b.Pos += b.v +} + +const ( + passDistance = 10 + baseAccel = 10 +) diff --git a/render.go b/gfx/gfx.go similarity index 58% rename from render.go rename to gfx/gfx.go index 88858e2..347a724 100644 --- a/render.go +++ b/gfx/gfx.go @@ -1,7 +1,8 @@ -package main +package gfx import ( "image/color" + "relay/game" "time" "github.com/faiface/pixel" @@ -10,24 +11,25 @@ import ( "golang.org/x/image/colornames" ) -func render(s state, w *pixelgl.Window, d time.Duration, colors map[*team]pixel.RGBA) { +func Render(s game.State, w *pixelgl.Window, d time.Duration) { + colors := teamColors(s.Teams) renderBots(s, w, d, colors) renderObstacles(s, w) } -func renderBots(s state, w *pixelgl.Window, d time.Duration, colors map[*team]pixel.RGBA) { +func renderBots(s game.State, w *pixelgl.Window, d time.Duration, colors map[*game.Team]pixel.RGBA) { b := w.Bounds() im := imdraw.New(nil) - for i, t := range s.teams { - for j, bot := range t.bots { - if &t.bots[j] == t.baton.holder { + for i, t := range s.Teams { + for j, bot := range t.Bots { + if &t.Bots[j] == t.Baton.Holder { im.Color = pixel.RGB(0, 1, 0) } else { - im.Color = colors[&s.teams[i]] + im.Color = colors[&s.Teams[i]] } - pos := lanePos(bot.pos, i, botWidth, b) + pos := lanePos(bot.Pos, i, botWidth, b) im.Push(pos) @@ -40,21 +42,21 @@ func renderBots(s state, w *pixelgl.Window, d time.Duration, colors map[*team]pi } func lanePos(pos, lane int, width float64, bounds pixel.Rect) pixel.Vec { - hOffset := bounds.Size().X / steps - vOffset := bounds.Size().Y / (numTeams + 1) + hOffset := bounds.Size().X / game.Steps + vOffset := bounds.Size().Y / (game.NumTeams + 1) return pixel.V(bounds.Min.X+width/2+float64(pos)*hOffset, bounds.Min.Y+float64(lane+1)*vOffset) } -func renderObstacles(s state, w *pixelgl.Window) { +func renderObstacles(s game.State, w *pixelgl.Window) { b := w.Bounds() im := imdraw.New(nil) - for _, o := range s.obstacles { + for _, o := range s.Obstacles { im.Color = pixel.RGB(1, 0, 1) - pos := lanePos(o.pos, o.lane, botWidth, b) + pos := lanePos(o.Pos, o.Lane, botWidth, b) im.Push(pos) @@ -65,8 +67,8 @@ func renderObstacles(s state, w *pixelgl.Window) { } } -func teamColors(ts []team) map[*team]pixel.RGBA { - m := make(map[*team]pixel.RGBA) +func teamColors(ts []game.Team) map[*game.Team]pixel.RGBA { + m := make(map[*game.Team]pixel.RGBA) for i := range ts { var c color.RGBA switch i { diff --git a/main.go b/main.go index 68b1c57..c94a988 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,8 @@ package main import ( "math/rand" + "relay/game" + "relay/gfx" "time" "github.com/faiface/pixel" @@ -23,20 +25,19 @@ func run() { rand.Seed(time.Now().UnixNano()) - s := newState() - colors := teamColors(s.teams) + s := game.NewState() start := time.Now() - for !w.Closed() && !s.gameOver { + for !w.Closed() && !s.GameOver { w.Clear(colornames.Peru) switch { case w.JustPressed(pixelgl.KeyQ): return case w.JustPressed(pixelgl.KeySpace): - s = updateState(s) + s = game.UpdateState(s) } - render(s, w, time.Since(start), colors) + gfx.Render(s, w, time.Since(start)) w.Update() } }