我目前正在为一个学校项目使用 JavaScript 开发 Pong 游戏。我的一条规则是人工智能每秒只能检查一次游戏状态。我正在寻找增强人工智能性能的方法。
目前,人工智能的运行原理很简单:
当球慢时,这种策略效果很好,因为人工智能总是有足够的时间来评估情况并做出反应,使其能够接住每一个球。然而,当球以正常速度移动时,人工智能通常只能在球击中玩家球拍之前看到它。这使得人工智能没有时间做出反应,从而导致错失机会。因此,我正在探索解决这个问题的策略,可能是通过预测球最终的去向。
这是人工智能的代码:
import * as g from './global.js';
import * as game from './game.js';
import * as physics from './physics.js';
/*
* The behaves according to the following rules:
*
* Since we can look at the state of the game once per second, it means
* that we have a 'budget' of 60 ticks to use to make our actions.
*
* The budget is divided as follow :
* | move towards the ball | wait for collision | move back towards the center | wait for the remainder of the budget |
*
*/
export class AIManager {
constructor() {
this.last_looked = 0.0;
this.game = new game.Game();
this.collision_point = new physics.Vector(0, 0);
this.inputs = [];
this.nput_movement = g.INPUT_NEUTRAL;
this.input_center = g.INPUT_NEUTRAL;
this.tick_budget = 60; /* The number of ticks we have before the next time we update our view */
this.tick_collision = 0; /* The number of ticks before the ball collide with the paddle */
this.tick_movement = 0; /* The minimum number of ticks before the paddle reaches the collision point */
this.tick_center = 0; /* The number of ticks need to go back to the center of the board */
this.intersect_area = new physics.Rectangle(0, 2 * g.BOARD_WALL, g.BOARD_WIDTH, g.BALL_SIDE, 0, 0);
}
reset() {
this.last_looked = performance.now();
this.game = new game.Game();
this.collision_point = new physics.Vector(0, 0);
this.inputs = [];
this.nput_movement = g.INPUT_NEUTRAL;
this.input_center = g.INPUT_NEUTRAL;
this.tick_budget = 60; /* The number of ticks we have before the next time we update our view */
this.tick_collision = 0; /* The number of ticks before the ball collide with the paddle */
this.tick_movement = 0; /* The minimum number of ticks before the paddle reaches the collision point */
this.tick_center = 0; /* The number of ticks need to go back to the center of the board */
this.intersect_area = new physics.Rectangle(0, 2 * g.BOARD_WALL, g.BOARD_WIDTH, g.BALL_SIDE, 0, 0);
}
refresh(game, dt) {
const elapsed = performance.now() - this.last_looked;
if (elapsed >= 1000) {
this.reset();
/* Load the game state into the AI's 'memory' */
this.load_state(game);
if (this.game.ball.velocity.y >= 0) {
this.tick_movement = 0;
this.tick_collision = 0;
this.handle_reset(dt);
} else {
this.handle_collision(dt);
this.handle_movement(dt);
this.handle_reset(dt);
}
this.queue_moves();
}
return this.inputs.shift();
}
handle_collision(dt) {
let tick = 0;
let collision = null;
let collision_happened = false;
while (!collision_happened && tick < 120) {
tick += 1;
collision = physics.aabb_continuous_detection(this.game.ball, this.intersect_area, dt);
if (collision.time > 0 && collision.time <= 1.0) {
collision_happened = true;
} else {
this.game.ball.position.x += this.game.ball.velocity.x * dt;
this.game.ball.position.y += this.game.ball.velocity.y * dt;
}
if (this.game.ball.position.x <= g.BOARD_WALL || this.game.ball.position.x + this.game.ball.size.x >= g.BOARD_WIDTH - g.BOARD_WALL) {
/* Left and right walls */
this.game.ball.position.x = this.game.ball.position.x <= g.BOARD_WALL ? g.BOARD_WALL : g.BOARD_WIDTH - this.game.ball.size.x - g.BOARD_WALL;
this.game.ball.velocity.x *= -1;
}
}
this.tick_collision = tick;
this.collision_point.x = collision.point.x;
this.collision_point.y = collision.point.y;
}
handle_movement(dt) {
let tick = 0;
const paddle = this.game.player2;
const paddle_center = this.game.player2.position.x + (this.game.player2.size.x / 2);
if (this.collision_point.x < paddle_center) {
this.game.player2.velocity.x = -g.PADDLE_SPEED;
this.nput_movement = g.INPUT_LEFT;
} else {
this.game.player2.velocity.x = g.PADDLE_SPEED;
this.nput_movement = g.INPUT_RIGHT;
}
while (!(paddle.position.x < this.collision_point.x && paddle.position.x + paddle.size.x >= this.collision_point.x) && tick < 60) {
tick += 1;
if (paddle.position.x + paddle.velocity.x * dt > g.BOARD_CORRIDOR && paddle.position.x + paddle.size.x + paddle.velocity.x * dt < g.BOARD_WIDTH - g.BOARD_CORRIDOR) {
paddle.position.x += paddle.velocity.x * dt;
}
}
this.tick_movement = tick;
}
handle_reset(dt) {
const board_center = g.BOARD_WIDTH / 2;
const paddle_center = this.game.player2.position.x + (this.game.player2.size.x / 2);
const distance_from_center = board_center - paddle_center;
const velocity = board_center <= paddle_center ? -g.PADDLE_SPEED : g.PADDLE_SPEED;
const delta_move = velocity * dt;
if (distance_from_center < 0) {
this.input_center = g.INPUT_LEFT;
} else if (distance_from_center > 0) {
this.input_center = g.INPUT_RIGHT;
}
this.tick_center = Math.abs(Math.floor(distance_from_center / delta_move));
}
queue_moves() {
for (let i = 0; i < this.tick_movement && this.tick_budget > 0; i++) {
this.inputs.push(this.nput_movement);
this.tick_budget -= 1;
}
for (let i = 0; i < this.tick_collision && this.tick_budget > 0; i++) {
this.inputs.push(g.INPUT_NEUTRAL);
this.tick_budget -= 1;
}
for (let i = 0; i < this.tick_center && this.tick_budget > 0; i++) {
this.inputs.push(this.input_center);
this.tick_budget -= 1;
}
for (let i = 0; this.tick_budget > 0; i++) {
this.inputs.push(g.INPUT_NEUTRAL);
this.tick_budget -= 1;
}
}
load_state(game) {
this.game.status = game.status;
this.game.who_serves = game.who_serves;
this.game.ball.position.x = game.ball.position.x;
this.game.ball.position.y = game.ball.position.y;
this.game.ball.velocity.x = game.ball.velocity.x;
this.game.ball.velocity.y = game.ball.velocity.y;
this.game.player1.position.x = game.player1.position.x;
this.game.player2.position.x = game.player2.position.x;
}
}
您可以预测球的位置,这样您的 AI 就可以快速做出反应,即使每秒只检查一次游戏状态...... 我不确定你的游戏基础设施以及所有内容是如何计算的,但这应该让你对我想要做什么有一个模糊的想法...... 这将导致更快的反应
预测球位置功能:
predictBallPositionAfterSecond() {
// Clone the ball's position and velocity to not affect the actual game state
let predictedPosition = {...this.game.ball.position};
let predictedVelocity = {...this.game.ball.velocity};
let timeRemaining = 1.0; // Predict for 1 second into the future
const dt = 0.016; // Assuming a frame rate of 60 FPS, so each tick is approximately 1/60th of a second
while (timeRemaining > 0) {
// Calculate time to next collision with top or bottom wall
let timeToCollision;
if (predictedVelocity.y > 0) {
// Moving downwards
timeToCollision = (g.BOARD_HEIGHT - g.BOARD_WALL - predictedPosition.y - this.game.ball.size.y) / predictedVelocity.y;
} else {
// Moving upwards
timeToCollision = (g.BOARD_WALL - predictedPosition.y) / predictedVelocity.y;
}
// If no collision within the next tick, move ball for the entire tick
if (timeToCollision > dt || timeToCollision < 0) {
predictedPosition.x += predictedVelocity.x * dt;
predictedPosition.y += predictedVelocity.y * dt;
timeRemaining -= dt;
} else {
// Move ball to the point of collision
predictedPosition.x += predictedVelocity.x * timeToCollision;
predictedPosition.y += predictedVelocity.y * timeToCollision;
// Reflect the ball's velocity
predictedVelocity.y *= -1;
// Subtract the elapsed time to collision from the remaining time
timeRemaining -= timeToCollision;
}
// Check for collision with left and right walls to reverse X direction if necessary
// This part can be adjusted based on your game's rules about side wall bounces
if (predictedPosition.x <= g.BOARD_WALL || predictedPosition.x + this.game.ball.size.x >= g.BOARD_WIDTH - g.BOARD_WALL) {
predictedVelocity.x *= -1;
}
}
return predictedPosition;
}
在代码中使用预测函数:
if (elapsed >= 1000) {
this.reset();
this.load_state(game);
// Predict the ball's position
const predictedPosition = this.predictBallPositionAfterSecond();
// Now you can use predictedPosition to decide how the AI should move
}
请记住,为了进行精确的预测,您需要调整碰撞和弹跳逻辑以匹配您的游戏规则。基本上,您的预测越能模仿游戏的物理原理,就越准确。