From 74d302b64c8b36d80b60f995406defeac1b5c83d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Thu, 19 Mar 2015 01:00:00 +0100 Subject: [PATCH] Added star system links generation --- src/scripts/game/StarLink.ts | 20 ++++++++ src/scripts/game/Universe.ts | 67 ++++++++++++++++++++----- src/scripts/game/specs/StarLink.spec.ts | 31 ++++++++++++ src/scripts/game/specs/Universe.spec.ts | 31 +++++++++++- src/scripts/view/map/UniverseMapView.ts | 2 +- 5 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 src/scripts/game/specs/StarLink.spec.ts diff --git a/src/scripts/game/StarLink.ts b/src/scripts/game/StarLink.ts index 61f85f9..4d73f92 100644 --- a/src/scripts/game/StarLink.ts +++ b/src/scripts/game/StarLink.ts @@ -20,5 +20,25 @@ module SpaceTac.Game { isLinking(first: Star, second: Star) { return (this.first === first && this.second === second) || (this.first === second && this.second === first); } + + // Get the length of a link + getLength(): number { + return this.first.getDistanceTo(this.second); + } + + // Check if this link crosses another + isCrossing(other: StarLink): boolean { + if (this.first === other.first || this.second === other.first || this.first === other.second || this.second === other.second) { + return false; + } + var ccw = (a: Star, b: Star, c: Star): boolean => { + return (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x); + }; + var cc1 = ccw(this.first, other.first, other.second); + var cc2 = ccw(this.second, other.first, other.second); + var cc3 = ccw(this.first, this.second, other.first); + var cc4 = ccw(this.first, this.second, other.second); + return cc1 !== cc2 && cc3 !== cc4; + } } } diff --git a/src/scripts/game/Universe.ts b/src/scripts/game/Universe.ts index 936b963..14330d3 100644 --- a/src/scripts/game/Universe.ts +++ b/src/scripts/game/Universe.ts @@ -47,40 +47,81 @@ module SpaceTac.Game { // Generates a universe, with star systems and such generate(starcount: number = 50, random: RandomGenerator = new RandomGenerator()): void { - this.stars = []; - this.starlinks = []; + this.stars = this.generateStars(starcount, random); - while (starcount) { + var links = this.getPotentialLinks(); + this.starlinks = this.filterCrossingLinks(links); + } + + // Generate a given number of stars, not too crowded + generateStars(count: number, random: RandomGenerator = new RandomGenerator()): Star[] { + var result: Star[] = []; + + while (count) { var x = random.throw() * this.radius * 2.0 - this.radius; var y = random.throw() * this.radius * 2.0 - this.radius; var star = new Star(this, x, y); - var nearest = this.getNearestTo(star); + var nearest = this.getNearestTo(star, result); if (nearest && nearest.getDistanceTo(star) < this.radius * 0.1) { continue; } - this.stars.push(star); + result.push(star); - starcount--; + count--; } - this.stars.forEach((first: Star) => { - var second = this.getNearestTo(first); - if (!this.areLinked(first, second)) { - this.starlinks.push(new StarLink(first, second)); + return result; + } + + // Get a list of potential links between the stars + getPotentialLinks(): StarLink[] { + var result: StarLink[] = []; + + this.stars.forEach((first: Star, idx1: number) => { + this.stars.forEach((second: Star, idx2: number) => { + if (idx1 < idx2) { + if (first.getDistanceTo(second) < this.radius * 0.6) { + result.push(new StarLink(first, second)); + } + } + }); + }); + + return result; + } + + // Filter a list of potential links to avoid crossing ones + filterCrossingLinks(links: StarLink[]): StarLink[] { + var result : StarLink[] = []; + + links.forEach((link1: StarLink) => { + var crossed = false; + links.forEach((link2: StarLink) => { + if (link1 !== link2 && link1.isCrossing(link2) && link1.getLength() >= link2.getLength()) { + crossed = true; + } + }); + if (!crossed) { + result.push(link1); } }); + + return result; } // Get the star nearest to another - getNearestTo(star: Star): Star { - if (this.stars.length === 0) { + getNearestTo(star: Star, others: Star[] = null): Star { + if (others === null) { + others = this.stars; + } + if (others.length === 0) { return null; } else { var mindist = this.radius * 2.0; var nearest: Star = null; - this.stars.forEach((istar: Star) => { + others.forEach((istar: Star) => { if (istar !== star) { var dist = star.getDistanceTo(istar); if (dist < mindist) { diff --git a/src/scripts/game/specs/StarLink.spec.ts b/src/scripts/game/specs/StarLink.spec.ts new file mode 100644 index 0000000..8c9f596 --- /dev/null +++ b/src/scripts/game/specs/StarLink.spec.ts @@ -0,0 +1,31 @@ +/// + +module SpaceTac.Game.Specs { + "use strict"; + + describe("StarLink", () => { + it("checks link intersection", () => { + var star1 = new Star(null, 0, 0); + var star2 = new Star(null, 0, 1); + var star3 = new Star(null, 1, 0); + var star4 = new Star(null, 1, 1); + var link1 = new StarLink(star1, star2); + var link2 = new StarLink(star1, star3); + var link3 = new StarLink(star1, star4); + var link4 = new StarLink(star2, star3); + var link5 = new StarLink(star2, star4); + var link6 = new StarLink(star3, star4); + var links = [link1, link2, link3, link4, link5, link6]; + links.forEach((first: StarLink) => { + links.forEach((second: StarLink) => { + if (first !== second) { + var expected = (first === link3 && second === link4) || + (first === link4 && second === link3); + expect(first.isCrossing(second)).toBe(expected); + expect(second.isCrossing(first)).toBe(expected); + } + }); + }); + }); + }); +} diff --git a/src/scripts/game/specs/Universe.spec.ts b/src/scripts/game/specs/Universe.spec.ts index a616494..bb8502d 100644 --- a/src/scripts/game/specs/Universe.spec.ts +++ b/src/scripts/game/specs/Universe.spec.ts @@ -42,9 +42,36 @@ module SpaceTac.Game.Specs { it("generates star systems", () => { var universe = new Universe(); - universe.generate(31); + var result = universe.generateStars(31); - expect(universe.stars.length).toBe(31); + expect(result.length).toBe(31); + }); + + it("lists potential links between star systems", () => { + var universe = new Universe(); + universe.stars.push(new Star(universe, 0, 0)); + universe.stars.push(new Star(universe, 0, 1)); + universe.stars.push(new Star(universe, 1, 0)); + + var result = universe.getPotentialLinks(); + expect(result.length).toBe(3); + expect(result[0]).toEqual(new StarLink(universe.stars[0], universe.stars[1])); + expect(result[1]).toEqual(new StarLink(universe.stars[0], universe.stars[2])); + expect(result[2]).toEqual(new StarLink(universe.stars[1], universe.stars[2])); + }); + + it("filters out crossing links", () => { + var universe = new Universe(); + universe.stars.push(new Star(universe, 0, 0)); + universe.stars.push(new Star(universe, 0, 1)); + universe.stars.push(new Star(universe, 1, 0)); + universe.stars.push(new Star(universe, 2, 2)); + + var result = universe.getPotentialLinks(); + expect(result.length).toBe(6); + + var filtered = universe.filterCrossingLinks(result); + expect(filtered.length).toBe(5); }); }); } diff --git a/src/scripts/view/map/UniverseMapView.ts b/src/scripts/view/map/UniverseMapView.ts index d98f362..832e385 100644 --- a/src/scripts/view/map/UniverseMapView.ts +++ b/src/scripts/view/map/UniverseMapView.ts @@ -29,7 +29,7 @@ module SpaceTac.View { this.universe.starlinks.forEach((link: Game.StarLink) => { var line = this.add.graphics(0, 0, this.stars); - line.lineStyle(0.3, 0xFFFFFF); + line.lineStyle(0.3, 0xA0A0A0); line.moveTo(link.first.x, link.first.y); line.lineTo(link.second.x, link.second.y); });