initial code commit
This commit is contained in:
parent
40fff01888
commit
60cd422423
15
go.mod
Normal file
15
go.mod
Normal file
@ -0,0 +1,15 @@
|
||||
module code.betamike.com/worm
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be // indirect
|
||||
github.com/hajimehoshi/ebiten/v2 v2.2.2 // indirect
|
||||
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 // indirect
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
|
||||
golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
)
|
69
go.sum
Normal file
69
go.sum
Normal file
@ -0,0 +1,69 @@
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be h1:vEIVIuBApEBQTEJt19GfhoU+zFSV+sNTa9E9FdnRYfk=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/hajimehoshi/bitmapfont/v2 v2.1.3/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.2.2 h1:92E+ogdNyH1P/LlvMQ7vonbFDh6bl+O7Ak+H1HX0RX8=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.2.2/go.mod h1:olKl/qqhMBBAm2oI7Zy292nCtE+nitlmYKNF3UpbFn0=
|
||||
github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
||||
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.2/go.mod h1:rUKQmwMkqmRxe+IAof9+tuYA2ofm8cAWXFmSfzDN8vQ=
|
||||
github.com/jakecoffman/cp v1.1.0/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg=
|
||||
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 h1:dy+DS31tGEGCsZzB45HmJJNHjur8GDgtRNX9U7HnSX4=
|
||||
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240/go.mod h1:3P4UH/k22rXyHIJD2w4h2XMqPX4Of/eySEZq9L6wqc4=
|
||||
github.com/jfreymuth/oggvorbis v1.0.3/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII=
|
||||
github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5 h1:peBP2oZO/xVnGMaWMCyFEI0WENsGj71wx5K12mRELHQ=
|
||||
golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5/go.mod h1:c4YKU3ZylDmvbw+H/PSvm42vhdWbuxCzbonauEAP9B8=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 h1:J27LZFQBFoihqXoegpscI10HpjZ7B5WQLLKL2FZXQKw=
|
||||
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
408
main.go
Normal file
408
main.go
Normal file
@ -0,0 +1,408 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"log"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/opentype"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
"github.com/hajimehoshi/ebiten/v2/text"
|
||||
)
|
||||
|
||||
var (
|
||||
mplusNormalFont font.Face
|
||||
)
|
||||
|
||||
func init() {
|
||||
// TODO: don't use the ebiten example code font
|
||||
tt, err := opentype.Parse(fonts.MPlus1pRegular_ttf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
const dpi = 72
|
||||
mplusNormalFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: 24,
|
||||
DPI: dpi,
|
||||
Hinting: font.HintingFull,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
rand.Seed(time.Now().UnixMicro())
|
||||
}
|
||||
|
||||
type Kind uint8
|
||||
|
||||
const (
|
||||
Empty Kind = iota
|
||||
Worm
|
||||
Food
|
||||
)
|
||||
|
||||
func (k Kind) String() string {
|
||||
switch k {
|
||||
case Empty:
|
||||
return "Empty"
|
||||
case Worm:
|
||||
return "Worm"
|
||||
case Food:
|
||||
return "Food"
|
||||
default:
|
||||
return "<unknown>"
|
||||
}
|
||||
}
|
||||
|
||||
type Direction uint8
|
||||
|
||||
const (
|
||||
None Direction = iota
|
||||
North
|
||||
South
|
||||
East
|
||||
West
|
||||
)
|
||||
|
||||
func (d Direction) String() string {
|
||||
switch d {
|
||||
case None:
|
||||
return "None"
|
||||
case North:
|
||||
return "North"
|
||||
case South:
|
||||
return "South"
|
||||
case East:
|
||||
return "East"
|
||||
case West:
|
||||
return "West"
|
||||
default:
|
||||
return "<unknown>"
|
||||
}
|
||||
}
|
||||
|
||||
type Tile struct {
|
||||
kind Kind
|
||||
dir Direction
|
||||
}
|
||||
|
||||
func (t Tile) String() string {
|
||||
return fmt.Sprintf("%s -- %s", t.kind, t.dir)
|
||||
}
|
||||
|
||||
var EmptyTile = Tile{kind: Empty, dir: None}
|
||||
var FoodTile = Tile{kind: Food, dir: None}
|
||||
|
||||
type point struct {
|
||||
x, y int
|
||||
}
|
||||
|
||||
type WormGame struct {
|
||||
screenWidth, screenHeight int
|
||||
width, height int
|
||||
|
||||
initLength int
|
||||
score int
|
||||
won, lost bool
|
||||
|
||||
board [][]Tile
|
||||
|
||||
head point
|
||||
tail point
|
||||
|
||||
lastMove time.Time
|
||||
speed time.Duration
|
||||
}
|
||||
|
||||
func NewGame(screenWidth, screenHeight int, initLength int, speed time.Duration) *WormGame {
|
||||
w := &WormGame{
|
||||
screenWidth: screenWidth,
|
||||
screenHeight: screenHeight,
|
||||
width: screenWidth / 4,
|
||||
height: screenHeight / 4,
|
||||
initLength: initLength,
|
||||
speed: speed,
|
||||
}
|
||||
w.Reset()
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *WormGame) Reset() {
|
||||
w.lost = false
|
||||
w.score = 0
|
||||
w.board = make([][]Tile, w.width)
|
||||
for idx := range w.board {
|
||||
w.board[idx] = make([]Tile, w.height)
|
||||
}
|
||||
|
||||
w.head = point{
|
||||
x: w.width / 2,
|
||||
y: w.height / 2,
|
||||
}
|
||||
|
||||
w.Set(w.head, Worm, East)
|
||||
for idx := 1; idx < w.initLength; idx++ {
|
||||
w.board[w.head.x-idx][w.head.y] = Tile{kind: Worm, dir: East}
|
||||
}
|
||||
w.tail = point{w.head.x - w.initLength + 1, w.head.y}
|
||||
w.RandomizeFood()
|
||||
w.lastMove = time.Now()
|
||||
}
|
||||
|
||||
func (w *WormGame) randomFreeSpace() *point {
|
||||
checked := map[point]struct{}{}
|
||||
for len(checked) <= w.width*w.height {
|
||||
x := rand.Intn(w.width)
|
||||
y := rand.Intn(w.height)
|
||||
p := point{x, y}
|
||||
if _, ok := checked[p]; ok {
|
||||
continue
|
||||
}
|
||||
checked[p] = struct{}{}
|
||||
if w.board[x][y].kind == Empty {
|
||||
return &p
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WormGame) RandomizeFood() {
|
||||
p := w.randomFreeSpace()
|
||||
if p == nil {
|
||||
w.won = true
|
||||
return
|
||||
}
|
||||
w.board[p.x][p.y] = FoodTile
|
||||
}
|
||||
|
||||
func (w *WormGame) InBounds(p point) bool {
|
||||
return p.x < w.width && p.x >= 0 && p.y < w.height && p.y >= 0
|
||||
}
|
||||
|
||||
func (w *WormGame) MustInBounds(p point) {
|
||||
if !w.InBounds(p) {
|
||||
panic("out of bounds!")
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WormGame) Get(p point) Tile {
|
||||
w.MustInBounds(p)
|
||||
return w.board[p.x][p.y]
|
||||
}
|
||||
|
||||
func (w *WormGame) Set(p point, kind Kind, dir Direction) {
|
||||
w.MustInBounds(p)
|
||||
w.board[p.x][p.y] = Tile{kind: kind, dir: dir}
|
||||
}
|
||||
func (w *WormGame) SetEmpty(p point) {
|
||||
w.MustInBounds(p)
|
||||
w.board[p.x][p.y] = EmptyTile
|
||||
}
|
||||
|
||||
func (w *WormGame) AdvanceWorm() {
|
||||
dir := w.Get(w.head).dir
|
||||
prev := w.head
|
||||
|
||||
switch dir {
|
||||
case North:
|
||||
if w.head.y-1 > w.height {
|
||||
panic("out of bounds")
|
||||
}
|
||||
w.head.y -= 1
|
||||
case South:
|
||||
if w.head.y+1 < 0 {
|
||||
panic("out of bounds")
|
||||
}
|
||||
w.head.y += 1
|
||||
case East:
|
||||
if w.head.x+1 > w.width {
|
||||
panic("out of bounds")
|
||||
}
|
||||
w.head.x += 1
|
||||
case West:
|
||||
if w.head.x-1 < 0 {
|
||||
panic("out of bounds")
|
||||
}
|
||||
w.head.x -= 1
|
||||
}
|
||||
|
||||
// change direction of prev worm segment
|
||||
w.Set(prev, Worm, dir)
|
||||
// add new worm segment
|
||||
w.Set(w.head, Worm, dir)
|
||||
|
||||
tailDir := w.Get(w.tail).dir
|
||||
// shorten tail
|
||||
w.SetEmpty(w.tail)
|
||||
|
||||
// update tail pointer
|
||||
switch tailDir {
|
||||
case North:
|
||||
w.tail = point{w.tail.x, w.tail.y - 1}
|
||||
case South:
|
||||
w.tail = point{w.tail.x, w.tail.y + 1}
|
||||
case East:
|
||||
w.tail = point{w.tail.x + 1, w.tail.y}
|
||||
case West:
|
||||
w.tail = point{w.tail.x - 1, w.tail.y}
|
||||
}
|
||||
|
||||
// update laste move ts
|
||||
w.lastMove = time.Now()
|
||||
}
|
||||
|
||||
func (w *WormGame) EmbiggenWorm() {
|
||||
tailDir := w.Get(w.tail).dir
|
||||
|
||||
// update tail pointer
|
||||
switch tailDir {
|
||||
case North:
|
||||
w.tail = point{w.tail.x, w.tail.y + 1}
|
||||
case South:
|
||||
w.tail = point{w.tail.x, w.tail.y - 1}
|
||||
case East:
|
||||
w.tail = point{w.tail.x - 1, w.tail.y}
|
||||
case West:
|
||||
w.tail = point{w.tail.x + 1, w.tail.y}
|
||||
}
|
||||
|
||||
w.Set(w.tail, Worm, tailDir)
|
||||
}
|
||||
|
||||
func (w *WormGame) Update() error {
|
||||
if w.won || w.lost {
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeySpace) {
|
||||
w.Reset()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
up := inpututil.IsKeyJustPressed(ebiten.KeyArrowUp)
|
||||
down := inpututil.IsKeyJustPressed(ebiten.KeyArrowDown)
|
||||
left := inpututil.IsKeyJustPressed(ebiten.KeyArrowLeft)
|
||||
right := inpututil.IsKeyJustPressed(ebiten.KeyArrowRight)
|
||||
|
||||
// cancel out simultaneous up and down
|
||||
if up && down {
|
||||
up = false
|
||||
down = false
|
||||
}
|
||||
// cancel up simultaneous left and right,
|
||||
// also up/down take priority over left/right
|
||||
if (left && right) || (up || down) {
|
||||
left = false
|
||||
right = false
|
||||
}
|
||||
|
||||
changedDirection := true
|
||||
headTile := w.Get(w.head)
|
||||
|
||||
if up && headTile.dir != South {
|
||||
headTile.dir = North
|
||||
} else if down && headTile.dir != North {
|
||||
headTile.dir = South
|
||||
} else if left && headTile.dir != East {
|
||||
headTile.dir = West
|
||||
} else if right && headTile.dir != West {
|
||||
headTile.dir = East
|
||||
} else {
|
||||
changedDirection = false
|
||||
}
|
||||
w.Set(w.head, headTile.kind, headTile.dir)
|
||||
|
||||
var next point
|
||||
switch headTile.dir {
|
||||
case North:
|
||||
next = point{w.head.x, w.head.y - 1}
|
||||
case South:
|
||||
next = point{w.head.x, w.head.y + 1}
|
||||
case East:
|
||||
next = point{w.head.x + 1, w.head.y}
|
||||
case West:
|
||||
next = point{w.head.x - 1, w.head.y}
|
||||
}
|
||||
|
||||
// don't evaluate space or advance if we didn't change direction and haven't reached timeout
|
||||
if !changedDirection && time.Now().Sub(w.lastMove) < w.speed {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ate bool
|
||||
if next.x >= w.width || next.y >= w.height || next.x < 0 || next.y < 0 {
|
||||
w.lost = true
|
||||
} else {
|
||||
tile := w.board[next.x][next.y]
|
||||
ate = (tile.kind == Food)
|
||||
w.lost = (tile.kind == Worm)
|
||||
}
|
||||
|
||||
if w.lost {
|
||||
return nil
|
||||
}
|
||||
|
||||
if ate {
|
||||
w.score += 1
|
||||
w.EmbiggenWorm()
|
||||
w.RandomizeFood()
|
||||
}
|
||||
|
||||
// check if we won (all spaces are worm) and skip moving (since it would be
|
||||
// an auto-loss) this is probably close to impossible?
|
||||
if w.won {
|
||||
return nil
|
||||
}
|
||||
|
||||
w.AdvanceWorm()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WormGame) Draw(screen *ebiten.Image) {
|
||||
screen.Clear()
|
||||
for col := range w.board {
|
||||
for row := range w.board[col] {
|
||||
x := float64(col * 4)
|
||||
y := float64(row * 4)
|
||||
switch w.board[col][row].kind {
|
||||
case Empty:
|
||||
ebitenutil.DrawRect(screen, x, y, 4, 4, color.Black)
|
||||
case Worm:
|
||||
ebitenutil.DrawRect(screen, x, y, 4, 4, color.White)
|
||||
case Food:
|
||||
ebitenutil.DrawRect(screen, x, y, 4, 4, color.White)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if w.won {
|
||||
text.Draw(screen, fmt.Sprintf("You Win! Score: %d", w.score), mplusNormalFont, 20, w.screenHeight/2, color.White)
|
||||
return
|
||||
} else if w.lost {
|
||||
text.Draw(screen, fmt.Sprintf("Game Over. Score: %d", w.score), mplusNormalFont, 20, w.screenHeight/2, color.White)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WormGame) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||
return w.screenWidth, w.screenHeight
|
||||
}
|
||||
|
||||
const (
|
||||
screenWidth = 320
|
||||
screenHeight = 240
|
||||
)
|
||||
|
||||
func main() {
|
||||
game := NewGame(screenWidth, screenHeight, 4, 75*time.Millisecond)
|
||||
ebiten.SetWindowSize(screenWidth*2, screenHeight*2)
|
||||
ebiten.SetWindowTitle("Worm")
|
||||
if err := ebiten.RunGame(game); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user