generated from michael/webpack-base
simultaneous labs
This commit is contained in:
parent
5444feee99
commit
66bab68e24
@ -1,4 +1,4 @@
|
||||
import { Engine, Keys, randint, UI, Vec2, vec2 } from './game-engine';
|
||||
import { Engine, randint, UI, Vec2, vec2 } from './game-engine';
|
||||
import { SnakeBrain } from './snake-brain';
|
||||
import { SGSHashSet, Snake, SnakeGameState, SnakeGameStateWithHistory } from './types';
|
||||
|
||||
@ -10,8 +10,27 @@ const CENTER_Y = BOARD_SIZE / 2;
|
||||
|
||||
export const BOARD_SQUARES = BOARD_SIZE / SQUARE_SIZE;
|
||||
|
||||
interface SnakeGameTrainerLab {
|
||||
id: number;
|
||||
brain: SnakeBrain;
|
||||
state: SnakeGameStateWithHistory;
|
||||
}
|
||||
|
||||
interface SnakeGameTrainerState {
|
||||
labs: SnakeGameTrainerLab[];
|
||||
}
|
||||
|
||||
// general functions -----------------------------------------------------------
|
||||
|
||||
function getStartSnakeGameState(): SnakeGameStateWithHistory {
|
||||
const dead = false;
|
||||
const snake = [vec2(1, 1), vec2(2, 1), vec2(3, 1)];
|
||||
const apple = getRandApplePos();
|
||||
const history = new SGSHashSet();
|
||||
|
||||
return { dead, snake, apple, history };
|
||||
}
|
||||
|
||||
function shallowCopySGS(state: SnakeGameState): SnakeGameState {
|
||||
return {
|
||||
dead: state.dead,
|
||||
@ -28,62 +47,58 @@ function getSnakeNextSquare(snake: Snake, dir: Vec2) {
|
||||
return snake[snake.length - 1]!.add(dir);
|
||||
}
|
||||
|
||||
function getStartSnakeGameState(): SnakeGameStateWithHistory {
|
||||
const dead = false;
|
||||
const snake = [vec2(1, 1), vec2(2, 1), vec2(3, 1)];
|
||||
const apple = getRandApplePos();
|
||||
const history = new SGSHashSet();
|
||||
|
||||
return { dead, snake, apple, history };
|
||||
}
|
||||
|
||||
// control code ----------------------------------------------------------------
|
||||
|
||||
export default function runCanvas(canvas: HTMLCanvasElement) {
|
||||
const ui = new UI(canvas);
|
||||
const keys = new Keys();
|
||||
const engine = new Engine({ updateDelay: 50 });
|
||||
|
||||
// game logic --------------------------------------------------------------
|
||||
|
||||
const brain = SnakeBrain.fromRandom({ hiddenLayerNodes: 12 });
|
||||
const state = getStartSnakeGameState();
|
||||
const trainer: SnakeGameTrainerState = {
|
||||
labs: Array.from({ length: 4 }).map((_, idx) => ({
|
||||
id: idx + 1,
|
||||
state: getStartSnakeGameState(),
|
||||
brain: SnakeBrain.fromRandom({ hiddenLayerNodes: 12 }),
|
||||
})),
|
||||
};
|
||||
|
||||
function update() {
|
||||
const { dead, snake, apple, history } = state;
|
||||
for (const lab of trainer.labs) {
|
||||
const { state, brain } = lab;
|
||||
const { dead, snake, apple, history } = state;
|
||||
|
||||
if (dead) return;
|
||||
if (dead) continue;
|
||||
|
||||
// kill cycling snakes
|
||||
if (history.has(state)) {
|
||||
state.dead = true;
|
||||
return;
|
||||
// kill cycling snakes
|
||||
if (history.has(state)) {
|
||||
state.dead = true;
|
||||
continue;
|
||||
}
|
||||
history.add(shallowCopySGS(state));
|
||||
|
||||
// perform ai
|
||||
const dir = brain.think(state);
|
||||
|
||||
// NOTE: brain.think handles out-of-bounds/tail intersect checking when it identifies
|
||||
// valid move options. it will return 'dead' if there are no valid moves
|
||||
if (dir === 'dead') {
|
||||
state.dead = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const nextHead = getSnakeNextSquare(snake, dir);
|
||||
|
||||
// check for snake hitting apple
|
||||
if (nextHead.eq(apple)) {
|
||||
state.apple = getRandApplePos();
|
||||
} else {
|
||||
snake.shift();
|
||||
}
|
||||
|
||||
// move the snake
|
||||
snake.push(nextHead);
|
||||
}
|
||||
history.add(shallowCopySGS(state));
|
||||
|
||||
// perform ai
|
||||
const dir = brain.think(state);
|
||||
|
||||
// NOTE: brain.think handles out-of-bounds/tail intersect checking when it identifies
|
||||
// valid move options. it will return 'dead' if there are no valid moves
|
||||
if (dir === 'dead') {
|
||||
state.dead = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const nextHead = getSnakeNextSquare(snake, dir);
|
||||
|
||||
// check for snake hitting apple
|
||||
if (nextHead.eq(apple)) {
|
||||
state.apple = getRandApplePos();
|
||||
} else {
|
||||
snake.shift();
|
||||
}
|
||||
|
||||
// move the snake
|
||||
snake.push(nextHead);
|
||||
|
||||
keys.update();
|
||||
}
|
||||
|
||||
// rendering ---------------------------------------------------------------
|
||||
@ -93,23 +108,34 @@ export default function runCanvas(canvas: HTMLCanvasElement) {
|
||||
}
|
||||
|
||||
function render() {
|
||||
const { dead, snake, apple } = state;
|
||||
|
||||
ui.setFillStyle('#333333');
|
||||
ui.clearCanvas();
|
||||
|
||||
ui.setFillStyle('#277edb');
|
||||
for (const square of snake.slice(0, snake.length - 1)) {
|
||||
drawSquare(square);
|
||||
for (const lab of trainer.labs) {
|
||||
const { state, brain } = lab;
|
||||
const { dead, snake, apple } = state;
|
||||
|
||||
// tail
|
||||
ui.setFillStyle('#277edb');
|
||||
for (const square of snake.slice(0, snake.length - 1)) {
|
||||
drawSquare(square);
|
||||
}
|
||||
|
||||
// head
|
||||
if (dead) {
|
||||
ui.setFillStyle('#ff0000');
|
||||
} else {
|
||||
ui.setFillStyle('#27b4db');
|
||||
}
|
||||
drawSquare(snake[snake.length - 1]!);
|
||||
|
||||
// apple
|
||||
ui.setFillStyle('#e01851');
|
||||
drawSquare(apple);
|
||||
}
|
||||
ui.setFillStyle('#29d17a');
|
||||
drawSquare(snake[snake.length - 1]!);
|
||||
|
||||
ui.setFillStyle('#e01851');
|
||||
drawSquare(apple);
|
||||
|
||||
if (dead) {
|
||||
const text = 'You Died';
|
||||
if (trainer.labs.findIndex(l => !l.state.dead) === -1) {
|
||||
const text = 'All Died';
|
||||
ui.setFont('40px sans-serif');
|
||||
const m = ui.measureText(text);
|
||||
|
||||
@ -122,10 +148,9 @@ export default function runCanvas(canvas: HTMLCanvasElement) {
|
||||
ui.setFillStyle('#cc0000');
|
||||
ui.setTextAlign('center');
|
||||
ui.setTextBaseline('middle');
|
||||
ui.fillText('You Died', BOARD_SIZE / 2, BOARD_SIZE / 2);
|
||||
ui.fillText(text, BOARD_SIZE / 2, BOARD_SIZE / 2);
|
||||
}
|
||||
}
|
||||
|
||||
keys.bindKeys();
|
||||
engine.run(update, render);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user