소스 검색

Add speed regulation

Alexey Edelev 5 년 전
부모
커밋
30e70191a1

+ 36 - 8
neuralnetwork/snakesimulator/snakesimulator.go

@@ -1,6 +1,7 @@
 package snakesimulator
 
 import (
+	context "context"
 	fmt "fmt"
 	math "math"
 	"math/rand"
@@ -17,9 +18,11 @@ type SnakeSimulator struct {
 	field            *Field
 	snake            *Snake
 	stats            *Stats
+	speed            uint32
 	fieldUpdateQueue chan bool
 	snakeUpdateQueue chan bool
 	statsUpdateQueue chan bool
+	speedQueue       chan uint32
 }
 
 func NewSnakeSimulator() (s *SnakeSimulator) {
@@ -40,6 +43,8 @@ func NewSnakeSimulator() (s *SnakeSimulator) {
 		fieldUpdateQueue: make(chan bool, 2),
 		snakeUpdateQueue: make(chan bool, 2),
 		statsUpdateQueue: make(chan bool, 2),
+		speedQueue:       make(chan uint32, 1),
+		speed:            10,
 	}
 	return
 }
@@ -91,12 +96,26 @@ func (s *SnakeSimulator) Verify(population *genetic.Population) (results []*gene
 		}
 
 		i := 0
+		s.stats.Move = 0
 		for i < 300 {
-			s.stats.Move = uint32(i)
 			s.statsUpdateQueue <- true
-
+			//Read speed from client
+			select {
+			case newSpeed := <-s.speedQueue:
+				fmt.Printf("Apply new speed: %v\n", newSpeed)
+				if newSpeed < 10 {
+					if newSpeed > 0 {
+						s.speed = newSpeed
+					} else {
+						s.speed = 0
+					}
+				}
+			default:
+			}
 			i++
-			s.snakeUpdateQueue <- true
+			if s.speed > 0 {
+				s.snakeUpdateQueue <- true
+			}
 			direction, _ := inidividual.Predict(mat.NewDense(inidividual.Sizes[0], 1, s.GetHeadState()))
 			newHead := s.snake.NewHead(Direction(direction + 1))
 			if newHead.X == s.field.Food.X && newHead.Y == s.field.Food.Y {
@@ -104,12 +123,12 @@ func (s *SnakeSimulator) Verify(population *genetic.Population) (results []*gene
 				s.field.GenerateNextFood()
 				s.fieldUpdateQueue <- true
 			} else if newHead.X >= s.field.Width || newHead.Y >= s.field.Height {
-				fmt.Printf("Game over\n")
+				// fmt.Printf("Game over\n")
 				// time.Sleep(1000 * time.Millisecond)
 				break
 			} else if selfCollisionIndex := s.snake.SelfCollision(newHead); selfCollisionIndex > 0 {
 				if selfCollisionIndex == 1 {
-					fmt.Printf("Step backward, skip\n")
+					// fmt.Printf("Step backward, skip\n")
 					continue
 				}
 				fmt.Printf("Game over self collision\n")
@@ -117,11 +136,15 @@ func (s *SnakeSimulator) Verify(population *genetic.Population) (results []*gene
 			} else {
 				s.snake.Move(newHead)
 			}
-			time.Sleep(10 * time.Millisecond)
+			s.stats.Move++
+
+			if s.speed > 0 {
+				time.Sleep(100 / time.Duration(s.speed) * time.Millisecond)
+			}
 		}
 
 		results[index] = &genetic.IndividalResult{
-			Result: float64(len(s.snake.Points)) * float64(i),
+			Result: float64(len(s.snake.Points)) * float64(s.stats.Move),
 			Index:  index,
 		}
 	}
@@ -258,7 +281,7 @@ func (s *SnakeSimulator) Run() {
 			s.snake.Feed(newHead)
 			s.field.GenerateNextFood()
 			s.fieldUpdateQueue <- true
-		} else if newHead.X >= s.field.Width || newHead.Y >= s.field.Height {
+		} 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 {
@@ -314,3 +337,8 @@ func (s *SnakeSimulator) Stats(_ *None, srv SnakeSimulator_StatsServer) error {
 		<-s.statsUpdateQueue
 	}
 }
+
+func (s *SnakeSimulator) SetSpeed(ctx context.Context, speed *Speed) (*None, error) {
+	s.speedQueue <- speed.Speed
+	return &None{}, nil
+}

+ 103 - 24
neuralnetwork/snakesimulator/snakesimulator.pb.go

@@ -254,6 +254,45 @@ func (m *Stats) GetMove() uint32 {
 	return 0
 }
 
+type Speed struct {
+	Speed                uint32   `protobuf:"varint,1,opt,name=speed,proto3" json:"speed,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Speed) Reset()         { *m = Speed{} }
+func (m *Speed) String() string { return proto.CompactTextString(m) }
+func (*Speed) ProtoMessage()    {}
+func (*Speed) Descriptor() ([]byte, []int) {
+	return fileDescriptor_b704e55df18a3970, []int{4}
+}
+
+func (m *Speed) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Speed.Unmarshal(m, b)
+}
+func (m *Speed) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Speed.Marshal(b, m, deterministic)
+}
+func (m *Speed) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Speed.Merge(m, src)
+}
+func (m *Speed) XXX_Size() int {
+	return xxx_messageInfo_Speed.Size(m)
+}
+func (m *Speed) XXX_DiscardUnknown() {
+	xxx_messageInfo_Speed.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Speed proto.InternalMessageInfo
+
+func (m *Speed) GetSpeed() uint32 {
+	if m != nil {
+		return m.Speed
+	}
+	return 0
+}
+
 type None struct {
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_unrecognized     []byte   `json:"-"`
@@ -264,7 +303,7 @@ 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{4}
+	return fileDescriptor_b704e55df18a3970, []int{5}
 }
 
 func (m *None) XXX_Unmarshal(b []byte) error {
@@ -291,34 +330,37 @@ func init() {
 	proto.RegisterType((*Snake)(nil), "snakesimulator.Snake")
 	proto.RegisterType((*Field)(nil), "snakesimulator.Field")
 	proto.RegisterType((*Stats)(nil), "snakesimulator.Stats")
+	proto.RegisterType((*Speed)(nil), "snakesimulator.Speed")
 	proto.RegisterType((*None)(nil), "snakesimulator.None")
 }
 
 func init() { proto.RegisterFile("snakesimulator.proto", fileDescriptor_b704e55df18a3970) }
 
 var fileDescriptor_b704e55df18a3970 = []byte{
-	// 329 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xcf, 0x4b, 0xfb, 0x30,
-	0x18, 0xc6, 0x97, 0xad, 0xe9, 0xf7, 0xbb, 0x77, 0x6e, 0x94, 0x97, 0x29, 0xc5, 0x83, 0x8c, 0x78,
-	0x99, 0x82, 0x43, 0x26, 0x88, 0x37, 0x2f, 0xc3, 0x93, 0x88, 0x74, 0xec, 0xe4, 0xc5, 0x6a, 0xb3,
-	0x35, 0xac, 0x4b, 0x4a, 0x9b, 0xfd, 0xfa, 0xe7, 0xfc, 0xdb, 0x24, 0x69, 0x06, 0x73, 0x22, 0xec,
-	0x96, 0xf7, 0xe9, 0xf3, 0xe4, 0xc3, 0xf3, 0x36, 0xd0, 0x2d, 0x65, 0x3c, 0xe7, 0xa5, 0x58, 0x2c,
-	0xb3, 0x58, 0xab, 0x62, 0x90, 0x17, 0x4a, 0x2b, 0xec, 0xfc, 0x54, 0xd9, 0x25, 0xd0, 0x57, 0x25,
-	0xa4, 0xc6, 0x13, 0x20, 0x9b, 0x90, 0xf4, 0x48, 0xbf, 0x1d, 0x91, 0x8d, 0x99, 0xb6, 0x61, 0xbd,
-	0x9a, 0xb6, 0xec, 0x1e, 0xe8, 0xd8, 0xc4, 0xf0, 0x06, 0xfc, 0xdc, 0xb8, 0xcb, 0x90, 0xf4, 0x1a,
-	0xfd, 0xd6, 0xf0, 0x74, 0x70, 0x00, 0xb1, 0x77, 0x45, 0xce, 0xc4, 0xde, 0x81, 0x3e, 0x09, 0x9e,
-	0x25, 0xd8, 0x05, 0xba, 0x16, 0x89, 0x4e, 0x1d, 0xa0, 0x1a, 0xf0, 0x0c, 0xfc, 0x94, 0x8b, 0x59,
-	0xaa, 0x1d, 0xc9, 0x4d, 0x78, 0x05, 0xde, 0x54, 0xa9, 0x24, 0x6c, 0xf4, 0xc8, 0xdf, 0x0c, 0x6b,
-	0x61, 0x6f, 0x40, 0xc7, 0x3a, 0xd6, 0x25, 0x5e, 0x00, 0xcc, 0xb8, 0xe4, 0x45, 0xac, 0x85, 0x92,
-	0x0e, 0xb3, 0xa7, 0x98, 0xef, 0x42, 0x26, 0x62, 0x25, 0x92, 0x65, 0x9c, 0x39, 0xde, 0x9e, 0x82,
-	0x08, 0xde, 0x42, 0xad, 0xb8, 0x65, 0xb6, 0x23, 0x7b, 0x66, 0x3e, 0x78, 0x2f, 0x4a, 0xf2, 0xeb,
-	0x47, 0x68, 0x8e, 0x44, 0xc1, 0x3f, 0xed, 0x45, 0x2d, 0xf8, 0x37, 0x91, 0x73, 0xa9, 0xd6, 0x32,
-	0xa8, 0xa1, 0x0f, 0xf5, 0x49, 0x1e, 0x10, 0xfc, 0x0f, 0xde, 0xc8, 0x28, 0x75, 0x73, 0x7a, 0xe6,
-	0x53, 0x1d, 0x34, 0xb0, 0x09, 0x34, 0x32, 0x75, 0x02, 0x6f, 0xf8, 0x45, 0xa0, 0x63, 0x17, 0x38,
-	0xde, 0x95, 0xc0, 0x07, 0xa0, 0xb6, 0x16, 0x76, 0x0f, 0xeb, 0x19, 0xe4, 0xf9, 0xaf, 0xd2, 0x36,
-	0xce, 0x6a, 0xb7, 0xc4, 0x24, 0xa7, 0xd5, 0x52, 0x8f, 0x4b, 0xda, 0x3f, 0xb0, 0x4b, 0x96, 0x76,
-	0x59, 0xc7, 0x32, 0x8d, 0xd9, 0x24, 0x3f, 0x7c, 0xfb, 0x78, 0xee, 0xbe, 0x03, 0x00, 0x00, 0xff,
-	0xff, 0x74, 0x50, 0xa2, 0x2d, 0x54, 0x02, 0x00, 0x00,
+	// 357 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xcf, 0x6b, 0xfa, 0x40,
+	0x10, 0xc5, 0x5d, 0xcd, 0xe6, 0xab, 0xe3, 0x57, 0x09, 0x8b, 0x2d, 0x41, 0x68, 0x91, 0xed, 0xc5,
+	0x16, 0x2a, 0xc5, 0x42, 0x69, 0x4f, 0xbd, 0x48, 0x4f, 0xa5, 0x94, 0x04, 0x4f, 0xbd, 0x34, 0x6d,
+	0x56, 0x5d, 0xd4, 0xdd, 0x90, 0xac, 0xbf, 0xfe, 0xf4, 0xde, 0xca, 0x4e, 0x22, 0x58, 0x6d, 0xc1,
+	0xdb, 0xcc, 0xe4, 0xbd, 0xfd, 0xec, 0xbc, 0x2c, 0xb4, 0x32, 0x15, 0x4d, 0x45, 0x26, 0xe7, 0x8b,
+	0x59, 0x64, 0x74, 0xda, 0x4b, 0x52, 0x6d, 0x34, 0x6b, 0xfe, 0x9c, 0xf2, 0x0b, 0xa0, 0xaf, 0x5a,
+	0x2a, 0xc3, 0xfe, 0x03, 0x59, 0xfb, 0xa4, 0x43, 0xba, 0x8d, 0x80, 0xac, 0x6d, 0xb7, 0xf1, 0xcb,
+	0x79, 0xb7, 0xe1, 0x77, 0x40, 0x43, 0x6b, 0x63, 0xd7, 0xe0, 0x26, 0x56, 0x9d, 0xf9, 0xa4, 0x53,
+	0xe9, 0xd6, 0xfb, 0x27, 0xbd, 0x3d, 0x08, 0x9e, 0x15, 0x14, 0x22, 0xfe, 0x0e, 0xf4, 0x49, 0x8a,
+	0x59, 0xcc, 0x5a, 0x40, 0x57, 0x32, 0x36, 0x93, 0x02, 0x90, 0x37, 0xec, 0x14, 0xdc, 0x89, 0x90,
+	0xe3, 0x89, 0x29, 0x48, 0x45, 0xc7, 0x2e, 0xc1, 0x19, 0x69, 0x1d, 0xfb, 0x95, 0x0e, 0xf9, 0x9b,
+	0x81, 0x12, 0xfe, 0x06, 0x34, 0x34, 0x91, 0xc9, 0xd8, 0x39, 0xc0, 0x58, 0x28, 0x91, 0x46, 0x46,
+	0x6a, 0x55, 0x60, 0x76, 0x26, 0xf6, 0xbb, 0x54, 0xb1, 0x5c, 0xca, 0x78, 0x11, 0xcd, 0x0a, 0xde,
+	0xce, 0x84, 0x31, 0x70, 0xe6, 0x7a, 0x29, 0x90, 0xd9, 0x08, 0xb0, 0xe6, 0x67, 0x40, 0xc3, 0x44,
+	0x08, 0xbc, 0x7e, 0x66, 0x8b, 0xed, 0xf5, 0xb1, 0xe1, 0x2e, 0x38, 0x2f, 0x5a, 0x89, 0xab, 0x47,
+	0xa8, 0x0d, 0x64, 0x2a, 0x3e, 0x91, 0x53, 0x87, 0x7f, 0x43, 0x35, 0x55, 0x7a, 0xa5, 0xbc, 0x12,
+	0x73, 0xa1, 0x3c, 0x4c, 0x3c, 0xc2, 0xaa, 0xe0, 0x0c, 0xec, 0xa4, 0x6c, 0xab, 0x67, 0x31, 0x32,
+	0x5e, 0x85, 0xd5, 0x80, 0x06, 0x76, 0x5b, 0xcf, 0xe9, 0x7f, 0x11, 0x68, 0x62, 0xbe, 0xe1, 0x76,
+	0x47, 0x76, 0x0f, 0x14, 0xb7, 0x66, 0xad, 0xfd, 0xed, 0x2d, 0xb2, 0x7d, 0x90, 0x09, 0xda, 0x79,
+	0xe9, 0x86, 0x58, 0xe7, 0x28, 0xcf, 0xfc, 0x38, 0x27, 0xfe, 0xa0, 0xad, 0x33, 0xc3, 0x2c, 0x8f,
+	0x65, 0x5a, 0x31, 0x3a, 0x1f, 0xa0, 0x9a, 0x09, 0x93, 0x67, 0x75, 0x28, 0xb3, 0xe3, 0xf6, 0xaf,
+	0x67, 0xf2, 0xd2, 0x87, 0x8b, 0xcf, 0xf2, 0xf6, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x27, 0xde, 0x83,
+	0x77, 0xae, 0x02, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -336,6 +378,7 @@ 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)
 	Stats(ctx context.Context, in *None, opts ...grpc.CallOption) (SnakeSimulator_StatsClient, error)
+	SetSpeed(ctx context.Context, in *Speed, opts ...grpc.CallOption) (*None, error)
 }
 
 type snakeSimulatorClient struct {
@@ -442,11 +485,21 @@ func (x *snakeSimulatorStatsClient) Recv() (*Stats, error) {
 	return m, nil
 }
 
+func (c *snakeSimulatorClient) SetSpeed(ctx context.Context, in *Speed, opts ...grpc.CallOption) (*None, error) {
+	out := new(None)
+	err := c.cc.Invoke(ctx, "/snakesimulator.SnakeSimulator/setSpeed", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 // SnakeSimulatorServer is the server API for SnakeSimulator service.
 type SnakeSimulatorServer interface {
 	Snake(*None, SnakeSimulator_SnakeServer) error
 	Field(*None, SnakeSimulator_FieldServer) error
 	Stats(*None, SnakeSimulator_StatsServer) error
+	SetSpeed(context.Context, *Speed) (*None, error)
 }
 
 // UnimplementedSnakeSimulatorServer can be embedded to have forward compatible implementations.
@@ -462,6 +515,9 @@ func (*UnimplementedSnakeSimulatorServer) Field(req *None, srv SnakeSimulator_Fi
 func (*UnimplementedSnakeSimulatorServer) Stats(req *None, srv SnakeSimulator_StatsServer) error {
 	return status.Errorf(codes.Unimplemented, "method Stats not implemented")
 }
+func (*UnimplementedSnakeSimulatorServer) SetSpeed(ctx context.Context, req *Speed) (*None, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method SetSpeed not implemented")
+}
 
 func RegisterSnakeSimulatorServer(s *grpc.Server, srv SnakeSimulatorServer) {
 	s.RegisterService(&_SnakeSimulator_serviceDesc, srv)
@@ -530,10 +586,33 @@ func (x *snakeSimulatorStatsServer) Send(m *Stats) error {
 	return x.ServerStream.SendMsg(m)
 }
 
+func _SnakeSimulator_SetSpeed_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(Speed)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(SnakeSimulatorServer).SetSpeed(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/snakesimulator.SnakeSimulator/SetSpeed",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(SnakeSimulatorServer).SetSpeed(ctx, req.(*Speed))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 var _SnakeSimulator_serviceDesc = grpc.ServiceDesc{
 	ServiceName: "snakesimulator.SnakeSimulator",
 	HandlerType: (*SnakeSimulatorServer)(nil),
-	Methods:     []grpc.MethodDesc{},
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "setSpeed",
+			Handler:    _SnakeSimulator_SetSpeed_Handler,
+		},
+	},
 	Streams: []grpc.StreamDesc{
 		{
 			StreamName:    "snake",

+ 5 - 0
neuralnetwork/snakesimulator/snakesimulator.proto

@@ -56,6 +56,10 @@ message Stats {
     uint32 move = 3;
 }
 
+message Speed {
+    uint32 speed = 1;
+}
+
 message None {
 }
 
@@ -63,4 +67,5 @@ service SnakeSimulator {
     rpc snake(None) returns (stream Snake) {}
     rpc field(None) returns (stream Field) {}
     rpc stats(None) returns (stream Stats) {}
+    rpc setSpeed(Speed) returns (None) {}
 }

+ 1 - 1
neuralnetwork/snakesimulator/snakesimulatorui/CMakeLists.txt

@@ -19,5 +19,5 @@ generate_qtprotobuf(TARGET SnakeSimulatorkUi PROTO_FILES ${PROTO_FILES})
 set(CMAKE_AUTOMOC ON)
 set(CMAKE_AUTORCC ON)
 
-add_executable(SnakeSimulatorkUi main.cpp qml.qrc)
+add_executable(SnakeSimulatorkUi main.cpp qml.qrc clientwrapper.cpp)
 target_link_libraries(SnakeSimulatorkUi Qt5::Core Qt5::Gui Qt5::Qml Qt5::Quick QtProtobufProject::QtProtobuf QtProtobufProject::QtGrpc ${QtProtobuf_GENERATED})

+ 1 - 0
neuralnetwork/snakesimulator/snakesimulatorui/clientwrapper.cpp

@@ -0,0 +1 @@
+#include "clientwrapper.h"

+ 21 - 0
neuralnetwork/snakesimulator/snakesimulatorui/clientwrapper.h

@@ -0,0 +1,21 @@
+#ifndef CLIENTWRAPPER_H
+#define CLIENTWRAPPER_H
+
+#include <QObject>
+#include "snakesimulatorclient.h"
+
+class ClientWrapper : public QObject
+{
+    Q_OBJECT
+public:
+    ClientWrapper(snakesimulator::SnakeSimulatorClient* client) :
+        m_client(client){}
+
+    Q_INVOKABLE void setSpeed(int speed) {
+        m_client->setSpeed({(QtProtobuf::uint32)speed, nullptr});
+    }
+private:
+    snakesimulator::SnakeSimulatorClient* m_client;
+};
+
+#endif // CLIENTWRAPPER_H

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

@@ -31,6 +31,7 @@
 #include "qgrpchttp2channel.h"
 #include "insecurecredentials.h"
 #include "snakesimulatorclient.h"
+#include "clientwrapper.h"
 
 class NoneCredencials : public QtProtobuf::CallCredentials
 {
@@ -43,10 +44,13 @@ int main(int argc, char *argv[])
 {
     QGuiApplication app(argc, argv);
 
+    qmlRegisterUncreatableType<QtProtobuf::QGrpcAsyncReply>("snakesimulator", 1, 0, "QGrpcAsyncReply", "");
     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);
 
+    ClientWrapper *wrap = new ClientWrapper(client.get());
+
     snakesimulator::Snake *snake = new snakesimulator::Snake;
     snakesimulator::Field *field = new snakesimulator::Field;
     snakesimulator::Stats *stats = new snakesimulator::Stats;
@@ -71,6 +75,7 @@ int main(int argc, char *argv[])
     engine.rootContext()->setContextProperty("field", field);
     engine.rootContext()->setContextProperty("snake", snake);
     engine.rootContext()->setContextProperty("stats", stats);
+    engine.rootContext()->setContextProperty("client", wrap);
     engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
     if (engine.rootObjects().isEmpty())
         return -1;

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

@@ -27,6 +27,8 @@ import QtQuick 2.11
 import QtQuick.Window 2.11
 import QtQuick.Controls 1.4
 
+import snakesimulator 1.0
+
 ApplicationWindow {
     id: root
     visible: true
@@ -76,6 +78,11 @@ ApplicationWindow {
             color: "#ddffee"
             text: "Move: " + stats.move
         }
+        TextField {
+            onAccepted:  {
+                client.setSpeed(parseInt(text, 10))
+            }
+        }
     }
 
     Connections {