Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
235
Task/Snake/Go/snake.go
Normal file
235
Task/Snake/Go/snake.go
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
termbox "github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
score, err := playSnake()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("Final score:", score)
|
||||
}
|
||||
|
||||
type snake struct {
|
||||
body []position // tail to head positions of the snake
|
||||
heading direction
|
||||
width, height int
|
||||
cells []termbox.Cell
|
||||
}
|
||||
|
||||
type position struct {
|
||||
X int
|
||||
Y int
|
||||
}
|
||||
|
||||
type direction int
|
||||
|
||||
const (
|
||||
North direction = iota
|
||||
East
|
||||
South
|
||||
West
|
||||
)
|
||||
|
||||
func (p position) next(d direction) position {
|
||||
switch d {
|
||||
case North:
|
||||
p.Y--
|
||||
case East:
|
||||
p.X++
|
||||
case South:
|
||||
p.Y++
|
||||
case West:
|
||||
p.X--
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func playSnake() (int, error) {
|
||||
err := termbox.Init()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer termbox.Close()
|
||||
|
||||
termbox.Clear(fg, bg)
|
||||
termbox.HideCursor()
|
||||
s := &snake{
|
||||
// It would be more efficient to use a circular
|
||||
// buffer instead of a plain slice for s.body.
|
||||
body: make([]position, 0, 32),
|
||||
cells: termbox.CellBuffer(),
|
||||
}
|
||||
s.width, s.height = termbox.Size()
|
||||
s.drawBorder()
|
||||
s.startSnake()
|
||||
s.placeFood()
|
||||
s.flush()
|
||||
|
||||
moveCh, errCh := s.startEventLoop()
|
||||
const delay = 125 * time.Millisecond
|
||||
for t := time.NewTimer(delay); ; t.Reset(delay) {
|
||||
var move direction
|
||||
select {
|
||||
case err = <-errCh:
|
||||
return len(s.body), err
|
||||
case move = <-moveCh:
|
||||
if !t.Stop() {
|
||||
<-t.C // handles race between moveCh and t.C
|
||||
}
|
||||
case <-t.C:
|
||||
move = s.heading
|
||||
}
|
||||
if s.doMove(move) {
|
||||
time.Sleep(1 * time.Second)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return len(s.body), err
|
||||
}
|
||||
|
||||
func (s *snake) startEventLoop() (<-chan direction, <-chan error) {
|
||||
moveCh := make(chan direction)
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer close(errCh)
|
||||
for {
|
||||
switch ev := termbox.PollEvent(); ev.Type {
|
||||
case termbox.EventKey:
|
||||
switch ev.Ch { // WSAD and HJKL movement
|
||||
case 'w', 'W', 'k', 'K':
|
||||
moveCh <- North
|
||||
case 'a', 'A', 'h', 'H':
|
||||
moveCh <- West
|
||||
case 's', 'S', 'j', 'J':
|
||||
moveCh <- South
|
||||
case 'd', 'D', 'l', 'L':
|
||||
moveCh <- East
|
||||
case 0:
|
||||
switch ev.Key { // Cursor key movement
|
||||
case termbox.KeyArrowUp:
|
||||
moveCh <- North
|
||||
case termbox.KeyArrowDown:
|
||||
moveCh <- South
|
||||
case termbox.KeyArrowLeft:
|
||||
moveCh <- West
|
||||
case termbox.KeyArrowRight:
|
||||
moveCh <- East
|
||||
case termbox.KeyEsc: // Quit
|
||||
return
|
||||
}
|
||||
}
|
||||
case termbox.EventResize:
|
||||
// TODO
|
||||
errCh <- errors.New("terminal resizing unsupported")
|
||||
return
|
||||
case termbox.EventError:
|
||||
errCh <- ev.Err
|
||||
return
|
||||
case termbox.EventInterrupt:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return moveCh, errCh
|
||||
}
|
||||
|
||||
func (s *snake) flush() {
|
||||
termbox.Flush()
|
||||
s.cells = termbox.CellBuffer()
|
||||
}
|
||||
|
||||
func (s *snake) getCellRune(p position) rune {
|
||||
i := p.Y*s.width + p.X
|
||||
return s.cells[i].Ch
|
||||
}
|
||||
func (s *snake) setCell(p position, c termbox.Cell) {
|
||||
i := p.Y*s.width + p.X
|
||||
s.cells[i] = c
|
||||
}
|
||||
|
||||
func (s *snake) drawBorder() {
|
||||
for x := 0; x < s.width; x++ {
|
||||
s.setCell(position{x, 0}, border)
|
||||
s.setCell(position{x, s.height - 1}, border)
|
||||
}
|
||||
for y := 0; y < s.height-1; y++ {
|
||||
s.setCell(position{0, y}, border)
|
||||
s.setCell(position{s.width - 1, y}, border)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *snake) placeFood() {
|
||||
for {
|
||||
// a random empty cell
|
||||
x := rand.Intn(s.width-2) + 1
|
||||
y := rand.Intn(s.height-2) + 1
|
||||
foodp := position{x, y}
|
||||
r := s.getCellRune(foodp)
|
||||
if r != ' ' {
|
||||
continue
|
||||
}
|
||||
s.setCell(foodp, food)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *snake) startSnake() {
|
||||
// a random cell somewhat near the center
|
||||
x := rand.Intn(s.width/2) + s.width/4
|
||||
y := rand.Intn(s.height/2) + s.height/4
|
||||
head := position{x, y}
|
||||
s.setCell(head, snakeHead)
|
||||
s.body = append(s.body[:0], head)
|
||||
s.heading = direction(rand.Intn(4))
|
||||
}
|
||||
|
||||
func (s *snake) doMove(move direction) bool {
|
||||
head := s.body[len(s.body)-1]
|
||||
s.setCell(head, snakeBody)
|
||||
head = head.next(move)
|
||||
s.heading = move
|
||||
s.body = append(s.body, head)
|
||||
r := s.getCellRune(head)
|
||||
s.setCell(head, snakeHead)
|
||||
gameOver := false
|
||||
switch r {
|
||||
case food.Ch:
|
||||
s.placeFood()
|
||||
case border.Ch, snakeBody.Ch:
|
||||
gameOver = true
|
||||
fallthrough
|
||||
case empty.Ch:
|
||||
s.setCell(s.body[0], empty)
|
||||
s.body = s.body[1:]
|
||||
default:
|
||||
panic(r)
|
||||
}
|
||||
s.flush()
|
||||
return gameOver
|
||||
}
|
||||
|
||||
const (
|
||||
fg = termbox.ColorWhite
|
||||
bg = termbox.ColorBlack
|
||||
)
|
||||
|
||||
// Symbols to use.
|
||||
// Could use Unicode instead of simple ASCII.
|
||||
var (
|
||||
empty = termbox.Cell{Ch: ' ', Bg: bg, Fg: fg}
|
||||
border = termbox.Cell{Ch: '+', Bg: bg, Fg: termbox.ColorBlue}
|
||||
snakeBody = termbox.Cell{Ch: '#', Bg: bg, Fg: termbox.ColorGreen}
|
||||
snakeHead = termbox.Cell{Ch: 'O', Bg: bg, Fg: termbox.ColorYellow | termbox.AttrBold}
|
||||
food = termbox.Cell{Ch: '@', Bg: bg, Fg: termbox.ColorRed}
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue