diff --git a/src/GameServer.spec.ts b/src/GameServer.spec.ts new file mode 100644 index 0000000..b105065 --- /dev/null +++ b/src/GameServer.spec.ts @@ -0,0 +1,106 @@ +// to get rid of "[ts] could not find module ..." in vs code realod the window: f1 -> type "reload window" +// official issue for this: https://github.com/Microsoft/TypeScript/issues/10346 + +import {expect} from "chai"; +import {GameServer} from "./GameServer"; +import { Direction, MazeMap, MazeTile } from "./MazeMap"; +// import { describe, it } from "mocha"; + +function createTrivialMap(): MazeMap +{ + const maze = new MazeMap("lvl1"); + let tile = new MazeTile("1"); + tile.paths.push(Direction.right); + tile.paths.push(Direction.bottom); + maze.layout.push([tile]); + tile = new MazeTile("2"); + tile.paths.push(Direction.left); + tile.paths.push(Direction.bottom); + maze.layout[0].push(tile); + + tile = new MazeTile("3"); + tile.paths.push(Direction.right); + tile.paths.push(Direction.top); + maze.layout.push([tile]); + tile = new MazeTile("4"); + tile.paths.push(Direction.left); + tile.paths.push(Direction.top); + maze.layout[1].push(tile); + + return maze; +} + +function createGameServer(): GameServer +{ + const server = new GameServer(); + server.addMap(createTrivialMap()); + return server; +} + +describe("Scenario: Starting the game", function(){ + const userID = "UserID"; + const mapID = "lvl1"; + describe("when a user starts an existing valid level", function(){ + const games = createGameServer(); + const position = games.startGame(userID, mapID); + + it("then the start position must exist", function(){ + expect(position).to.be.not.null; + }); + + it("then the start position must be remembered for the user", function(){ + const current = games.currentPosition(userID); + expect(current.id).to.equal(position.id); + }); + }); +}); + +describe("Scenario: Navigating in ongoing game", function(){ + const userID = "UserID"; + const mapID = "lvl1"; + const games = createGameServer(); + const startPosition = games.startGame(userID, mapID); + + describe("When player navigates in existing direction", function(){ + const naviagtionDirection = startPosition.paths[0]; + const nextPosition = games.navigate(userID, startPosition.id, naviagtionDirection); + it("then the new position has to have a different id", function(){ + expect(nextPosition.id).to.not.equal(startPosition.id); + }); + + it("then the new position must have pathway back", function(){ + let hasOpposite:boolean = false; + for (const dir of nextPosition.paths) { + switch (dir) { + case Direction.top: + if(naviagtionDirection === Direction.bottom) + { + hasOpposite = true; + } + break; + case Direction.left: + if(naviagtionDirection === Direction.right) + { + hasOpposite = true; + } + break; + case Direction.bottom: + if(naviagtionDirection === Direction.top) + { + hasOpposite = true; + } + break; + case Direction.right: + if(naviagtionDirection === Direction.left) + { + hasOpposite = true; + } + break; + default: + throw new Error(`Direction "${dir}" not recognized.`); + } + } + expect(hasOpposite).to.be.true; + }); + }); +}); diff --git a/src/GameServer.spec.ts b/src/GameServer.spec.ts new file mode 100644 index 0000000..b105065 --- /dev/null +++ b/src/GameServer.spec.ts @@ -0,0 +1,106 @@ +// to get rid of "[ts] could not find module ..." in vs code realod the window: f1 -> type "reload window" +// official issue for this: https://github.com/Microsoft/TypeScript/issues/10346 + +import {expect} from "chai"; +import {GameServer} from "./GameServer"; +import { Direction, MazeMap, MazeTile } from "./MazeMap"; +// import { describe, it } from "mocha"; + +function createTrivialMap(): MazeMap +{ + const maze = new MazeMap("lvl1"); + let tile = new MazeTile("1"); + tile.paths.push(Direction.right); + tile.paths.push(Direction.bottom); + maze.layout.push([tile]); + tile = new MazeTile("2"); + tile.paths.push(Direction.left); + tile.paths.push(Direction.bottom); + maze.layout[0].push(tile); + + tile = new MazeTile("3"); + tile.paths.push(Direction.right); + tile.paths.push(Direction.top); + maze.layout.push([tile]); + tile = new MazeTile("4"); + tile.paths.push(Direction.left); + tile.paths.push(Direction.top); + maze.layout[1].push(tile); + + return maze; +} + +function createGameServer(): GameServer +{ + const server = new GameServer(); + server.addMap(createTrivialMap()); + return server; +} + +describe("Scenario: Starting the game", function(){ + const userID = "UserID"; + const mapID = "lvl1"; + describe("when a user starts an existing valid level", function(){ + const games = createGameServer(); + const position = games.startGame(userID, mapID); + + it("then the start position must exist", function(){ + expect(position).to.be.not.null; + }); + + it("then the start position must be remembered for the user", function(){ + const current = games.currentPosition(userID); + expect(current.id).to.equal(position.id); + }); + }); +}); + +describe("Scenario: Navigating in ongoing game", function(){ + const userID = "UserID"; + const mapID = "lvl1"; + const games = createGameServer(); + const startPosition = games.startGame(userID, mapID); + + describe("When player navigates in existing direction", function(){ + const naviagtionDirection = startPosition.paths[0]; + const nextPosition = games.navigate(userID, startPosition.id, naviagtionDirection); + it("then the new position has to have a different id", function(){ + expect(nextPosition.id).to.not.equal(startPosition.id); + }); + + it("then the new position must have pathway back", function(){ + let hasOpposite:boolean = false; + for (const dir of nextPosition.paths) { + switch (dir) { + case Direction.top: + if(naviagtionDirection === Direction.bottom) + { + hasOpposite = true; + } + break; + case Direction.left: + if(naviagtionDirection === Direction.right) + { + hasOpposite = true; + } + break; + case Direction.bottom: + if(naviagtionDirection === Direction.top) + { + hasOpposite = true; + } + break; + case Direction.right: + if(naviagtionDirection === Direction.left) + { + hasOpposite = true; + } + break; + default: + throw new Error(`Direction "${dir}" not recognized.`); + } + } + expect(hasOpposite).to.be.true; + }); + }); +}); diff --git a/src/GameServer.ts b/src/GameServer.ts new file mode 100644 index 0000000..c0479cf --- /dev/null +++ b/src/GameServer.ts @@ -0,0 +1,94 @@ +import { + Direction, + MazeMap, + MazeTile, +} from "./MazeMap"; + +class GameState +{ + public userID: string; + public mapID: string; + public tileID: string; + + constructor(userID: string, mapID: string, tileID: string) + { + this.userID = userID; + this.mapID = mapID; + this.tileID = tileID; + } +} + +export class GameServer +{ + private mapList: MazeMap[]; + private states: GameState[]; + + constructor() + { + this.mapList = []; + this.states = []; + } + + public addMap(newMap: MazeMap): boolean + { + const validationResult = newMap.validate(); + if(validationResult.length > 0){ + let message = "Cannot add invalid map:"; + for (const e of validationResult) { + message += "\n\t" + e; + } + throw new Error(message); + } + + if(-1 === this.mapList.findIndex(m => m.id === newMap.id)) + { + this.mapList.push(newMap); + return true; + } + else { + return false; + } + } + + public startGame(userID: string, mapID: string): MazeTile + { + const map = this.mapList.find(m => m.id === mapID); + if(map == null){ + throw new Error(`Map "${mapID}" does not exist.`); + } + const start = map.getStartTile(); + + let state = this.states.find(s => s.userID === userID); + if(state == null) + { + state = new GameState(userID, map.id, start.id); + this.states.push(state); + } + else + { + state.mapID = map.id; + state.tileID = start.id; + } + + return start; + } + + public navigate(userID: string, currentTile:string, direction:Direction): MazeTile + { + const newPosition = new MazeTile(""); + newPosition.paths.push(Direction.left); + return newPosition; + } + + public currentPosition(userID: string): MazeTile + { + const state = this.states.find(s => s.userID === userID); + if(state == null){ + return null; + } + + const map = this.mapList.find(m => m.id === state.mapID); + const tile = map.getTile(state.tileID); + return tile; + } +} diff --git a/src/GameServer.spec.ts b/src/GameServer.spec.ts new file mode 100644 index 0000000..b105065 --- /dev/null +++ b/src/GameServer.spec.ts @@ -0,0 +1,106 @@ +// to get rid of "[ts] could not find module ..." in vs code realod the window: f1 -> type "reload window" +// official issue for this: https://github.com/Microsoft/TypeScript/issues/10346 + +import {expect} from "chai"; +import {GameServer} from "./GameServer"; +import { Direction, MazeMap, MazeTile } from "./MazeMap"; +// import { describe, it } from "mocha"; + +function createTrivialMap(): MazeMap +{ + const maze = new MazeMap("lvl1"); + let tile = new MazeTile("1"); + tile.paths.push(Direction.right); + tile.paths.push(Direction.bottom); + maze.layout.push([tile]); + tile = new MazeTile("2"); + tile.paths.push(Direction.left); + tile.paths.push(Direction.bottom); + maze.layout[0].push(tile); + + tile = new MazeTile("3"); + tile.paths.push(Direction.right); + tile.paths.push(Direction.top); + maze.layout.push([tile]); + tile = new MazeTile("4"); + tile.paths.push(Direction.left); + tile.paths.push(Direction.top); + maze.layout[1].push(tile); + + return maze; +} + +function createGameServer(): GameServer +{ + const server = new GameServer(); + server.addMap(createTrivialMap()); + return server; +} + +describe("Scenario: Starting the game", function(){ + const userID = "UserID"; + const mapID = "lvl1"; + describe("when a user starts an existing valid level", function(){ + const games = createGameServer(); + const position = games.startGame(userID, mapID); + + it("then the start position must exist", function(){ + expect(position).to.be.not.null; + }); + + it("then the start position must be remembered for the user", function(){ + const current = games.currentPosition(userID); + expect(current.id).to.equal(position.id); + }); + }); +}); + +describe("Scenario: Navigating in ongoing game", function(){ + const userID = "UserID"; + const mapID = "lvl1"; + const games = createGameServer(); + const startPosition = games.startGame(userID, mapID); + + describe("When player navigates in existing direction", function(){ + const naviagtionDirection = startPosition.paths[0]; + const nextPosition = games.navigate(userID, startPosition.id, naviagtionDirection); + it("then the new position has to have a different id", function(){ + expect(nextPosition.id).to.not.equal(startPosition.id); + }); + + it("then the new position must have pathway back", function(){ + let hasOpposite:boolean = false; + for (const dir of nextPosition.paths) { + switch (dir) { + case Direction.top: + if(naviagtionDirection === Direction.bottom) + { + hasOpposite = true; + } + break; + case Direction.left: + if(naviagtionDirection === Direction.right) + { + hasOpposite = true; + } + break; + case Direction.bottom: + if(naviagtionDirection === Direction.top) + { + hasOpposite = true; + } + break; + case Direction.right: + if(naviagtionDirection === Direction.left) + { + hasOpposite = true; + } + break; + default: + throw new Error(`Direction "${dir}" not recognized.`); + } + } + expect(hasOpposite).to.be.true; + }); + }); +}); diff --git a/src/GameServer.ts b/src/GameServer.ts new file mode 100644 index 0000000..c0479cf --- /dev/null +++ b/src/GameServer.ts @@ -0,0 +1,94 @@ +import { + Direction, + MazeMap, + MazeTile, +} from "./MazeMap"; + +class GameState +{ + public userID: string; + public mapID: string; + public tileID: string; + + constructor(userID: string, mapID: string, tileID: string) + { + this.userID = userID; + this.mapID = mapID; + this.tileID = tileID; + } +} + +export class GameServer +{ + private mapList: MazeMap[]; + private states: GameState[]; + + constructor() + { + this.mapList = []; + this.states = []; + } + + public addMap(newMap: MazeMap): boolean + { + const validationResult = newMap.validate(); + if(validationResult.length > 0){ + let message = "Cannot add invalid map:"; + for (const e of validationResult) { + message += "\n\t" + e; + } + throw new Error(message); + } + + if(-1 === this.mapList.findIndex(m => m.id === newMap.id)) + { + this.mapList.push(newMap); + return true; + } + else { + return false; + } + } + + public startGame(userID: string, mapID: string): MazeTile + { + const map = this.mapList.find(m => m.id === mapID); + if(map == null){ + throw new Error(`Map "${mapID}" does not exist.`); + } + const start = map.getStartTile(); + + let state = this.states.find(s => s.userID === userID); + if(state == null) + { + state = new GameState(userID, map.id, start.id); + this.states.push(state); + } + else + { + state.mapID = map.id; + state.tileID = start.id; + } + + return start; + } + + public navigate(userID: string, currentTile:string, direction:Direction): MazeTile + { + const newPosition = new MazeTile(""); + newPosition.paths.push(Direction.left); + return newPosition; + } + + public currentPosition(userID: string): MazeTile + { + const state = this.states.find(s => s.userID === userID); + if(state == null){ + return null; + } + + const map = this.mapList.find(m => m.id === state.mapID); + const tile = map.getTile(state.tileID); + return tile; + } +} diff --git a/src/MazeMap.ts b/src/MazeMap.ts index 5265cdf..7110157 100644 --- a/src/MazeMap.ts +++ b/src/MazeMap.ts @@ -124,6 +124,23 @@ return output; } + public getTile(tileID: string): MazeTile + { + for (const row of this.layout) { + for (const tile of row) { + if(tile.id === tileID){ + return tile; + } + } + } + return null; + } + + public getStartTile(): MazeTile + { + return this.layout[this.startPosition[0]][this.startPosition[1]]; + } + public validate(): MazeValidationError[] { const errors:MazeValidationError[] = []; diff --git a/src/GameServer.spec.ts b/src/GameServer.spec.ts new file mode 100644 index 0000000..b105065 --- /dev/null +++ b/src/GameServer.spec.ts @@ -0,0 +1,106 @@ +// to get rid of "[ts] could not find module ..." in vs code realod the window: f1 -> type "reload window" +// official issue for this: https://github.com/Microsoft/TypeScript/issues/10346 + +import {expect} from "chai"; +import {GameServer} from "./GameServer"; +import { Direction, MazeMap, MazeTile } from "./MazeMap"; +// import { describe, it } from "mocha"; + +function createTrivialMap(): MazeMap +{ + const maze = new MazeMap("lvl1"); + let tile = new MazeTile("1"); + tile.paths.push(Direction.right); + tile.paths.push(Direction.bottom); + maze.layout.push([tile]); + tile = new MazeTile("2"); + tile.paths.push(Direction.left); + tile.paths.push(Direction.bottom); + maze.layout[0].push(tile); + + tile = new MazeTile("3"); + tile.paths.push(Direction.right); + tile.paths.push(Direction.top); + maze.layout.push([tile]); + tile = new MazeTile("4"); + tile.paths.push(Direction.left); + tile.paths.push(Direction.top); + maze.layout[1].push(tile); + + return maze; +} + +function createGameServer(): GameServer +{ + const server = new GameServer(); + server.addMap(createTrivialMap()); + return server; +} + +describe("Scenario: Starting the game", function(){ + const userID = "UserID"; + const mapID = "lvl1"; + describe("when a user starts an existing valid level", function(){ + const games = createGameServer(); + const position = games.startGame(userID, mapID); + + it("then the start position must exist", function(){ + expect(position).to.be.not.null; + }); + + it("then the start position must be remembered for the user", function(){ + const current = games.currentPosition(userID); + expect(current.id).to.equal(position.id); + }); + }); +}); + +describe("Scenario: Navigating in ongoing game", function(){ + const userID = "UserID"; + const mapID = "lvl1"; + const games = createGameServer(); + const startPosition = games.startGame(userID, mapID); + + describe("When player navigates in existing direction", function(){ + const naviagtionDirection = startPosition.paths[0]; + const nextPosition = games.navigate(userID, startPosition.id, naviagtionDirection); + it("then the new position has to have a different id", function(){ + expect(nextPosition.id).to.not.equal(startPosition.id); + }); + + it("then the new position must have pathway back", function(){ + let hasOpposite:boolean = false; + for (const dir of nextPosition.paths) { + switch (dir) { + case Direction.top: + if(naviagtionDirection === Direction.bottom) + { + hasOpposite = true; + } + break; + case Direction.left: + if(naviagtionDirection === Direction.right) + { + hasOpposite = true; + } + break; + case Direction.bottom: + if(naviagtionDirection === Direction.top) + { + hasOpposite = true; + } + break; + case Direction.right: + if(naviagtionDirection === Direction.left) + { + hasOpposite = true; + } + break; + default: + throw new Error(`Direction "${dir}" not recognized.`); + } + } + expect(hasOpposite).to.be.true; + }); + }); +}); diff --git a/src/GameServer.ts b/src/GameServer.ts new file mode 100644 index 0000000..c0479cf --- /dev/null +++ b/src/GameServer.ts @@ -0,0 +1,94 @@ +import { + Direction, + MazeMap, + MazeTile, +} from "./MazeMap"; + +class GameState +{ + public userID: string; + public mapID: string; + public tileID: string; + + constructor(userID: string, mapID: string, tileID: string) + { + this.userID = userID; + this.mapID = mapID; + this.tileID = tileID; + } +} + +export class GameServer +{ + private mapList: MazeMap[]; + private states: GameState[]; + + constructor() + { + this.mapList = []; + this.states = []; + } + + public addMap(newMap: MazeMap): boolean + { + const validationResult = newMap.validate(); + if(validationResult.length > 0){ + let message = "Cannot add invalid map:"; + for (const e of validationResult) { + message += "\n\t" + e; + } + throw new Error(message); + } + + if(-1 === this.mapList.findIndex(m => m.id === newMap.id)) + { + this.mapList.push(newMap); + return true; + } + else { + return false; + } + } + + public startGame(userID: string, mapID: string): MazeTile + { + const map = this.mapList.find(m => m.id === mapID); + if(map == null){ + throw new Error(`Map "${mapID}" does not exist.`); + } + const start = map.getStartTile(); + + let state = this.states.find(s => s.userID === userID); + if(state == null) + { + state = new GameState(userID, map.id, start.id); + this.states.push(state); + } + else + { + state.mapID = map.id; + state.tileID = start.id; + } + + return start; + } + + public navigate(userID: string, currentTile:string, direction:Direction): MazeTile + { + const newPosition = new MazeTile(""); + newPosition.paths.push(Direction.left); + return newPosition; + } + + public currentPosition(userID: string): MazeTile + { + const state = this.states.find(s => s.userID === userID); + if(state == null){ + return null; + } + + const map = this.mapList.find(m => m.id === state.mapID); + const tile = map.getTile(state.tileID); + return tile; + } +} diff --git a/src/MazeMap.ts b/src/MazeMap.ts index 5265cdf..7110157 100644 --- a/src/MazeMap.ts +++ b/src/MazeMap.ts @@ -124,6 +124,23 @@ return output; } + public getTile(tileID: string): MazeTile + { + for (const row of this.layout) { + for (const tile of row) { + if(tile.id === tileID){ + return tile; + } + } + } + return null; + } + + public getStartTile(): MazeTile + { + return this.layout[this.startPosition[0]][this.startPosition[1]]; + } + public validate(): MazeValidationError[] { const errors:MazeValidationError[] = []; diff --git a/src/NavigationEndpoint.spec.ts b/src/NavigationEndpoint.spec.ts deleted file mode 100644 index 83588f1..0000000 --- a/src/NavigationEndpoint.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -// to get rid of "[ts] could not find module ..." in vs code realod the window: f1 -> type "reload window" -// official issue for this: https://github.com/Microsoft/TypeScript/issues/10346 - -import {expect} from "chai"; -import { Direction } from "./MazeMap"; -// import { describe, it } from "mocha"; -import {navigate, startGame} from "./NavigationEndpoint"; - -describe("Scenario: Starting the game", function(){ - describe("when the game is started", function(){ - const position = startGame(); - - it("then the start position must exist", function(){ - expect(position).to.be.not.null; - }); - - it("then the position must have at least one pathway", function(){ - expect(position.paths.length).to.be.greaterThan(0); - }); - }); -}); - -describe("Scenario: Navigating in ongoing game", function(){ - const startPosition = startGame(); - - describe("When player navigates in existing direction", function(){ - const naviagtionDirection = startPosition.paths[0]; - const nextPosition = navigate(startPosition.id, naviagtionDirection); - it("then the new position has to have a different id", function(){ - expect(nextPosition.id).to.not.equal(startPosition.id); - }); - - it("then the new position must have pathway back", function(){ - let hasOpposite:boolean = false; - for (const dir of nextPosition.paths) { - switch (dir) { - case Direction.top: - if(naviagtionDirection === Direction.bottom) - { - hasOpposite = true; - } - break; - case Direction.left: - if(naviagtionDirection === Direction.right) - { - hasOpposite = true; - } - break; - case Direction.bottom: - if(naviagtionDirection === Direction.top) - { - hasOpposite = true; - } - break; - case Direction.right: - if(naviagtionDirection === Direction.left) - { - hasOpposite = true; - } - break; - default: - throw new Error(`Direction "${dir}" not recognized.`); - } - } - expect(hasOpposite).to.be.true; - }); - }); -}); diff --git a/src/GameServer.spec.ts b/src/GameServer.spec.ts new file mode 100644 index 0000000..b105065 --- /dev/null +++ b/src/GameServer.spec.ts @@ -0,0 +1,106 @@ +// to get rid of "[ts] could not find module ..." in vs code realod the window: f1 -> type "reload window" +// official issue for this: https://github.com/Microsoft/TypeScript/issues/10346 + +import {expect} from "chai"; +import {GameServer} from "./GameServer"; +import { Direction, MazeMap, MazeTile } from "./MazeMap"; +// import { describe, it } from "mocha"; + +function createTrivialMap(): MazeMap +{ + const maze = new MazeMap("lvl1"); + let tile = new MazeTile("1"); + tile.paths.push(Direction.right); + tile.paths.push(Direction.bottom); + maze.layout.push([tile]); + tile = new MazeTile("2"); + tile.paths.push(Direction.left); + tile.paths.push(Direction.bottom); + maze.layout[0].push(tile); + + tile = new MazeTile("3"); + tile.paths.push(Direction.right); + tile.paths.push(Direction.top); + maze.layout.push([tile]); + tile = new MazeTile("4"); + tile.paths.push(Direction.left); + tile.paths.push(Direction.top); + maze.layout[1].push(tile); + + return maze; +} + +function createGameServer(): GameServer +{ + const server = new GameServer(); + server.addMap(createTrivialMap()); + return server; +} + +describe("Scenario: Starting the game", function(){ + const userID = "UserID"; + const mapID = "lvl1"; + describe("when a user starts an existing valid level", function(){ + const games = createGameServer(); + const position = games.startGame(userID, mapID); + + it("then the start position must exist", function(){ + expect(position).to.be.not.null; + }); + + it("then the start position must be remembered for the user", function(){ + const current = games.currentPosition(userID); + expect(current.id).to.equal(position.id); + }); + }); +}); + +describe("Scenario: Navigating in ongoing game", function(){ + const userID = "UserID"; + const mapID = "lvl1"; + const games = createGameServer(); + const startPosition = games.startGame(userID, mapID); + + describe("When player navigates in existing direction", function(){ + const naviagtionDirection = startPosition.paths[0]; + const nextPosition = games.navigate(userID, startPosition.id, naviagtionDirection); + it("then the new position has to have a different id", function(){ + expect(nextPosition.id).to.not.equal(startPosition.id); + }); + + it("then the new position must have pathway back", function(){ + let hasOpposite:boolean = false; + for (const dir of nextPosition.paths) { + switch (dir) { + case Direction.top: + if(naviagtionDirection === Direction.bottom) + { + hasOpposite = true; + } + break; + case Direction.left: + if(naviagtionDirection === Direction.right) + { + hasOpposite = true; + } + break; + case Direction.bottom: + if(naviagtionDirection === Direction.top) + { + hasOpposite = true; + } + break; + case Direction.right: + if(naviagtionDirection === Direction.left) + { + hasOpposite = true; + } + break; + default: + throw new Error(`Direction "${dir}" not recognized.`); + } + } + expect(hasOpposite).to.be.true; + }); + }); +}); diff --git a/src/GameServer.ts b/src/GameServer.ts new file mode 100644 index 0000000..c0479cf --- /dev/null +++ b/src/GameServer.ts @@ -0,0 +1,94 @@ +import { + Direction, + MazeMap, + MazeTile, +} from "./MazeMap"; + +class GameState +{ + public userID: string; + public mapID: string; + public tileID: string; + + constructor(userID: string, mapID: string, tileID: string) + { + this.userID = userID; + this.mapID = mapID; + this.tileID = tileID; + } +} + +export class GameServer +{ + private mapList: MazeMap[]; + private states: GameState[]; + + constructor() + { + this.mapList = []; + this.states = []; + } + + public addMap(newMap: MazeMap): boolean + { + const validationResult = newMap.validate(); + if(validationResult.length > 0){ + let message = "Cannot add invalid map:"; + for (const e of validationResult) { + message += "\n\t" + e; + } + throw new Error(message); + } + + if(-1 === this.mapList.findIndex(m => m.id === newMap.id)) + { + this.mapList.push(newMap); + return true; + } + else { + return false; + } + } + + public startGame(userID: string, mapID: string): MazeTile + { + const map = this.mapList.find(m => m.id === mapID); + if(map == null){ + throw new Error(`Map "${mapID}" does not exist.`); + } + const start = map.getStartTile(); + + let state = this.states.find(s => s.userID === userID); + if(state == null) + { + state = new GameState(userID, map.id, start.id); + this.states.push(state); + } + else + { + state.mapID = map.id; + state.tileID = start.id; + } + + return start; + } + + public navigate(userID: string, currentTile:string, direction:Direction): MazeTile + { + const newPosition = new MazeTile(""); + newPosition.paths.push(Direction.left); + return newPosition; + } + + public currentPosition(userID: string): MazeTile + { + const state = this.states.find(s => s.userID === userID); + if(state == null){ + return null; + } + + const map = this.mapList.find(m => m.id === state.mapID); + const tile = map.getTile(state.tileID); + return tile; + } +} diff --git a/src/MazeMap.ts b/src/MazeMap.ts index 5265cdf..7110157 100644 --- a/src/MazeMap.ts +++ b/src/MazeMap.ts @@ -124,6 +124,23 @@ return output; } + public getTile(tileID: string): MazeTile + { + for (const row of this.layout) { + for (const tile of row) { + if(tile.id === tileID){ + return tile; + } + } + } + return null; + } + + public getStartTile(): MazeTile + { + return this.layout[this.startPosition[0]][this.startPosition[1]]; + } + public validate(): MazeValidationError[] { const errors:MazeValidationError[] = []; diff --git a/src/NavigationEndpoint.spec.ts b/src/NavigationEndpoint.spec.ts deleted file mode 100644 index 83588f1..0000000 --- a/src/NavigationEndpoint.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -// to get rid of "[ts] could not find module ..." in vs code realod the window: f1 -> type "reload window" -// official issue for this: https://github.com/Microsoft/TypeScript/issues/10346 - -import {expect} from "chai"; -import { Direction } from "./MazeMap"; -// import { describe, it } from "mocha"; -import {navigate, startGame} from "./NavigationEndpoint"; - -describe("Scenario: Starting the game", function(){ - describe("when the game is started", function(){ - const position = startGame(); - - it("then the start position must exist", function(){ - expect(position).to.be.not.null; - }); - - it("then the position must have at least one pathway", function(){ - expect(position.paths.length).to.be.greaterThan(0); - }); - }); -}); - -describe("Scenario: Navigating in ongoing game", function(){ - const startPosition = startGame(); - - describe("When player navigates in existing direction", function(){ - const naviagtionDirection = startPosition.paths[0]; - const nextPosition = navigate(startPosition.id, naviagtionDirection); - it("then the new position has to have a different id", function(){ - expect(nextPosition.id).to.not.equal(startPosition.id); - }); - - it("then the new position must have pathway back", function(){ - let hasOpposite:boolean = false; - for (const dir of nextPosition.paths) { - switch (dir) { - case Direction.top: - if(naviagtionDirection === Direction.bottom) - { - hasOpposite = true; - } - break; - case Direction.left: - if(naviagtionDirection === Direction.right) - { - hasOpposite = true; - } - break; - case Direction.bottom: - if(naviagtionDirection === Direction.top) - { - hasOpposite = true; - } - break; - case Direction.right: - if(naviagtionDirection === Direction.left) - { - hasOpposite = true; - } - break; - default: - throw new Error(`Direction "${dir}" not recognized.`); - } - } - expect(hasOpposite).to.be.true; - }); - }); -}); diff --git a/src/NavigationEndpoint.ts b/src/NavigationEndpoint.ts deleted file mode 100644 index 514cf98..0000000 --- a/src/NavigationEndpoint.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { - Direction, - MazeTile, -} from "./MazeMap"; - -export function startGame(): MazeTile -{ - const StartingPoint = new MazeTile("start"); - StartingPoint.paths.push(Direction.right); - return StartingPoint; -} - -export function navigate(currentPosition:string, direction:Direction): MazeTile -{ - const newPosition = new MazeTile(""); - newPosition.paths.push(Direction.left); - return newPosition; -} diff --git a/src/GameServer.spec.ts b/src/GameServer.spec.ts new file mode 100644 index 0000000..b105065 --- /dev/null +++ b/src/GameServer.spec.ts @@ -0,0 +1,106 @@ +// to get rid of "[ts] could not find module ..." in vs code realod the window: f1 -> type "reload window" +// official issue for this: https://github.com/Microsoft/TypeScript/issues/10346 + +import {expect} from "chai"; +import {GameServer} from "./GameServer"; +import { Direction, MazeMap, MazeTile } from "./MazeMap"; +// import { describe, it } from "mocha"; + +function createTrivialMap(): MazeMap +{ + const maze = new MazeMap("lvl1"); + let tile = new MazeTile("1"); + tile.paths.push(Direction.right); + tile.paths.push(Direction.bottom); + maze.layout.push([tile]); + tile = new MazeTile("2"); + tile.paths.push(Direction.left); + tile.paths.push(Direction.bottom); + maze.layout[0].push(tile); + + tile = new MazeTile("3"); + tile.paths.push(Direction.right); + tile.paths.push(Direction.top); + maze.layout.push([tile]); + tile = new MazeTile("4"); + tile.paths.push(Direction.left); + tile.paths.push(Direction.top); + maze.layout[1].push(tile); + + return maze; +} + +function createGameServer(): GameServer +{ + const server = new GameServer(); + server.addMap(createTrivialMap()); + return server; +} + +describe("Scenario: Starting the game", function(){ + const userID = "UserID"; + const mapID = "lvl1"; + describe("when a user starts an existing valid level", function(){ + const games = createGameServer(); + const position = games.startGame(userID, mapID); + + it("then the start position must exist", function(){ + expect(position).to.be.not.null; + }); + + it("then the start position must be remembered for the user", function(){ + const current = games.currentPosition(userID); + expect(current.id).to.equal(position.id); + }); + }); +}); + +describe("Scenario: Navigating in ongoing game", function(){ + const userID = "UserID"; + const mapID = "lvl1"; + const games = createGameServer(); + const startPosition = games.startGame(userID, mapID); + + describe("When player navigates in existing direction", function(){ + const naviagtionDirection = startPosition.paths[0]; + const nextPosition = games.navigate(userID, startPosition.id, naviagtionDirection); + it("then the new position has to have a different id", function(){ + expect(nextPosition.id).to.not.equal(startPosition.id); + }); + + it("then the new position must have pathway back", function(){ + let hasOpposite:boolean = false; + for (const dir of nextPosition.paths) { + switch (dir) { + case Direction.top: + if(naviagtionDirection === Direction.bottom) + { + hasOpposite = true; + } + break; + case Direction.left: + if(naviagtionDirection === Direction.right) + { + hasOpposite = true; + } + break; + case Direction.bottom: + if(naviagtionDirection === Direction.top) + { + hasOpposite = true; + } + break; + case Direction.right: + if(naviagtionDirection === Direction.left) + { + hasOpposite = true; + } + break; + default: + throw new Error(`Direction "${dir}" not recognized.`); + } + } + expect(hasOpposite).to.be.true; + }); + }); +}); diff --git a/src/GameServer.ts b/src/GameServer.ts new file mode 100644 index 0000000..c0479cf --- /dev/null +++ b/src/GameServer.ts @@ -0,0 +1,94 @@ +import { + Direction, + MazeMap, + MazeTile, +} from "./MazeMap"; + +class GameState +{ + public userID: string; + public mapID: string; + public tileID: string; + + constructor(userID: string, mapID: string, tileID: string) + { + this.userID = userID; + this.mapID = mapID; + this.tileID = tileID; + } +} + +export class GameServer +{ + private mapList: MazeMap[]; + private states: GameState[]; + + constructor() + { + this.mapList = []; + this.states = []; + } + + public addMap(newMap: MazeMap): boolean + { + const validationResult = newMap.validate(); + if(validationResult.length > 0){ + let message = "Cannot add invalid map:"; + for (const e of validationResult) { + message += "\n\t" + e; + } + throw new Error(message); + } + + if(-1 === this.mapList.findIndex(m => m.id === newMap.id)) + { + this.mapList.push(newMap); + return true; + } + else { + return false; + } + } + + public startGame(userID: string, mapID: string): MazeTile + { + const map = this.mapList.find(m => m.id === mapID); + if(map == null){ + throw new Error(`Map "${mapID}" does not exist.`); + } + const start = map.getStartTile(); + + let state = this.states.find(s => s.userID === userID); + if(state == null) + { + state = new GameState(userID, map.id, start.id); + this.states.push(state); + } + else + { + state.mapID = map.id; + state.tileID = start.id; + } + + return start; + } + + public navigate(userID: string, currentTile:string, direction:Direction): MazeTile + { + const newPosition = new MazeTile(""); + newPosition.paths.push(Direction.left); + return newPosition; + } + + public currentPosition(userID: string): MazeTile + { + const state = this.states.find(s => s.userID === userID); + if(state == null){ + return null; + } + + const map = this.mapList.find(m => m.id === state.mapID); + const tile = map.getTile(state.tileID); + return tile; + } +} diff --git a/src/MazeMap.ts b/src/MazeMap.ts index 5265cdf..7110157 100644 --- a/src/MazeMap.ts +++ b/src/MazeMap.ts @@ -124,6 +124,23 @@ return output; } + public getTile(tileID: string): MazeTile + { + for (const row of this.layout) { + for (const tile of row) { + if(tile.id === tileID){ + return tile; + } + } + } + return null; + } + + public getStartTile(): MazeTile + { + return this.layout[this.startPosition[0]][this.startPosition[1]]; + } + public validate(): MazeValidationError[] { const errors:MazeValidationError[] = []; diff --git a/src/NavigationEndpoint.spec.ts b/src/NavigationEndpoint.spec.ts deleted file mode 100644 index 83588f1..0000000 --- a/src/NavigationEndpoint.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -// to get rid of "[ts] could not find module ..." in vs code realod the window: f1 -> type "reload window" -// official issue for this: https://github.com/Microsoft/TypeScript/issues/10346 - -import {expect} from "chai"; -import { Direction } from "./MazeMap"; -// import { describe, it } from "mocha"; -import {navigate, startGame} from "./NavigationEndpoint"; - -describe("Scenario: Starting the game", function(){ - describe("when the game is started", function(){ - const position = startGame(); - - it("then the start position must exist", function(){ - expect(position).to.be.not.null; - }); - - it("then the position must have at least one pathway", function(){ - expect(position.paths.length).to.be.greaterThan(0); - }); - }); -}); - -describe("Scenario: Navigating in ongoing game", function(){ - const startPosition = startGame(); - - describe("When player navigates in existing direction", function(){ - const naviagtionDirection = startPosition.paths[0]; - const nextPosition = navigate(startPosition.id, naviagtionDirection); - it("then the new position has to have a different id", function(){ - expect(nextPosition.id).to.not.equal(startPosition.id); - }); - - it("then the new position must have pathway back", function(){ - let hasOpposite:boolean = false; - for (const dir of nextPosition.paths) { - switch (dir) { - case Direction.top: - if(naviagtionDirection === Direction.bottom) - { - hasOpposite = true; - } - break; - case Direction.left: - if(naviagtionDirection === Direction.right) - { - hasOpposite = true; - } - break; - case Direction.bottom: - if(naviagtionDirection === Direction.top) - { - hasOpposite = true; - } - break; - case Direction.right: - if(naviagtionDirection === Direction.left) - { - hasOpposite = true; - } - break; - default: - throw new Error(`Direction "${dir}" not recognized.`); - } - } - expect(hasOpposite).to.be.true; - }); - }); -}); diff --git a/src/NavigationEndpoint.ts b/src/NavigationEndpoint.ts deleted file mode 100644 index 514cf98..0000000 --- a/src/NavigationEndpoint.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { - Direction, - MazeTile, -} from "./MazeMap"; - -export function startGame(): MazeTile -{ - const StartingPoint = new MazeTile("start"); - StartingPoint.paths.push(Direction.right); - return StartingPoint; -} - -export function navigate(currentPosition:string, direction:Direction): MazeTile -{ - const newPosition = new MazeTile(""); - newPosition.paths.push(Direction.left); - return newPosition; -} diff --git a/tslint.json b/tslint.json index 13e0631..1ad9eda 100644 --- a/tslint.json +++ b/tslint.json @@ -13,7 +13,8 @@ "max-classes-per-file":false, "only-arrow-functions": false, "no-unused-expression": false, - "typedef": [true, "parameter", "member-variable-declaration"] + "typedef": [true, "parameter", "member-variable-declaration"], + "arrow-parens": false }, "rulesDirectory": [], "linterOptions": {