package snakesimulator import ( fmt "fmt" "math/rand" "net" "time" grpc "google.golang.org/grpc" ) type SnakeSimulator struct { field *Field snake *Snake fieldUpdateQueue chan bool snakeUpdateQueue chan bool } func NewSnakeSimulator() (s *SnakeSimulator) { s = &SnakeSimulator{ field: &Field{ Food: &Point{}, Width: 40, Height: 40, }, snake: &Snake{ Points: []*Point{ &Point{X: 20, Y: 20}, &Point{X: 21, Y: 20}, &Point{X: 22, Y: 20}, }, }, fieldUpdateQueue: make(chan bool, 2), snakeUpdateQueue: make(chan bool, 2), } return } func (s *SnakeSimulator) Run() { go func() { grpcServer := grpc.NewServer() RegisterSnakeSimulatorServer(grpcServer, s) lis, err := net.Listen("tcp", "localhost:65002") if err != nil { fmt.Printf("Failed to listen: %v\n", err) } fmt.Printf("Listen SnakeSimulator localhost:65002\n") if err := grpcServer.Serve(lis); err != nil { fmt.Printf("Failed to serve: %v\n", err) } }() s.field.GenerateNextFood() for true { direction := rand.Int31()%4 + 1 newHead := s.snake.NewHead(Direction(direction)) if newHead.X == s.field.Food.X && newHead.Y == s.field.Food.Y { s.snake.Feed(newHead) s.field.GenerateNextFood() s.fieldUpdateQueue <- true } else if newHead.X >= s.field.Width || newHead.Y >= s.field.Height { fmt.Printf("Game over\n") break } else if selfCollisionIndex := s.snake.SelfCollision(newHead); selfCollisionIndex > 0 { if selfCollisionIndex == 1 { fmt.Printf("Step backward, skip\n") continue } fmt.Printf("Game over self collision\n") break } else { s.snake.Move(newHead) } s.snakeUpdateQueue <- true time.Sleep(50 * time.Millisecond) } } func (s *SnakeSimulator) Field(_ *None, srv SnakeSimulator_FieldServer) error { ctx := srv.Context() for { select { case <-ctx.Done(): return ctx.Err() default: } srv.Send(s.field) <-s.fieldUpdateQueue } } func (s *SnakeSimulator) Snake(_ *None, srv SnakeSimulator_SnakeServer) error { ctx := srv.Context() for { select { case <-ctx.Done(): return ctx.Err() default: } srv.Send(s.snake) <-s.snakeUpdateQueue } }