Ver código fonte

Implement snake

Alexey Edelev 5 anos atrás
pai
commit
20b98a49c5

+ 5 - 0
build.sh

@@ -10,6 +10,11 @@ mkdir -p $RPC_PATH
 rm -f $RPC_PATH/*.pb.go
 protoc -I$RPC_PATH --go_out=plugins=grpc:$RPC_PATH $RPC_PATH/remotecontrol.proto
 
+export SNAKE_RPC_PATH=$PWD/neuralnetwork/snakesimulator
+mkdir -p $SNAKE_RPC_PATH
+rm -f $SNAKE_RPC_PATH/*.pb.go
+protoc -I$SNAKE_RPC_PATH --go_out=plugins=grpc:$SNAKE_RPC_PATH $SNAKE_RPC_PATH/snakesimulator.proto
+
 cd neuralnetwork
 
 go get -v

+ 1 - 1
gui/qtprotobuf

@@ -1 +1 @@
-Subproject commit 2545e7b9536e831b663468f02b74723176654a91
+Subproject commit 79f5272e3ef435ab2def99d126739099f377277e

+ 3 - 0
neuralnetwork/main.go

@@ -3,9 +3,12 @@ package main
 import (
 	neuralnetwork "./neuralnetwork"
 	remotecontrol "./remotecontrol"
+	snakesimulator "./snakesimulator"
 )
 
 func main() {
+	s := snakesimulator.NewSnakeSimulator()
+	s.Run()
 	// genetic.NewPopulation(nil, mutagen.NewDummyMutagen(50), 200, []int{13, 8, 12, 3})
 	sizes := []int{13, 8, 12, 3}
 	nn, _ := neuralnetwork.NewNeuralNetwork(sizes, neuralnetwork.NewRPropInitializer(neuralnetwork.RPropConfig{

+ 12 - 0
neuralnetwork/snakesimulator/field.go

@@ -0,0 +1,12 @@
+package snakesimulator
+
+import (
+	"math/rand"
+	"time"
+)
+
+func (f *Field) GenerateNextFood() {
+	rand.Seed(time.Now().UnixNano())
+	f.Food.X = rand.Uint32() % f.Width
+	f.Food.Y = rand.Uint32() % f.Height
+}

+ 43 - 0
neuralnetwork/snakesimulator/snake.go

@@ -0,0 +1,43 @@
+package snakesimulator
+
+func (p Point) Copy() (pCopy *Point) {
+	pCopy = &Point{
+		X: p.X,
+		Y: p.Y,
+	}
+	return
+}
+
+func (s *Snake) NewHead(direction Direction) (newHead *Point) {
+	newHead = s.Points[0].Copy()
+	switch direction {
+	case Direction_Up:
+		newHead.Y -= 1
+	case Direction_Down:
+		newHead.Y += 1
+	case Direction_Right:
+		newHead.X += 1
+	case Direction_Left:
+		newHead.X -= 1
+	}
+	return
+}
+
+func (s *Snake) Move(newHead *Point) {
+	s.Points = s.Points[:len(s.Points)-1]
+	s.Points = append([]*Point{newHead}, s.Points...)
+}
+
+func (s *Snake) Feed(food *Point) {
+	s.Points = append([]*Point{food}, s.Points...)
+}
+
+func (s *Snake) SelfCollision(head *Point) int {
+	for index, point := range s.Points[:len(s.Points)-1] {
+		if point.X == head.X && point.Y == head.Y {
+			return index
+		}
+	}
+
+	return 0
+}

+ 104 - 0
neuralnetwork/snakesimulator/snakesimulator.go

@@ -0,0 +1,104 @@
+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
+	}
+}

+ 433 - 0
neuralnetwork/snakesimulator/snakesimulator.pb.go

@@ -0,0 +1,433 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: snakesimulator.proto
+
+package snakesimulator
+
+import (
+	context "context"
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type Direction int32
+
+const (
+	Direction_Unknown Direction = 0
+	Direction_Up      Direction = 1
+	Direction_Down    Direction = 2
+	Direction_Left    Direction = 3
+	Direction_Right   Direction = 4
+)
+
+var Direction_name = map[int32]string{
+	0: "Unknown",
+	1: "Up",
+	2: "Down",
+	3: "Left",
+	4: "Right",
+}
+
+var Direction_value = map[string]int32{
+	"Unknown": 0,
+	"Up":      1,
+	"Down":    2,
+	"Left":    3,
+	"Right":   4,
+}
+
+func (x Direction) String() string {
+	return proto.EnumName(Direction_name, int32(x))
+}
+
+func (Direction) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_b704e55df18a3970, []int{0}
+}
+
+type Point struct {
+	X                    uint32   `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"`
+	Y                    uint32   `protobuf:"varint,2,opt,name=y,proto3" json:"y,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Point) Reset()         { *m = Point{} }
+func (m *Point) String() string { return proto.CompactTextString(m) }
+func (*Point) ProtoMessage()    {}
+func (*Point) Descriptor() ([]byte, []int) {
+	return fileDescriptor_b704e55df18a3970, []int{0}
+}
+
+func (m *Point) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Point.Unmarshal(m, b)
+}
+func (m *Point) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Point.Marshal(b, m, deterministic)
+}
+func (m *Point) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Point.Merge(m, src)
+}
+func (m *Point) XXX_Size() int {
+	return xxx_messageInfo_Point.Size(m)
+}
+func (m *Point) XXX_DiscardUnknown() {
+	xxx_messageInfo_Point.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Point proto.InternalMessageInfo
+
+func (m *Point) GetX() uint32 {
+	if m != nil {
+		return m.X
+	}
+	return 0
+}
+
+func (m *Point) GetY() uint32 {
+	if m != nil {
+		return m.Y
+	}
+	return 0
+}
+
+type Snake struct {
+	Points               []*Point `protobuf:"bytes,1,rep,name=points,proto3" json:"points,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Snake) Reset()         { *m = Snake{} }
+func (m *Snake) String() string { return proto.CompactTextString(m) }
+func (*Snake) ProtoMessage()    {}
+func (*Snake) Descriptor() ([]byte, []int) {
+	return fileDescriptor_b704e55df18a3970, []int{1}
+}
+
+func (m *Snake) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Snake.Unmarshal(m, b)
+}
+func (m *Snake) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Snake.Marshal(b, m, deterministic)
+}
+func (m *Snake) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Snake.Merge(m, src)
+}
+func (m *Snake) XXX_Size() int {
+	return xxx_messageInfo_Snake.Size(m)
+}
+func (m *Snake) XXX_DiscardUnknown() {
+	xxx_messageInfo_Snake.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Snake proto.InternalMessageInfo
+
+func (m *Snake) GetPoints() []*Point {
+	if m != nil {
+		return m.Points
+	}
+	return nil
+}
+
+type Field struct {
+	Width                uint32   `protobuf:"varint,1,opt,name=width,proto3" json:"width,omitempty"`
+	Height               uint32   `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"`
+	Food                 *Point   `protobuf:"bytes,3,opt,name=food,proto3" json:"food,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Field) Reset()         { *m = Field{} }
+func (m *Field) String() string { return proto.CompactTextString(m) }
+func (*Field) ProtoMessage()    {}
+func (*Field) Descriptor() ([]byte, []int) {
+	return fileDescriptor_b704e55df18a3970, []int{2}
+}
+
+func (m *Field) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Field.Unmarshal(m, b)
+}
+func (m *Field) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Field.Marshal(b, m, deterministic)
+}
+func (m *Field) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Field.Merge(m, src)
+}
+func (m *Field) XXX_Size() int {
+	return xxx_messageInfo_Field.Size(m)
+}
+func (m *Field) XXX_DiscardUnknown() {
+	xxx_messageInfo_Field.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Field proto.InternalMessageInfo
+
+func (m *Field) GetWidth() uint32 {
+	if m != nil {
+		return m.Width
+	}
+	return 0
+}
+
+func (m *Field) GetHeight() uint32 {
+	if m != nil {
+		return m.Height
+	}
+	return 0
+}
+
+func (m *Field) GetFood() *Point {
+	if m != nil {
+		return m.Food
+	}
+	return nil
+}
+
+type None struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *None) Reset()         { *m = None{} }
+func (m *None) String() string { return proto.CompactTextString(m) }
+func (*None) ProtoMessage()    {}
+func (*None) Descriptor() ([]byte, []int) {
+	return fileDescriptor_b704e55df18a3970, []int{3}
+}
+
+func (m *None) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_None.Unmarshal(m, b)
+}
+func (m *None) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_None.Marshal(b, m, deterministic)
+}
+func (m *None) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_None.Merge(m, src)
+}
+func (m *None) XXX_Size() int {
+	return xxx_messageInfo_None.Size(m)
+}
+func (m *None) XXX_DiscardUnknown() {
+	xxx_messageInfo_None.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_None proto.InternalMessageInfo
+
+func init() {
+	proto.RegisterEnum("snakesimulator.Direction", Direction_name, Direction_value)
+	proto.RegisterType((*Point)(nil), "snakesimulator.Point")
+	proto.RegisterType((*Snake)(nil), "snakesimulator.Snake")
+	proto.RegisterType((*Field)(nil), "snakesimulator.Field")
+	proto.RegisterType((*None)(nil), "snakesimulator.None")
+}
+
+func init() { proto.RegisterFile("snakesimulator.proto", fileDescriptor_b704e55df18a3970) }
+
+var fileDescriptor_b704e55df18a3970 = []byte{
+	// 273 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xc1, 0x4b, 0xc3, 0x30,
+	0x14, 0x87, 0x97, 0xb6, 0xa9, 0xee, 0x4d, 0x47, 0x79, 0x54, 0x29, 0x9e, 0x4a, 0xbc, 0x4c, 0xc1,
+	0x21, 0x13, 0xc4, 0x9b, 0x97, 0xe1, 0x49, 0x44, 0x3a, 0x76, 0x77, 0xda, 0xd4, 0x86, 0xcd, 0xa4,
+	0xb4, 0x91, 0x6d, 0x77, 0xff, 0x70, 0x79, 0x69, 0x3d, 0x38, 0x11, 0x76, 0xcb, 0xef, 0xf1, 0xbe,
+	0x7c, 0xef, 0x25, 0x10, 0x37, 0x7a, 0xb1, 0x94, 0x8d, 0xfa, 0xf8, 0x5c, 0x2d, 0xac, 0xa9, 0xc7,
+	0x55, 0x6d, 0xac, 0xc1, 0xe1, 0xef, 0xaa, 0x38, 0x07, 0xfe, 0x6c, 0x94, 0xb6, 0x78, 0x04, 0x6c,
+	0x93, 0xb0, 0x94, 0x8d, 0x8e, 0x33, 0xb6, 0xa1, 0xb4, 0x4d, 0xbc, 0x36, 0x6d, 0xc5, 0x2d, 0xf0,
+	0x19, 0x61, 0x78, 0x05, 0x61, 0x45, 0xdd, 0x4d, 0xc2, 0x52, 0x7f, 0x34, 0x98, 0x9c, 0x8c, 0x77,
+	0x24, 0xee, 0xae, 0xac, 0x6b, 0x12, 0x2f, 0xc0, 0x1f, 0x94, 0x5c, 0xe5, 0x18, 0x03, 0x5f, 0xab,
+	0xdc, 0x96, 0x9d, 0xa0, 0x0d, 0x78, 0x0a, 0x61, 0x29, 0xd5, 0x7b, 0x69, 0x3b, 0x53, 0x97, 0xf0,
+	0x02, 0x82, 0xc2, 0x98, 0x3c, 0xf1, 0x53, 0xf6, 0xbf, 0xc3, 0xb5, 0x88, 0x10, 0x82, 0x27, 0xa3,
+	0xe5, 0xe5, 0x3d, 0xf4, 0xa7, 0xaa, 0x96, 0x6f, 0x56, 0x19, 0x8d, 0x03, 0x38, 0x98, 0xeb, 0xa5,
+	0x36, 0x6b, 0x1d, 0xf5, 0x30, 0x04, 0x6f, 0x5e, 0x45, 0x0c, 0x0f, 0x21, 0x98, 0x52, 0xc5, 0xa3,
+	0xd3, 0xa3, 0x2c, 0x6c, 0xe4, 0x63, 0x1f, 0x78, 0x46, 0xc6, 0x28, 0x98, 0x7c, 0x31, 0x18, 0xba,
+	0x1d, 0x67, 0x3f, 0x1e, 0xbc, 0x03, 0xee, 0xcc, 0x18, 0xef, 0x4e, 0x40, 0xca, 0xb3, 0x3f, 0x73,
+	0x39, 0x5c, 0xf4, 0xae, 0x19, 0x91, 0x45, 0xbb, 0xf7, 0x7e, 0xa4, 0x7b, 0x24, 0x22, 0x5f, 0x43,
+	0xf7, 0x4b, 0x37, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x15, 0x7b, 0x53, 0xb6, 0xbd, 0x01, 0x00,
+	0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// SnakeSimulatorClient is the client API for SnakeSimulator service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type SnakeSimulatorClient interface {
+	Snake(ctx context.Context, in *None, opts ...grpc.CallOption) (SnakeSimulator_SnakeClient, error)
+	Field(ctx context.Context, in *None, opts ...grpc.CallOption) (SnakeSimulator_FieldClient, error)
+}
+
+type snakeSimulatorClient struct {
+	cc *grpc.ClientConn
+}
+
+func NewSnakeSimulatorClient(cc *grpc.ClientConn) SnakeSimulatorClient {
+	return &snakeSimulatorClient{cc}
+}
+
+func (c *snakeSimulatorClient) Snake(ctx context.Context, in *None, opts ...grpc.CallOption) (SnakeSimulator_SnakeClient, error) {
+	stream, err := c.cc.NewStream(ctx, &_SnakeSimulator_serviceDesc.Streams[0], "/snakesimulator.SnakeSimulator/snake", opts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &snakeSimulatorSnakeClient{stream}
+	if err := x.ClientStream.SendMsg(in); err != nil {
+		return nil, err
+	}
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	return x, nil
+}
+
+type SnakeSimulator_SnakeClient interface {
+	Recv() (*Snake, error)
+	grpc.ClientStream
+}
+
+type snakeSimulatorSnakeClient struct {
+	grpc.ClientStream
+}
+
+func (x *snakeSimulatorSnakeClient) Recv() (*Snake, error) {
+	m := new(Snake)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+func (c *snakeSimulatorClient) Field(ctx context.Context, in *None, opts ...grpc.CallOption) (SnakeSimulator_FieldClient, error) {
+	stream, err := c.cc.NewStream(ctx, &_SnakeSimulator_serviceDesc.Streams[1], "/snakesimulator.SnakeSimulator/field", opts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &snakeSimulatorFieldClient{stream}
+	if err := x.ClientStream.SendMsg(in); err != nil {
+		return nil, err
+	}
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	return x, nil
+}
+
+type SnakeSimulator_FieldClient interface {
+	Recv() (*Field, error)
+	grpc.ClientStream
+}
+
+type snakeSimulatorFieldClient struct {
+	grpc.ClientStream
+}
+
+func (x *snakeSimulatorFieldClient) Recv() (*Field, error) {
+	m := new(Field)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+// SnakeSimulatorServer is the server API for SnakeSimulator service.
+type SnakeSimulatorServer interface {
+	Snake(*None, SnakeSimulator_SnakeServer) error
+	Field(*None, SnakeSimulator_FieldServer) error
+}
+
+// UnimplementedSnakeSimulatorServer can be embedded to have forward compatible implementations.
+type UnimplementedSnakeSimulatorServer struct {
+}
+
+func (*UnimplementedSnakeSimulatorServer) Snake(req *None, srv SnakeSimulator_SnakeServer) error {
+	return status.Errorf(codes.Unimplemented, "method Snake not implemented")
+}
+func (*UnimplementedSnakeSimulatorServer) Field(req *None, srv SnakeSimulator_FieldServer) error {
+	return status.Errorf(codes.Unimplemented, "method Field not implemented")
+}
+
+func RegisterSnakeSimulatorServer(s *grpc.Server, srv SnakeSimulatorServer) {
+	s.RegisterService(&_SnakeSimulator_serviceDesc, srv)
+}
+
+func _SnakeSimulator_Snake_Handler(srv interface{}, stream grpc.ServerStream) error {
+	m := new(None)
+	if err := stream.RecvMsg(m); err != nil {
+		return err
+	}
+	return srv.(SnakeSimulatorServer).Snake(m, &snakeSimulatorSnakeServer{stream})
+}
+
+type SnakeSimulator_SnakeServer interface {
+	Send(*Snake) error
+	grpc.ServerStream
+}
+
+type snakeSimulatorSnakeServer struct {
+	grpc.ServerStream
+}
+
+func (x *snakeSimulatorSnakeServer) Send(m *Snake) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+func _SnakeSimulator_Field_Handler(srv interface{}, stream grpc.ServerStream) error {
+	m := new(None)
+	if err := stream.RecvMsg(m); err != nil {
+		return err
+	}
+	return srv.(SnakeSimulatorServer).Field(m, &snakeSimulatorFieldServer{stream})
+}
+
+type SnakeSimulator_FieldServer interface {
+	Send(*Field) error
+	grpc.ServerStream
+}
+
+type snakeSimulatorFieldServer struct {
+	grpc.ServerStream
+}
+
+func (x *snakeSimulatorFieldServer) Send(m *Field) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+var _SnakeSimulator_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "snakesimulator.SnakeSimulator",
+	HandlerType: (*SnakeSimulatorServer)(nil),
+	Methods:     []grpc.MethodDesc{},
+	Streams: []grpc.StreamDesc{
+		{
+			StreamName:    "snake",
+			Handler:       _SnakeSimulator_Snake_Handler,
+			ServerStreams: true,
+		},
+		{
+			StreamName:    "field",
+			Handler:       _SnakeSimulator_Field_Handler,
+			ServerStreams: true,
+		},
+	},
+	Metadata: "snakesimulator.proto",
+}

+ 59 - 0
neuralnetwork/snakesimulator/snakesimulator.proto

@@ -0,0 +1,59 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of NeuralNetwork project https://git.semlanik.org/semlanik/NeuralNetwork
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+ syntax="proto3";
+
+package snakesimulator;
+
+enum Direction {
+    Unknown = 0;
+    Up = 1;
+    Down = 2;
+    Left = 3;
+    Right = 4;
+}
+
+message Point {
+    uint32 x = 1;
+    uint32 y = 2;
+}
+
+message Snake {
+    repeated Point points = 1;
+}
+
+message Field {
+    uint32 width = 1;
+    uint32 height = 2;
+    Point food = 3;
+}
+
+message None {
+}
+
+service SnakeSimulator {
+    rpc snake(None) returns (stream Snake) {}
+    rpc field(None) returns (stream Field) {}
+}

+ 23 - 0
neuralnetwork/snakesimulator/snakesimulatorui/CMakeLists.txt

@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(SnakeSimulatorkUi LANGUAGES CXX)
+
+find_package(Qt5 COMPONENTS Quick Gui Core Qml REQUIRED)
+
+set(QTPROTOBUF_MAKE_TESTS false)
+set(QTPROTOBUF_MAKE_EXAMPLES false)
+add_subdirectory("qtprotobuf")
+find_package(QtProtobufProject CONFIG COMPONENTS QtProtobuf QtGrpc REQUIRED)
+if(Qt5_POSITION_INDEPENDENT_CODE)
+    set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
+endif()
+
+file(GLOB PROTO_FILES ABSOLUTE "${CMAKE_CURRENT_SOURCE_DIR}/../snakesimulator.proto")
+
+generate_qtprotobuf(TARGET SnakeSimulatorkUi PROTO_FILES ${PROTO_FILES})
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+add_executable(SnakeSimulatorkUi main.cpp qml.qrc)
+target_link_libraries(SnakeSimulatorkUi Qt5::Core Qt5::Gui Qt5::Qml Qt5::Quick QtProtobufProject::QtProtobuf QtProtobufProject::QtGrpc ${QtProtobuf_GENERATED})

+ 72 - 0
neuralnetwork/snakesimulator/snakesimulatorui/main.cpp

@@ -0,0 +1,72 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of NeuralNetwork project https://git.semlanik.org/semlanik/NeuralNetwork
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <QQmlContext>
+#include <QDebug>
+
+#include "qgrpchttp2channel.h"
+#include "insecurecredentials.h"
+#include "snakesimulatorclient.h"
+
+class NoneCredencials : public QtProtobuf::CallCredentials
+{
+public:
+    NoneCredencials() : QtProtobuf::CallCredentials(QtProtobuf::AbstractCredentials::CredentialMap{}) {}
+};
+
+
+int main(int argc, char *argv[])
+{
+    QGuiApplication app(argc, argv);
+
+    std::shared_ptr<snakesimulator::SnakeSimulatorClient> client(new snakesimulator::SnakeSimulatorClient);
+    auto chan = std::shared_ptr<QtProtobuf::QGrpcHttp2Channel>(new QtProtobuf::QGrpcHttp2Channel(QUrl("http://localhost:65002"), QtProtobuf::InsecureCredentials()|NoneCredencials()));
+    client->attachChannel(chan);
+
+    snakesimulator::Snake *snake = new snakesimulator::Snake;
+    snakesimulator::Field *field = new snakesimulator::Field;
+
+    QObject::connect(client.get(), &snakesimulator::SnakeSimulatorClient::fieldUpdated, [field](const snakesimulator::Field & _field){
+        *field = _field;
+    });
+
+    QObject::connect(client.get(), &snakesimulator::SnakeSimulatorClient::snakeUpdated, [snake](const snakesimulator::Snake & _snake){
+        *snake = _snake;
+    });
+
+    client->subscribeFieldUpdates({});
+    client->subscribeSnakeUpdates({});
+
+    QQmlApplicationEngine engine;
+    engine.rootContext()->setContextProperty("field", field);
+    engine.rootContext()->setContextProperty("snake", snake);
+    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+    if (engine.rootObjects().isEmpty())
+        return -1;
+
+    return app.exec();
+}

+ 67 - 0
neuralnetwork/snakesimulator/snakesimulatorui/main.qml

@@ -0,0 +1,67 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of NeuralNetwork project https://git.semlanik.org/semlanik/NeuralNetwork
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+import QtQuick 2.11
+import QtQuick.Window 2.11
+import QtQuick.Controls 1.4
+
+ApplicationWindow {
+    id: root
+    visible: true
+    property int tileSize: 20
+    width: field.width*tileSize
+    height: field.height*tileSize
+
+    Rectangle {
+        color: "#000000"
+        anchors.fill: parent
+    }
+
+    Repeater {
+        model: snake.pointsData.length
+        Rectangle {
+            color: "#ffffff"
+            x: snake.pointsData[model.index].x*tileSize
+            y: snake.pointsData[model.index].y*tileSize
+            width: tileSize
+            height: tileSize
+        }
+    }
+
+    Rectangle {
+        color: "#eeffee"
+        x: field.food.x*tileSize
+        y: field.food.y*tileSize
+        width: tileSize
+        height: tileSize
+    }
+
+    Connections {
+        target: field
+        onWidthChanged: {
+            console.log("New width: " + field.width)
+        }
+    }
+}

+ 5 - 0
neuralnetwork/snakesimulator/snakesimulatorui/qml.qrc

@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/">
+        <file>main.qml</file>
+    </qresource>
+</RCC>