Skip to content
This repository was archived by the owner on Jan 12, 2023. It is now read-only.

Commit 4c229ea

Browse files
committed
feat: player
1 parent 3028879 commit 4c229ea

File tree

7 files changed

+152
-112
lines changed

7 files changed

+152
-112
lines changed

ui/src/logic/game/game.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export type GameInput = {
4141

4242

4343
export enum EGameStateObjectType {
44-
player, bullet
44+
player, bullet, wall
4545
}
4646

4747
export type GameStateObject = {
@@ -72,6 +72,7 @@ export type GameStateBullet = GameStateObject & {
7272
};
7373

7474
export type GameState = {
75+
walls: GameStateObject[],
7576
players: GameStatePlayer[],
7677
bullets: GameStateBullet[],
7778
/** index of game winner, if -1 game isn't over yet -2 if game is draw */
@@ -104,10 +105,11 @@ export class Game {
104105

105106
/** returns game state object from body ID */
106107
private getObjFromId(id: number): GameStateObject | undefined {
107-
const { bullets, players } = this.gameState;
108+
const { bullets, players, walls } = this.gameState;
108109
const find = (arr: GameStateObject[]) => arr.find(x => x.body.id === id);
109110
const player = find(players); if (player) return player;
110111
const bullet = find(bullets); if (bullet) return bullet;
112+
const wall = find(walls); if (wall) return wall;
111113
}
112114

113115

@@ -329,6 +331,7 @@ export class Game {
329331
World.add(world, players);
330332

331333
this.gameState = {
334+
walls: walls.map(x=>({body:x, health:Infinity, type:EGameStateObjectType.wall})),
332335
players: players.map((body) =>
333336
({
334337
type: EGameStateObjectType.player,

ui/src/logic/gameAi/GameAiEval.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ const numFromType = (type: EGameStateObjectType | "none" | "unknown") => {
1919
switch (type) {
2020
case "none":
2121
return 1;
22-
case EGameStateObjectType.player:
22+
case EGameStateObjectType.wall:
2323
return 2;
24-
case EGameStateObjectType.bullet:
24+
case EGameStateObjectType.player:
2525
return 3;
26+
case EGameStateObjectType.bullet:
27+
return 4;
28+
2629
case "unknown":
2730
return 4;
2831

@@ -79,7 +82,7 @@ export class GameAiEval {
7982
/** conver output of neural net into game input */
8083
private inputFromNN(numbers: number[]): GameInputPlayer {
8184
return {
82-
rotate: numbers[0],
85+
rotate: numbers[0] * 2 - 1,
8386
use: numbers[1] >= .5,
8487
walk: numbers[2] >= .5,
8588
switch: numbers[3],

ui/src/logic/gameAi/GameAiLiveTrain.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ export class GameAiLiveTrain {
5959
const evaler = new GameAiEval(selected.map(x => x.nn), Game.SETTINGS_DEFAULT);
6060

6161
const startingHealths = selected.map(x => {
62-
x.health -= 1;
62+
x.health -= 1 / healthMultiplier;
6363
if (x.health >= 0)
6464
return 1;
6565

66-
const minus = x.health;
66+
const minus = x.health * healthMultiplier;
6767
x.health = 0;
6868
return 1 + minus;
6969
});
@@ -75,10 +75,10 @@ export class GameAiLiveTrain {
7575
const resHealth = evaler.game.gameState.players.map(x => x.health);
7676
resHealth.forEach((x, i) => {
7777
const bot = selected[i];
78-
bot.health += x;
79-
if (x > 0) bot.health += this.params.healthGrowAfterGame;
78+
bot.health += x / healthMultiplier;
79+
if (x > 0) bot.health += healthGrowAfterGame/healthMultiplier;
8080

81-
const exceedingHealth = bot.health - this.params.healthMultiplier;
81+
const exceedingHealth = (bot.health - healthMultiplier)*healthMultiplier;
8282
if (exceedingHealth > 0) {
8383
bot.health = this.params.healthMultiplier;
8484
const bonusMultiplier =

ui/src/utils/raycast.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ import { Vector, Query } from "matter-js";
33
// source: https://github.com/liabru/matter-js/issues/181#issuecomment-164615987
44
export const raycast = (bodies: Matter.Body[], start: Vector, rotation: Vector, dist: number) => {
55
const normRay = Vector.normalise(rotation);
6-
const current = start;
76
for (let i = 0; i < dist; i++) {
8-
Vector.add(start, normRay, current);
9-
const body = Query.point(bodies, current)[0];
7+
const ray = Vector.add(start, Vector.mult(normRay, i));
8+
const body = Query.point(bodies, ray)[0];
109
if (body) {
11-
return { point: current, body };
10+
return { point: ray, body };
1211
}
1312
}
1413
return;

ui/src/views/MainPage.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@ import { Settings, TSettingState } from "./Settings";
66
import { History } from "./History";
77
import { RunAI } from "./RunAI";
88
import { GameAI } from "../logic/gameAi/GameAi";
9+
import { GameAiEval } from "../logic/gameAi/GameAiEval";
10+
import { NeuralNet } from "../logic/ai/nn/nn";
911

10-
const {defaultInitParams} = GameAI;
12+
const { defaultInitParams } = GameAI;
1113

1214
type TMainPageProps = {
1315

1416
};
1517

1618
export const MainPage: React.FC<TMainPageProps> = () => {
1719
const [aiSettings, setAiSettings] = useState<TSettingState>(defaultInitParams as any);
20+
const [snapshot, setSnapshot] = useState<NeuralNet[] | undefined>(undefined);
1821

1922
return <>
2023
<Card>
@@ -24,14 +27,14 @@ export const MainPage: React.FC<TMainPageProps> = () => {
2427
</Col> */}
2528
{/* <Col xxl={8}> */}
2629
{/* <Col sm={8}> */}
27-
<Row gutter={[8, 8]}>
28-
<Col sm={8}>
29-
<RunAI />
30-
</Col>
31-
<Col sm={16}>
32-
<PlayPage />
33-
</Col>
34-
</Row>
30+
<Row gutter={[8, 8]}>
31+
<Col sm={8}>
32+
<RunAI onSnapshot={(e) => setSnapshot(e)} />
33+
</Col>
34+
<Col sm={16}>
35+
<PlayPage snapshot={snapshot} />
36+
</Col>
37+
</Row>
3538
{/* </Col> */}
3639
{/* </Col> */}
3740
{/* <Col xxl={8}>

ui/src/views/Play.tsx

Lines changed: 113 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,144 @@
1-
import React, { useEffect, useRef } from "react";
2-
import { Card } from "antd";
3-
import { Game } from "../logic/game/game";
4-
import { keyCaptureStart, keyCaptureStop } from "../core/keycapture";
5-
import { gameRender } from "../logic/game/render";
1+
import React, { useEffect, useRef, useState } from "react";
2+
import { Button, Card } from "antd";
63
import { GameAiEval } from "../logic/gameAi/GameAiEval";
7-
import { range } from "../core/common";
8-
import { GameAI } from "../logic/gameAi/GameAi";
9-
import { IASelectionFunctionType } from "../logic/ai/ga/gaProcesGenerationFunction";
4+
import { Render, Events } from "matter-js";
5+
import { renderPoint, renderLine } from "../utils/rendererUtils";
6+
import { Game } from "../logic/game/game";
7+
import { NeuralNet } from "../_old/logic/ai/nn/nn";
8+
109

1110
const debug = false;
1211

1312
type TRunnerProps = {
1413
capture?: boolean,
14+
snapshot: NeuralNet[] | undefined,
1515
};
1616

17-
type TRendererProps = {
18-
width: number,
19-
height: number,
20-
};
17+
const height = 300;
18+
const width = 300;
2119

22-
const Renderer: React.FC<TRendererProps> = ({ height, width }) => {
20+
export const PlayPage: React.FC<TRunnerProps> = ({ capture, snapshot }) => {
21+
const runningState = useState(false);
22+
const [evaler, setEvaler] = useState<GameAiEval | undefined>(undefined);
2323
const ref = useRef<HTMLDivElement>(undefined as any);
24+
const [renderer, setRenderer] = useState<Render | undefined>(undefined);
25+
const setRunning = runningState[1];
26+
27+
const canStart = !(runningState[0] || !snapshot);
2428

25-
const init = () => {
26-
setTimeout(() => {
27-
// _init();
28-
}, (2000));
29+
const start = () => {
30+
if (!canStart) return;
31+
setRunning(true);
32+
setEvaler(new GameAiEval(snapshot!, Game.SETTINGS_DEFAULT));
2933
}
3034

31-
const _init = async () => {
32-
if (!ref.current)
33-
return;
34-
// const game = new Game();
35+
const stop = () => {
36+
setRunning(false);
37+
}
38+
39+
useEffect(() => {
40+
if (runningState[0]) return;
41+
if (renderer)
42+
Render.stop(renderer)
43+
setRenderer(undefined);
44+
if (ref.current) ref.current.innerHTML = "";
45+
}, [renderer, runningState[0]])
46+
47+
useEffect(() => {
48+
if (!ref.current || !evaler || !runningState[0]) return;
49+
// if (!ref.current) return;
3550
const element = ref.current;
3651

37-
const gameAi = new GameAI({
38-
aiParams: {
39-
sensors: Game.SETTINGS_DEFAULT.ai.sensorSidesArrayAngle,
40-
},
41-
gaInit: {
42-
popSize: 5,
43-
proccessFunction: {
44-
breedingParents: 1,
45-
mutationRate: .01,
46-
selection: {
47-
type: IASelectionFunctionType.percent,
48-
value: 5,
49-
}
50-
}
51-
},
52-
games: 2,
53-
gameSettings: Game.SETTINGS_DEFAULT,
54-
nnInit: { hiddenLayers: [5], },
55-
}, () => {
56-
console.log("game ended");
52+
const game = evaler.game;
53+
// const game = new Game();
54+
const engine = game.engine;
55+
56+
// create renderer
57+
const render = Render.create({
58+
element,
59+
engine,
60+
options: {
61+
width,
62+
height,
63+
64+
background: "#cccccc",
65+
wireframeBackground: "#0f0f13",
66+
wireframes: false,
67+
68+
...(debug
69+
? {
70+
wireframes: true,
71+
showVelocity: true,
72+
showCollisions: true,
73+
showSeparations: true,
74+
showAngleIndicator: true,
75+
} : {}),
76+
} as any,
5777
});
5878

59-
range(0).forEach(x => {
60-
console.log(`generation ${x} best: ${gameAi.gann.ga.population[0].fitness}`);
61-
gameAi.next(() => {
62-
console.log("game ended");
79+
setRenderer(render);
80+
81+
// Render.run(render);
82+
83+
// fit the render viewport to the scene
84+
(Render as any).lookAt(render, {
85+
min: { x: 0, y: 0 },
86+
max: { x: game.settings.map.size, y: game.settings.map.size },
87+
});
88+
89+
Events.on(render, "afterRender", () => {
90+
game.gameState.players.forEach((_p, pi) => {
91+
const res = game.sensor(pi);
92+
const playerPos = game.gameState.players[pi].body.position;
93+
94+
res.forEach(res => {
95+
renderPoint(render, res.point);
96+
renderLine(render, playerPos, res.point);
97+
});
6398
});
64-
})
99+
});
65100

66-
// const botsNNs = range(2).map(() => GameAiEval.initializeRandomBot(
67-
// { hiddens: [8, 5, 3], sensors: GameAI.defaultInitParams.aiParams.sensors }
68-
// ))
101+
const targetDelta = game.settings.simulation.delta;
69102

70-
const pop = gameAi.gann.ga.population;
71-
const botsNNs = [pop[0].dna, pop[1].dna];
103+
let last = Date.now();
104+
const step = () => {
105+
const now = Date.now();
106+
const delta = now - last;
107+
if (delta < targetDelta) return;
72108

73-
const gameAiEval = new GameAiEval(botsNNs, Game.SETTINGS_DEFAULT);
109+
last += delta;
110+
// game.next();
111+
evaler.next();
112+
step();
113+
};
74114

75-
const aiInput = () => gameAiEval.calculateBotResponse();
115+
const rafLoop = () => {
116+
step();
117+
if (!game.isGameOver) {
118+
// if ([runningState[0]])
119+
requestAnimationFrame(rafLoop);
120+
} else {
76121

77-
// keyCaptureStart();
78-
// const userInput = () => {
79-
// const walk = capturedKeys.has("ArrowUp");
80-
// const rotate = capturedKeys.has("ArrowLeft") ? -1
81-
// : capturedKeys.has("ArrowRight") ? 1
82-
// : 0
83-
// ;
84-
// const use = capturedKeys.has(" ");
85-
// return { players: [{ walk, rotate, use } as any] };
86-
// };
122+
// todo: print winner on canvas
123+
console.log(`winner is ${game.gameState.winner}`);
124+
}
125+
};
126+
requestAnimationFrame(rafLoop);
87127

88-
gameRender({
89-
element, game: gameAiEval.game, height, width, debug, input: aiInput,
90-
});
91-
};
92-
useEffect(init, []);
128+
Render.run(render);
129+
}, [evaler])
93130

94-
return <>
95-
<div style={{ justifyContent: "center", display: "flex" }}>
96-
<div ref={ref} />
97-
</div>
98-
</>;
99-
};
131+
const button = runningState[0]
132+
? <Button onClick={stop}>Stop</Button>
133+
: <Button onClick={start} disabled={!canStart}>Play</Button>
134+
;
100135

101-
export const PlayPage: React.FC<TRunnerProps> = ({ capture }) => {
102-
if (capture)
103-
keyCaptureStart();
104-
else
105-
keyCaptureStop();
106136

107137
return <>
108-
<Card title="Play">
109-
<Renderer {...{ width: 500, height: 500 }} />
138+
<Card title="Play" extra={button}>
139+
<div style={{ justifyContent: "center", display: "flex" }}>
140+
<div ref={ref} />
141+
</div>
110142
</Card>
111143
</>;
112144
};

0 commit comments

Comments
 (0)