Break out packages
This commit is contained in:
parent
f91a975bf1
commit
d57e1e98e1
146
game.go
146
game.go
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
)
|
|
@ -1,7 +1,8 @@
|
||||||
package main
|
package gfx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"relay/game"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/faiface/pixel"
|
"github.com/faiface/pixel"
|
||||||
|
@ -10,24 +11,25 @@ import (
|
||||||
"golang.org/x/image/colornames"
|
"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)
|
renderBots(s, w, d, colors)
|
||||||
renderObstacles(s, w)
|
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()
|
b := w.Bounds()
|
||||||
im := imdraw.New(nil)
|
im := imdraw.New(nil)
|
||||||
|
|
||||||
for i, t := range s.teams {
|
for i, t := range s.Teams {
|
||||||
for j, bot := range t.bots {
|
for j, bot := range t.Bots {
|
||||||
if &t.bots[j] == t.baton.holder {
|
if &t.Bots[j] == t.Baton.Holder {
|
||||||
im.Color = pixel.RGB(0, 1, 0)
|
im.Color = pixel.RGB(0, 1, 0)
|
||||||
} else {
|
} 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)
|
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 {
|
func lanePos(pos, lane int, width float64, bounds pixel.Rect) pixel.Vec {
|
||||||
hOffset := bounds.Size().X / steps
|
hOffset := bounds.Size().X / game.Steps
|
||||||
vOffset := bounds.Size().Y / (numTeams + 1)
|
vOffset := bounds.Size().Y / (game.NumTeams + 1)
|
||||||
|
|
||||||
return pixel.V(bounds.Min.X+width/2+float64(pos)*hOffset,
|
return pixel.V(bounds.Min.X+width/2+float64(pos)*hOffset,
|
||||||
bounds.Min.Y+float64(lane+1)*vOffset)
|
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()
|
b := w.Bounds()
|
||||||
im := imdraw.New(nil)
|
im := imdraw.New(nil)
|
||||||
|
|
||||||
for _, o := range s.obstacles {
|
for _, o := range s.Obstacles {
|
||||||
im.Color = pixel.RGB(1, 0, 1)
|
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)
|
im.Push(pos)
|
||||||
|
|
||||||
|
@ -65,8 +67,8 @@ func renderObstacles(s state, w *pixelgl.Window) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func teamColors(ts []team) map[*team]pixel.RGBA {
|
func teamColors(ts []game.Team) map[*game.Team]pixel.RGBA {
|
||||||
m := make(map[*team]pixel.RGBA)
|
m := make(map[*game.Team]pixel.RGBA)
|
||||||
for i := range ts {
|
for i := range ts {
|
||||||
var c color.RGBA
|
var c color.RGBA
|
||||||
switch i {
|
switch i {
|
11
main.go
11
main.go
|
@ -2,6 +2,8 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"relay/game"
|
||||||
|
"relay/gfx"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/faiface/pixel"
|
"github.com/faiface/pixel"
|
||||||
|
@ -23,20 +25,19 @@ func run() {
|
||||||
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
s := newState()
|
s := game.NewState()
|
||||||
colors := teamColors(s.teams)
|
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
for !w.Closed() && !s.gameOver {
|
for !w.Closed() && !s.GameOver {
|
||||||
w.Clear(colornames.Peru)
|
w.Clear(colornames.Peru)
|
||||||
switch {
|
switch {
|
||||||
case w.JustPressed(pixelgl.KeyQ):
|
case w.JustPressed(pixelgl.KeyQ):
|
||||||
return
|
return
|
||||||
case w.JustPressed(pixelgl.KeySpace):
|
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()
|
w.Update()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue