<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" c initial-scale=1.0">
<title>Snake Game with RL</title>
<style>
canvas {
border: 1px solid #000;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="400" height="400"></canvas>
[removed]
// Game variables
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
const scale = 20;
const rows = canvas.height / scale;
const columns = canvas.width / scale;
let snake;
let fruit;
// Snake object
class Snake {
constructor() {
this.x = 0;
this.y = 0;
this.xSpeed = scale * 1;
this.ySpeed = 0;
this.total = 0;
this.tail = [];
}
draw() {
ctx.fillStyle = "#FFFFFF";
for (let i = 0; i < this.tail.length; i++) {
ctx.fillRect(this.tail[i].x, this.tail[i].y, scale, scale);
}
ctx.fillRect(this.x, this.y, scale, scale);
}
update() {
for (let i = 0; i < this.tail.length - 1; i++) {
this.tail[i] = this.tail[i + 1];
}
this.tail[this.total - 1] = { x: this.x, y: this.y };
this.x += this.xSpeed;
this.y += this.ySpeed;
if (this.x >= canvas.width) {
this.x = 0;
} else if (this.x < 0) {
this.x = canvas.width - scale;
}
if (this.y >= canvas.height) {
this.y = 0;
} else if (this.y < 0) {
this.y = canvas.height - scale;
}
}
changeDirection(direction) {
switch(direction) {
case 'Up':
if (this.ySpeed !== scale * 1) {
this.xSpeed = 0;
this.ySpeed = -scale * 1;
}
break;
case 'Down':
if (this.ySpeed !== -scale * 1) {
this.xSpeed = 0;
this.ySpeed = scale * 1;
}
break;
case 'Left':
if (this.xSpeed !== scale * 1) {
this.xSpeed = -scale * 1;
this.ySpeed = 0;
}
break;
case 'Right':
if (this.xSpeed !== -scale * 1) {
this.xSpeed = scale * 1;
this.ySpeed = 0;
}
break;
}
}
eat(fruit) {
if (this.x === fruit.x && this.y === fruit.y) {
this.total++;
return true;
}
return false;
}
collision() {
for (let i = 0; i < this.tail.length; i++) {
if (this.x === this.tail[i].x && this.y === this.tail[i].y) {
return true;
}
}
return false;
}
getHeadPosition() {
return { x: this.x, y: this.y };
}
}
// Fruit object
class Fruit {
constructor() {
this.x = (Math.floor(Math.random() * rows)) * scale;
this.y = (Math.floor(Math.random() * columns)) * scale;
}
draw() {
ctx.fill
ctx.fillRect(this.x, this.y, scale, scale);
}
}
// Q-learning RL agent
class QAgent {
constructor() {
this.qTable = {};
this.alpha = 0.1;
this.gamma = 0.9;
this.epsil
this.lastState = null;
this.lastActi
}
getStateKey(state) {
return `${state.x},${state.y}`;
}
chooseAction(state) {
if (Math.random() < this.epsilon || !(this.getStateKey(state) in this.qTable)) {
const acti 'Down', 'Left', 'Right'];
return actions[Math.floor(Math.random() * actions.length)];
} else {
const stateKey = this.getStateKey(state);
const acti
const maxActi b) => actionValues[a] > actionValues[b] ? a : b);
return maxAction;
}
}
learn(state, action, reward, nextState) {
const stateKey = this.getStateKey(state);
const nextStateKey = this.getStateKey(nextState);
if (!(stateKey in this.qTable)) {
this.qTable[stateKey] = { Up: 0, Down: 0, Left: 0, Right: 0 };
}
if (!(nextStateKey in this.qTable)) {
this.qTable[nextStateKey] = { Up: 0, Down: 0, Left: 0, Right: 0 };
}
const oldQValue = this.qTable[stateKey][action];
const maxNextQValue = Math.max(...Object.values(this.qTable[nextStateKey]));
this.qTable[stateKey][action] += this.alpha * (reward + this.gamma * maxNextQValue - oldQValue);
}
}
// Game initialization
let qAgent = new QAgent();
function init() {
snake = new Snake();
fruit = new Fruit();
window.setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
fruit.draw();
snake.update();
snake.draw();
const state = snake.getHeadPosition();
const action = qAgent.chooseAction(state);
snake.changeDirection(action);
const nextState = snake.getHeadPosition();
const reward = snake.eat(fruit) ? 1 : 0;
qAgent.learn(state, action, reward, nextState);
if (snake.collision()) {
alert("Game Over!");
snake = new Snake();
fruit = new Fruit();
}
}, 100);
}
// Start the game
init();
[removed]
</body>
</html>