BIG WIP of refactoring my work in TS
authorDavid MAECHLER <d.maechler@thecodingmachine.com>
Tue, 7 Apr 2020 08:08:04 +0000 (10:08 +0200)
committerDavid MAECHLER <d.maechler@thecodingmachine.com>
Tue, 7 Apr 2020 08:08:04 +0000 (10:08 +0200)
12 files changed:
back/position-test.js [moved from back/src/Controller/position.js with 100% similarity]
back/src/Controller/IoSocketController.ts
back/src/Controller/PositionController.ts
back/src/Model/Distance.ts [new file with mode: 0644]
back/src/Model/Group.ts [new file with mode: 0644]
back/src/Model/UserInterface.ts [new file with mode: 0644]
back/src/Model/Websocket/Group.ts [new file with mode: 0644]
back/src/Model/Websocket/Message.ts
back/src/Model/Websocket/MessageUserPosition.ts
back/src/Model/World.ts [new file with mode: 0644]
back/tests/MessageTest.ts
back/tests/WorldTest.ts [new file with mode: 0644]

index aa5dfdc9fe3044b069b68f096de5650165f9129b..06e0cb11f9be513cacf0600cdcd0bab8bd1c8f24 100644 (file)
@@ -87,7 +87,8 @@ export class IoSocketController{
     //Hydrate and manage error
     hydrateMessageReceive(message : string) : MessageUserPosition | Error{
         try {
-            return new MessageUserPosition(message);
+            let data = JSON.parse(message);
+            return new MessageUserPosition(data);
         }catch (err) {
             //TODO log error
             return new Error(err);
index 0ffdd02fcbce683e436c0030ffe0517135c6ceda..f22f0917811d9d8eb3f3ba0db142184c9b6c76a9 100644 (file)
@@ -1 +1,130 @@
-// TODO
\ No newline at end of file
+import {MessageUserPosition} from "_Model/Websocket/MessageUserPosition";
+import {Distance} from "_Model/Distance";
+
+export class PositionController {
+    static readonly MIN_DISTANCE = 12;
+    static readonly MAX_PER_GROUP = 3;
+
+    constructor ()
+    {
+        // Injecter socket ?
+    }
+
+    getDistancesBetweenAllUsers(users: MessageUserPosition[]): Distance[]
+    {
+        let i = 0;
+        let distances: Distance[] = [];
+        users.forEach(function(user1, key1) {
+            users.forEach(function(user2, key2) {
+                if(key1 < key2) {
+                    distances[i] = {
+                        distance: PositionController.computeDistance(user1, user2),
+                        user1: user1,
+                        user2: user2
+                    };
+                    i++;
+                }
+            });
+        });
+
+        distances.sort(PositionController.compareDistances);
+
+        return distances;
+    }
+
+    createGroups(distances: Distance[], nbOfUsers: number, Oldgroups: MessageUserPosition[][]): MessageUserPosition[][]
+    {
+        // TODO : detect in existing groups if a user must be removed from the group
+        let alreadyInAGroup: any[string] = [];
+        let groups: MessageUserPosition[][] = [];
+
+        let roomId = 0;
+        for(let i = 0; i < distances.length; i++) {
+            let dist = distances[i];
+
+            if(dist.distance <= PositionController.MIN_DISTANCE) {
+                if(typeof groups[roomId] === 'undefined') {
+                    groups[roomId] = [];
+                }
+
+                if(groups[roomId].indexOf(dist.user1) === -1 && typeof alreadyInAGroup[dist.user1.userId] === 'undefined') {
+                    if(groups[roomId].length > 1) {
+                        // if group is not empty we check current user can be added in the group according to its distance to the others already in it
+                        for(let j = 0; j < groups[roomId].length; j++) {
+                            let userTotest = groups[roomId][j];
+                            if(PositionController.computeDistance(dist.user1, userTotest) <= PositionController.MIN_DISTANCE) {
+                                groups[roomId].push(dist.user1);
+                                alreadyInAGroup[dist.user1.userId] = true;
+                                break;
+                            }
+                        }
+                    } else {
+                        groups[roomId].push(dist.user1);
+                        alreadyInAGroup[dist.user1.userId] = true;
+                    }
+                }
+
+                if(groups[roomId].length === PositionController.MAX_PER_GROUP) {
+                    roomId++; // on créé un nouveau groupe
+                    if(roomId > (nbOfUsers / PositionController.MAX_PER_GROUP)) {
+                        console.log('There is no room left for user ID : ' + dist.user2.userId + ' !');
+                        break;
+                    }
+                    continue;
+                }
+
+                if(groups[roomId].indexOf(dist.user2) === -1 && typeof alreadyInAGroup[dist.user2.userId] === 'undefined') {
+                    if(groups[roomId].length > 1) {
+                        // if group is not empty we check current user can be added in the group according to its distance to the others already in it
+                        for(let j = 0; j < groups[roomId].length; j++) {
+                            let userTotest = groups[roomId][j];
+                            if(PositionController.computeDistance(dist.user2, userTotest) <= PositionController.MIN_DISTANCE) {
+                                groups[roomId].push(dist.user2);
+                                alreadyInAGroup[dist.user2.userId] = true;
+                                break;
+                            }
+                        }
+                    } else {
+                        groups[roomId].push(dist.user2);
+                        alreadyInAGroup[dist.user2.userId] = true;
+                    }
+                }
+            }
+        }
+        return groups;
+    }
+
+    // FIXME
+    checkGroupDistance (groups: MessageUserPosition[][])
+    {
+        for(let i = 0; i < groups.length; i++) {
+            let group = groups[i];
+            group.forEach((user1, key1) => {
+                group.forEach((user2, key2) => {
+                    if(key1 < key2) {
+                        let distance = PositionController.computeDistance(user1, user2);
+                        if(distance > PositionController.MIN_DISTANCE) {
+                            // TODO : message a user1 et user2
+                        }
+                    }
+                });
+            });
+        }
+    }
+
+    private static computeDistance(user1: MessageUserPosition, user2: MessageUserPosition): number
+    {
+        return Math.sqrt(Math.pow(user2.position.x - user1.position.x, 2) + Math.pow(user2.position.y - user1.position.y, 2));
+    }
+
+    private static compareDistances(distA: Distance, distB: Distance): number
+    {
+        if (distA.distance < distB.distance) {
+            return -1;
+        }
+        if (distA.distance > distB.distance) {
+            return 1;
+        }
+        return 0;
+    }
+}
\ No newline at end of file
diff --git a/back/src/Model/Distance.ts b/back/src/Model/Distance.ts
new file mode 100644 (file)
index 0000000..0abde38
--- /dev/null
@@ -0,0 +1,7 @@
+import {MessageUserPosition} from "../Model/Websocket/MessageUserPosition";
+
+export interface Distance {
+    distance: number,
+    user1: MessageUserPosition,
+    user2: MessageUserPosition,
+}
\ No newline at end of file
diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts
new file mode 100644 (file)
index 0000000..208615d
--- /dev/null
@@ -0,0 +1,18 @@
+import {MessageUserPosition} from "./Websocket/MessageUserPosition";
+export class Group {
+    static readonly MAX_PER_GROUP = 4;
+    
+    users: MessageUserPosition[];
+
+    constructor(users: MessageUserPosition[]) {
+        this.users = users;
+    }
+
+    getUsers(): MessageUserPosition[] {
+        return this.users;
+    }
+
+    isFull(): boolean {
+        return this.users.length >= Group.MAX_PER_GROUP;
+    }
+}
\ No newline at end of file
diff --git a/back/src/Model/UserInterface.ts b/back/src/Model/UserInterface.ts
new file mode 100644 (file)
index 0000000..c75d3f3
--- /dev/null
@@ -0,0 +1,7 @@
+import { Group } from "./Group";
+import { PointInterface } from "./Websocket/PointInterface";
+
+export interface Userinteface {
+    group: Group,
+    pointInterface: PointInterface
+}
\ No newline at end of file
diff --git a/back/src/Model/Websocket/Group.ts b/back/src/Model/Websocket/Group.ts
new file mode 100644 (file)
index 0000000..1daa8d6
--- /dev/null
@@ -0,0 +1,18 @@
+import {MessageUserPosition} from "./MessageUserPosition";
+export class Group {
+    static readonly MAX_PER_GROUP = 4;
+    
+    users: MessageUserPosition[];
+
+    constructor(users: MessageUserPosition[]) {
+        this.users = users;
+    }
+
+    getUsers(): MessageUserPosition[] {
+        return this.users;
+    }
+
+    isFull(): boolean {
+        return this.users.length >= Group.MAX_PER_GROUP;
+    }
+}
\ No newline at end of file
index 8e6f2c9a0748cc18360477e26e8d8ea1b1fc1a15..d726968fa19513e08cfc76b656196f641aef520a 100644 (file)
@@ -2,8 +2,7 @@ export class Message {
     userId: string;
     roomId: string;
 
-    constructor(message: string) {
-        let data = JSON.parse(message);
+    constructor(data: any) {
         if(!data.userId || !data.roomId){
             throw Error("userId or roomId cannot be null");
         }
index 493f1457b02261d6364f4426cafe134a2c569989..2e79f5efa10e6f0867804314b2673dd5b4b17bc8 100644 (file)
@@ -24,9 +24,8 @@ export class Point implements PointInterface{
 export class MessageUserPosition extends Message{
     position: PointInterface;
 
-    constructor(message: string) {
-        super(message);
-        let data = JSON.parse(message);
+    constructor(data: any) {
+        super(data);
         this.position = new Point(data.position.x, data.position.y);
     }
 
diff --git a/back/src/Model/World.ts b/back/src/Model/World.ts
new file mode 100644 (file)
index 0000000..e9bd547
--- /dev/null
@@ -0,0 +1,46 @@
+import {MessageUserPosition} from "./Websocket/MessageUserPosition";
+import {PointInterface} from "./Websocket/PointInterface";
+import {Group} from "./Group";
+
+export class World {
+    // Users, sorted by ID
+    private users: Map<string, PointInterface>;
+    private groups: Group[]
+    private connectCallback: (user1: string, user2: string) => void;
+    private disconnectCallback: (user1: string, user2: string) => void;
+
+    constructor(connectCallback: (user1: string, user2: string) => void, disconnectCallback: (user1: string, user2: string) => void) 
+    {
+        this.users = new Map<string, PointInterface>();
+        this.groups = [];
+        this.connectCallback = connectCallback;
+        this.disconnectCallback = disconnectCallback;
+    }    
+
+    public join(userPosition: MessageUserPosition): void {
+        this.users.set(userPosition.userId, userPosition.position);
+    }
+
+    public updatePosition(userPosition: MessageUserPosition): void {
+        if(typeof userPosition.userId === 'undefined') {
+            throw new Error('unkown id');
+        }
+        //this.users.get(userPosition.userId).x;
+
+        // TODO: compute distance between peers.
+
+        // Is the user in a group?
+
+        // Is the user leaving the group? (is the user at more than max distance of each player)
+
+        // Should we split the group? (is each player reachable from the current player?)
+                // This is needed if
+                //         A <==> B <==> C <===> D
+                // becomes A <==> B <=====> C <> D
+                // If C moves right, the distance between B and C is too great and we must form 2 groups
+
+        // If the user is in no group
+        //    is there someone in a group close enough and with room in the group?
+    }
+
+}
\ No newline at end of file
index 69d57fce742a6ed1a0eb15999873ef82921e116f..070ddf57f8e4830608228cf681edca0e2c997a36 100644 (file)
@@ -3,27 +3,27 @@ import {Message} from "../src/Model/Websocket/Message";
 
 describe("Message Model", () => {
     it("should find userId and roomId", () => {
-        let message = JSON.stringify({userId: "test1", roomId: "test2"});
+        let message = {userId: "test1", roomId: "test2"};
         let messageObject = new Message(message);
         expect(messageObject.userId).toBe("test1");
         expect(messageObject.roomId).toBe("test2");
     })
 
     it("should expose a toJson method", () => {
-        let message = JSON.stringify({userId: "test1", roomId: "test2"});
+        let message = {userId: "test1", roomId: "test2"};
         let messageObject = new Message(message);
         expect(messageObject.toJson()).toEqual({userId: "test1", roomId: "test2"});
     })
 
     it("should find throw error when no userId", () => {
-        let message = JSON.stringify({roomId: "test2"});
+        let message = {roomId: "test2"};
         expect(() => {
             let messageObject = new Message(message);
         }).toThrow(new Error("userId or roomId cannot be null"));
     })
 
     it("should find throw error when no roomId", () => {
-        let message = JSON.stringify({userId: "test1"});
+        let message = {userId: "test1"};
         expect(() => {
             let messageObject = new Message(message);
         }).toThrow(new Error("userId or roomId cannot be null"));
diff --git a/back/tests/WorldTest.ts b/back/tests/WorldTest.ts
new file mode 100644 (file)
index 0000000..7b8c167
--- /dev/null
@@ -0,0 +1,39 @@
+import "jasmine";
+import {Message} from "../src/Model/Websocket/Message";
+import {World} from "../src/Model/World";
+import {MessageUserPosition, Point} from "../src/Model/Websocket/MessageUserPosition";
+
+describe("World", () => {
+    it("should connect user1 and user2", () => {
+        let connectCalled: boolean = false;
+        let connect = (user1: string, user2: string): void => {
+            connectCalled = true;
+        }
+        let disconnect = (user1: string, user2: string): void => {
+            
+        }
+
+        let world = new World(connect, disconnect);
+
+        world.join(new MessageUserPosition({
+            userId: "foo",
+            roomId: 1,
+            position: new Point(100, 100)
+        }));
+
+        world.join(new MessageUserPosition({
+            userId: "bar",
+            roomId: 1,
+            position: new Point(500, 100)
+        }));
+
+        world.updatePosition(new MessageUserPosition({
+            userId: "bar",
+            roomId: 1,
+            position: new Point(101, 100)
+        }));
+
+        expect(connectCalled).toBe(true);
+
+    })
+})
\ No newline at end of file