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

Commit

Permalink
feat: player
Browse files Browse the repository at this point in the history
  • Loading branch information
Sciator committed Dec 16, 2020
1 parent 3028879 commit 4c229ea
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 112 deletions.
7 changes: 5 additions & 2 deletions ui/src/logic/game/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export type GameInput = {


export enum EGameStateObjectType {
player, bullet
player, bullet, wall
}

export type GameStateObject = {
Expand Down Expand Up @@ -72,6 +72,7 @@ export type GameStateBullet = GameStateObject & {
};

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

/** returns game state object from body ID */
private getObjFromId(id: number): GameStateObject | undefined {
const { bullets, players } = this.gameState;
const { bullets, players, walls } = this.gameState;
const find = (arr: GameStateObject[]) => arr.find(x => x.body.id === id);
const player = find(players); if (player) return player;
const bullet = find(bullets); if (bullet) return bullet;
const wall = find(walls); if (wall) return wall;
}


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

this.gameState = {
walls: walls.map(x=>({body:x, health:Infinity, type:EGameStateObjectType.wall})),
players: players.map((body) =>
({
type: EGameStateObjectType.player,
Expand Down
9 changes: 6 additions & 3 deletions ui/src/logic/gameAi/GameAiEval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ const numFromType = (type: EGameStateObjectType | "none" | "unknown") => {
switch (type) {
case "none":
return 1;
case EGameStateObjectType.player:
case EGameStateObjectType.wall:
return 2;
case EGameStateObjectType.bullet:
case EGameStateObjectType.player:
return 3;
case EGameStateObjectType.bullet:
return 4;

case "unknown":
return 4;

Expand Down Expand Up @@ -79,7 +82,7 @@ export class GameAiEval {
/** conver output of neural net into game input */
private inputFromNN(numbers: number[]): GameInputPlayer {
return {
rotate: numbers[0],
rotate: numbers[0] * 2 - 1,
use: numbers[1] >= .5,
walk: numbers[2] >= .5,
switch: numbers[3],
Expand Down
10 changes: 5 additions & 5 deletions ui/src/logic/gameAi/GameAiLiveTrain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ export class GameAiLiveTrain {
const evaler = new GameAiEval(selected.map(x => x.nn), Game.SETTINGS_DEFAULT);

const startingHealths = selected.map(x => {
x.health -= 1;
x.health -= 1 / healthMultiplier;
if (x.health >= 0)
return 1;

const minus = x.health;
const minus = x.health * healthMultiplier;
x.health = 0;
return 1 + minus;
});
Expand All @@ -75,10 +75,10 @@ export class GameAiLiveTrain {
const resHealth = evaler.game.gameState.players.map(x => x.health);
resHealth.forEach((x, i) => {
const bot = selected[i];
bot.health += x;
if (x > 0) bot.health += this.params.healthGrowAfterGame;
bot.health += x / healthMultiplier;
if (x > 0) bot.health += healthGrowAfterGame/healthMultiplier;

const exceedingHealth = bot.health - this.params.healthMultiplier;
const exceedingHealth = (bot.health - healthMultiplier)*healthMultiplier;
if (exceedingHealth > 0) {
bot.health = this.params.healthMultiplier;
const bonusMultiplier =
Expand Down
7 changes: 3 additions & 4 deletions ui/src/utils/raycast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import { Vector, Query } from "matter-js";
// source: https://github.com/liabru/matter-js/issues/181#issuecomment-164615987
export const raycast = (bodies: Matter.Body[], start: Vector, rotation: Vector, dist: number) => {
const normRay = Vector.normalise(rotation);
const current = start;
for (let i = 0; i < dist; i++) {
Vector.add(start, normRay, current);
const body = Query.point(bodies, current)[0];
const ray = Vector.add(start, Vector.mult(normRay, i));
const body = Query.point(bodies, ray)[0];
if (body) {
return { point: current, body };
return { point: ray, body };
}
}
return;
Expand Down
21 changes: 12 additions & 9 deletions ui/src/views/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import { Settings, TSettingState } from "./Settings";
import { History } from "./History";
import { RunAI } from "./RunAI";
import { GameAI } from "../logic/gameAi/GameAi";
import { GameAiEval } from "../logic/gameAi/GameAiEval";
import { NeuralNet } from "../logic/ai/nn/nn";

const {defaultInitParams} = GameAI;
const { defaultInitParams } = GameAI;

type TMainPageProps = {

};

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

return <>
<Card>
Expand All @@ -24,14 +27,14 @@ export const MainPage: React.FC<TMainPageProps> = () => {
</Col> */}
{/* <Col xxl={8}> */}
{/* <Col sm={8}> */}
<Row gutter={[8, 8]}>
<Col sm={8}>
<RunAI />
</Col>
<Col sm={16}>
<PlayPage />
</Col>
</Row>
<Row gutter={[8, 8]}>
<Col sm={8}>
<RunAI onSnapshot={(e) => setSnapshot(e)} />
</Col>
<Col sm={16}>
<PlayPage snapshot={snapshot} />
</Col>
</Row>
{/* </Col> */}
{/* </Col> */}
{/* <Col xxl={8}>
Expand Down
194 changes: 113 additions & 81 deletions ui/src/views/Play.tsx
Original file line number Diff line number Diff line change
@@ -1,112 +1,144 @@
import React, { useEffect, useRef } from "react";
import { Card } from "antd";
import { Game } from "../logic/game/game";
import { keyCaptureStart, keyCaptureStop } from "../core/keycapture";
import { gameRender } from "../logic/game/render";
import React, { useEffect, useRef, useState } from "react";
import { Button, Card } from "antd";
import { GameAiEval } from "../logic/gameAi/GameAiEval";
import { range } from "../core/common";
import { GameAI } from "../logic/gameAi/GameAi";
import { IASelectionFunctionType } from "../logic/ai/ga/gaProcesGenerationFunction";
import { Render, Events } from "matter-js";
import { renderPoint, renderLine } from "../utils/rendererUtils";
import { Game } from "../logic/game/game";
import { NeuralNet } from "../_old/logic/ai/nn/nn";


const debug = false;

type TRunnerProps = {
capture?: boolean,
snapshot: NeuralNet[] | undefined,
};

type TRendererProps = {
width: number,
height: number,
};
const height = 300;
const width = 300;

const Renderer: React.FC<TRendererProps> = ({ height, width }) => {
export const PlayPage: React.FC<TRunnerProps> = ({ capture, snapshot }) => {
const runningState = useState(false);
const [evaler, setEvaler] = useState<GameAiEval | undefined>(undefined);
const ref = useRef<HTMLDivElement>(undefined as any);
const [renderer, setRenderer] = useState<Render | undefined>(undefined);
const setRunning = runningState[1];

const canStart = !(runningState[0] || !snapshot);

const init = () => {
setTimeout(() => {
// _init();
}, (2000));
const start = () => {
if (!canStart) return;
setRunning(true);
setEvaler(new GameAiEval(snapshot!, Game.SETTINGS_DEFAULT));
}

const _init = async () => {
if (!ref.current)
return;
// const game = new Game();
const stop = () => {
setRunning(false);
}

useEffect(() => {
if (runningState[0]) return;
if (renderer)
Render.stop(renderer)
setRenderer(undefined);
if (ref.current) ref.current.innerHTML = "";
}, [renderer, runningState[0]])

useEffect(() => {
if (!ref.current || !evaler || !runningState[0]) return;
// if (!ref.current) return;
const element = ref.current;

const gameAi = new GameAI({
aiParams: {
sensors: Game.SETTINGS_DEFAULT.ai.sensorSidesArrayAngle,
},
gaInit: {
popSize: 5,
proccessFunction: {
breedingParents: 1,
mutationRate: .01,
selection: {
type: IASelectionFunctionType.percent,
value: 5,
}
}
},
games: 2,
gameSettings: Game.SETTINGS_DEFAULT,
nnInit: { hiddenLayers: [5], },
}, () => {
console.log("game ended");
const game = evaler.game;
// const game = new Game();
const engine = game.engine;

// create renderer
const render = Render.create({
element,
engine,
options: {
width,
height,

background: "#cccccc",
wireframeBackground: "#0f0f13",
wireframes: false,

...(debug
? {
wireframes: true,
showVelocity: true,
showCollisions: true,
showSeparations: true,
showAngleIndicator: true,
} : {}),
} as any,
});

range(0).forEach(x => {
console.log(`generation ${x} best: ${gameAi.gann.ga.population[0].fitness}`);
gameAi.next(() => {
console.log("game ended");
setRenderer(render);

// Render.run(render);

// fit the render viewport to the scene
(Render as any).lookAt(render, {
min: { x: 0, y: 0 },
max: { x: game.settings.map.size, y: game.settings.map.size },
});

Events.on(render, "afterRender", () => {
game.gameState.players.forEach((_p, pi) => {
const res = game.sensor(pi);
const playerPos = game.gameState.players[pi].body.position;

res.forEach(res => {
renderPoint(render, res.point);
renderLine(render, playerPos, res.point);
});
});
})
});

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

const pop = gameAi.gann.ga.population;
const botsNNs = [pop[0].dna, pop[1].dna];
let last = Date.now();
const step = () => {
const now = Date.now();
const delta = now - last;
if (delta < targetDelta) return;

const gameAiEval = new GameAiEval(botsNNs, Game.SETTINGS_DEFAULT);
last += delta;
// game.next();
evaler.next();
step();
};

const aiInput = () => gameAiEval.calculateBotResponse();
const rafLoop = () => {
step();
if (!game.isGameOver) {
// if ([runningState[0]])
requestAnimationFrame(rafLoop);
} else {

// keyCaptureStart();
// const userInput = () => {
// const walk = capturedKeys.has("ArrowUp");
// const rotate = capturedKeys.has("ArrowLeft") ? -1
// : capturedKeys.has("ArrowRight") ? 1
// : 0
// ;
// const use = capturedKeys.has(" ");
// return { players: [{ walk, rotate, use } as any] };
// };
// todo: print winner on canvas
console.log(`winner is ${game.gameState.winner}`);
}
};
requestAnimationFrame(rafLoop);

gameRender({
element, game: gameAiEval.game, height, width, debug, input: aiInput,
});
};
useEffect(init, []);
Render.run(render);
}, [evaler])

return <>
<div style={{ justifyContent: "center", display: "flex" }}>
<div ref={ref} />
</div>
</>;
};
const button = runningState[0]
? <Button onClick={stop}>Stop</Button>
: <Button onClick={start} disabled={!canStart}>Play</Button>
;

export const PlayPage: React.FC<TRunnerProps> = ({ capture }) => {
if (capture)
keyCaptureStart();
else
keyCaptureStop();

return <>
<Card title="Play">
<Renderer {...{ width: 500, height: 500 }} />
<Card title="Play" extra={button}>
<div style={{ justifyContent: "center", display: "flex" }}>
<div ref={ref} />
</div>
</Card>
</>;
};
Loading

0 comments on commit 4c229ea

Please sign in to comment.