<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="700" height="600" style="border:1px solid #c3c3c3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
// AI Array, put in at least two constructor functions as described in the
// data file, and it will do round robin on them.
// Also, you can adjust some variables for testing purposes only.
var interval = 1; // in milliseconds per turn
var matches = 10; // number of matches for each pairing
var aiArray = [];
myAI = function(player1) {
this.player1 = player1;
this.yourMove = function(b) {
var me;
if (this.player1) {
me = b.player1;
} else {
me = b.player2;
}
var d = 0;
var tokenP;
while (tokenP == undefined) {
var arr = this.findTokensAtDistance(me.pos, d)
tokenP = this.findBestToken(arr, b.tokens, me);
d += 1;
}
return this.startAndGoalToCommand(me.pos, tokenP);
}
this.findTokensAtDistance = function(p, d) {
if (d == 0) {
return [
[p[0], p[1]]
];
}
var myArr = [];
for (i = 0; i <= d; i++) {
myArr[i] = [i, d - i];
}
var mySecArr = [];
for (i = 0; i <= d; i++) {
mySecArr[i] = [myArr[i][0] + p[0], myArr[i][1] + p[1]];
}
mySecArr[mySecArr.length] = [myArr[0][0] + p[0], -myArr[0][1] + p[1]];
for (i = 1; i < myArr.length - 1; i++) {
mySecArr[mySecArr.length] = [-myArr[i][0] + p[0], myArr[i][1] + p[1]]
mySecArr[mySecArr.length] = [myArr[i][0] + p[0], -myArr[i][1] + p[1]]
mySecArr[mySecArr.length] = [-myArr[i][0] + p[0], -myArr[i][1] + p[1]]
}
mySecArr[mySecArr.length] = [-myArr[myArr.length - 1][0] + p[0], myArr[myArr.length - 1][1] + p[1]];
return mySecArr;
}
this.findBestToken = function(arr, t, player) {
var tokenPos;
for (i = 0; i < arr.length; i++) {
if (arr[i][0] >= 0 && arr[i][0] < t.length && arr[i][1] >= 0 && arr[i][1] < t.length) {
if (t[arr[i][0]][arr[i][1]] != false && ((tokenPos == undefined) || (this.tokenScore(player, t[arr[i][0]][arr[i][1]]) > this.tokenScore(player, t[tokenPos[0]][tokenPos[1]])))) {
tokenPos = [arr[i][0],
[arr[i][1]]
];
}
}
}
return tokenPos;
}
this.tokenScore = function(player, token) {
if (player.lastColor == token.color) {
return player.colorBonus + 1 + token.points;
} else {
return token.points;
}
}
this.startAndGoalToCommand = function(start, goal) {
var diff = [goal[0] - start[0], goal[1] - start[1]];
if (diff[0] > 0) {
return "RIGHT";
} else if (diff[1] > 0) {
return "DOWN";
} else if (diff[1] < 0) {
return "UP";
} else if (diff[0] < 0) {
return "LEFT";
} else {
return "EAT";
}
}
}
aiArray[0] = myAI;
// This is a really stupid AI, used to test that the round-robin selected
// it is the worst by far
myStupidAI = function(player1) {
this.player1 = player1;
this.yourMove = function(b) {
var me;
if (this.player1) {
me = b.player1;
} else {
me = b.player2;
}
if (b.tokens[me.pos[0]][me.pos[1]] != false) {
return "EAT";
} else {
var dirs = this.getViableDirections(b, me.pos);
var rand = Math.floor(Math.random() * dirs.length);
return dirs[rand];
}
}
this.getViableDirections = function(b, p) {
var dirs = [];
if (p[0] > 0) {
dirs.push("LEFT");
}
if (p[1] > 0) {
dirs.push("UP");
}
if (p[1] < b.tokens.length - 1) {
dirs.push("DOWN");
}
if (p[0] < b.tokens.length - 1) {
dirs.push("RIGHT");
}
return dirs;
}
}
aiArray[1] = myStupidAI;
mirrorBot = function(player1){
this.hasStarted=0;
this.player1 = player1;
this.yourMove = function(b){
this.op = this.player1 ? b.player2 : b.player1;
var out = "EAT";
if(this.hasStarted){
if(this.opl.pos[0] < this.op.pos[0]) out = "LEFT";
if(this.opl.pos[0] > this.op.pos[0]) out = "RIGHT";
if(this.opl.pos[1] < this.op.pos[1]) out = "UP";
if(this.opl.pos[1] > this.op.pos[1]) out = "DOWN";
}
this.opl = this.op;
this.hasStarted = 1;
return out;
}
}
aiArray[2] = mirrorBot;
hungryBot = function(first) {
// Set up "self"
var self = this;
// Determine player order
this.player = -(first - 2);
this.enemy = first + 1;
// Action associative array
this.actions = ['EAT', 'LEFT', 'RIGHT', 'UP', 'DOWN'];
//Logic handler
this.yourMove = function(board) {
// Determine player object
var player = board['player' + self.player];
var enemy = board['player' + self.enemy];
// Point value action grid
var actions = [0, 0, 0, 0, 0]; // Associative with "this.actions"
// Board dimensions
var size = board.tokens.length;
var maxDist = size * 2;
// Colors remaining
var colors = {
'#FF0000': 0,
'#00FF00': 0,
'#0000FF': 0
};
// Averaged value weight
var average = [0, 0];
// Total points
var points = 0;
// Token holder
var tokens = [];
// Token parser
for (var i = 0, x = 0, y = 0; i < size * size; i += 1, x = i % size, y = i / size | 0) {
if (!board.tokens[x][y]) {
continue;
} else {
var token = {};
token.points = board.tokens[x][y].points;
token.color = board.tokens[x][y].color;
token.x = x - player.pos[0];
token.y = y - player.pos[1];
token.distX = Math.abs(token.x);
token.distY = Math.abs(token.y);
token.dist = token.distX + token.distY;
token.distE = Math.abs(x - enemy.pos[0]) + Math.abs(y - enemy.pos[1]);
token.value = -token.points - (player.colorBonus + 1) * (token.color == player.lastColor) * ((token.dist == 0) + 1) * 1.618 - (enemy.colorBonus + 1) * (token.color == enemy.lastColor);
tokens.push(token);
colors[token.color] += 1;
points += token.points;
average[0] += x * token.points;
average[1] += y * token.points;
}
}
// Determine actual average
average[0] = average[0] / points | 0;
average[1] = average[1] / points | 0;
// Pick best token
var best = 0;
// Calculate point values of tokens
for (i = 0; i < tokens.length; i++) {
var token = tokens[i];
// Add remaining numbers of tokens of color as factor
token.value -= (colors[token.color] / tokens.length) * 1.618;
// Subtract distance as a factor
token.value += token.dist;
// Add distance to average to value
token.value += (Math.abs(average[0] - (token.x + player.pos[0])) + Math.abs(average[1] - (token.y + player.pos[1]))) / Math.sqrt(2);
// Consider them higher value if we are closer, and lower if they are
token.value += ((token.dist - token.distE) / (token.dist + token.distE + 0.001)) * token.dist;
// Don't go for it if enemy is already there
token.value += (token.distE == 0 && token.dist > 0) * 100;
if (tokens[best].value > tokens[i].value || (tokens[best].value === tokens[i].value && Math.round(Math.random()))) {
best = i;
}
}
// Set token to best token
var token = tokens[best];
// What to respond with
var response = 'PASS';
// Find best action to get token
if (token.dist == 0) {
response = 'EAT'; // We're on the token
} else if (token.distX >= token.distY) { // Token is more horizontal
if (token.x < 0) { // Token is left
response = 'LEFT';
} else if (token.x > 0) { // Token is right
response = 'RIGHT';
}
} else if (token.distX < token.distY) { // Token is more vertical
if (token.y < 0) { // Token is above
response = 'UP';
} else if (token.y > 0) { // Token is below
response = 'DOWN';
}
}
// Return response
return response;
}
};
aiArray[3]=hungryBot;
for (i = 0; i < aiArray.length; i += 1) {
Object.freeze(aiArray[i]);
}
// This is the actual code to run the program. You shouldn't mess with it,
// or your AI might not work during the actual competition.
// Also note that you can't access this code at all in your AI.
// Feel free to make suggestions and we'll try to incorporate them,
// and let us know if something isn't working correctly.
Object.freeze(aiArray);
setInterval((function() {
function token(color, points) {
this.color = color;
this.points = points;
}
function player(pos, score, colorBonus, lastColor) {
this.pos = pos;
this.score = score;
this.colorBonus = colorBonus;
this.lastColor = lastColor;
}
function board(player1, player2, tokens) {
this.player1 = player1;
this.player2 = player2;
this.tokens = tokens;
}
function copyBoard(b) {
return new board(copyPlayer(b.player1), copyPlayer(b.player2), copyTokens(b.tokens));
}
function copyTokens(t) {
var tokens = [];
for (i = 0; i < t.length; i++) {
tokens[i] = [];
for (j = 0; j < t[i].length; j++) {
tokens[i][j] = t[i][j];
}
}
return tokens;
}
function copyPlayer(p) {
return new player([p.pos[0], p.pos[1]], p.score, p.colorBonus, p.lastColor);
}
function endGame(b) {
if (b.player1.score > b.player2.score) {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.font = 20 + "px Arial";
ctx.textAlign = "left";
ctx.fillStyle = "#000000";
ctx.fillText("Player 1", 601, 160);
ctx.fillText("wins!", 601, 180);
} else if (b.player1.score < b.player2.score) {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.font = 20 + "px Arial";
ctx.textAlign = "left";
ctx.fillStyle = "#000000";
ctx.fillText("Player 2", 601, 160);
ctx.fillText("wins!", 601, 180);
} else {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.font = 20 + "px Arial";
ctx.textAlign = "left";
ctx.fillStyle = "#000000";
ctx.fillText("It's a", 601, 160);
ctx.fillText("draw!", 601, 180);
}
}
function genTokenArray(size) {
var temp = [];
for (i = 0; i < size; i++) {
temp[i] = [];
for (j = 0; j < size; j++) {
temp[i][j] = false;
}
}
return temp;
}
function genRandomTokenArray(size) {
var temp = genTokenArray(size);
for (i = 0; i < size * 2; i++) {
var rand = Math.floor(Math.random() * temp.length);
var points = Math.floor(Math.random() * 3 + 1);
var rand2 = Math.floor(Math.random() * 3);
var color;
var rand3 = Math.floor(Math.random() * temp[rand].length);
if (rand2 == 0) {
color = "#FF0000"
} else if (rand2 == 1) {
color = "#00FF00"
} else {
color = "#0000FF"
}
temp[rand][rand3] = new token(color, points);
}
return temp;
}
function countTokens(tokenArr) {
var x = 0;
for (i = 0; i < tokenArr.length; i++) {
for (j = 0; j < tokenArr[i].length; j++) {
if (tokenArr[i][j] != false) {
x += 1
}
}
}
return x;
}
function actPlayer(b, p, s) {
if (s == "UP" && p.pos[1] > 0) {
p.pos[1] -= 1;
} else if (s == "RIGHT" && p.pos[0] < b.tokens.length - 1) {
p.pos[0] += 1;
} else if (s == "DOWN" && p.pos[1] < b.tokens.length - 1) {
p.pos[1] += 1;
} else if (s == "LEFT" && p.pos[0] > 0) {
p.pos[0] -= 1;
} else if (s == "EAT" && b.tokens[p.pos[0]][p.pos[1]] != false) {
if (p.lastColor == b.tokens[p.pos[0]][p.pos[1]].color) {
p.colorBonus += 1;
p.score += p.colorBonus;
} else {
p.lastColor = b.tokens[p.pos[0]][p.pos[1]].color;
p.colorBonus = 0;
}
p.score += b.tokens[p.pos[0]][p.pos[1]].points;
b.tokens[p.pos[0]][p.pos[1]] = false;
}
}
function drawEmptyRect() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, 700, 600);
}
function drawToken(t, x, y, g) {
drawEntity(x, y, t.points, t.color, "#000000", 300 / g, g);
}
function drawLittleEntity(x, y, label, fillColor, labelColor, size, cX, cY, g) {
var delta = 600 / g;
var x1 = x * delta + cX * delta / 4;
var y1 = y * delta + cY * delta / 4;
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(x1, y1, size / 2, 0, 2 * Math.PI);
ctx.stroke();
ctx.fillStyle = fillColor;
ctx.fill();
ctx.font = delta / 2 + "px Arial";
ctx.textAlign = "center";
ctx.fillStyle = labelColor;
ctx.fillText(label, x1, y1 + delta * 3 / 16);
}
function drawEntity(x, y, label, fillColor, labelColor, size, g) {
var delta = 600 / g;
var x1 = x * delta + delta / 2;
var y1 = y * delta + delta / 2;
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(x1, y1, size, 0, 2 * Math.PI);
ctx.stroke();
ctx.fillStyle = fillColor;
ctx.fill();
ctx.font = delta + "px Arial";
ctx.textAlign = "center";
ctx.fillStyle = labelColor;
ctx.fillText(label, x1, y1 + delta * 3 / 8);
}
function drawAllTokens(arrToken) {
for (i = 0; i < arrToken.length; i++) {
for (j = 0; j < arrToken[i].length; j++) {
if (arrToken[i][j] != false) {
drawToken(arrToken[i][j], i, j, arrToken.length);
}
}
}
}
function drawGrid(g) {
var delta = 600 / g;
for (i = 0; i <= g; i++) {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(i * delta, 0);
ctx.lineTo(i * delta, 600);
ctx.stroke();
}
for (j = 0; j <= g; j++) {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(0, j * delta);
ctx.lineTo(600, j * delta);
ctx.stroke();
}
}
function drawPlayer1(b, g) {
drawLittleEntity(b.player1.pos[0], b.player1.pos[1], "F", "#000000", "#FFFFFF", 300 / g, 1, 1, g);
}
function drawPlayer2(b, g) {
drawLittleEntity(b.player2.pos[0], b.player2.pos[1], "S", "#000000", "#FFFFFF", 300 / g, 3, 3, g);
}
function drawScore(b) {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.font = "20px Arial";
ctx.fillStyle = "#000000";
ctx.textAlign = "left";
ctx.fillText("P" + indices[0] + " Score:", 601, 50);
ctx.fillText(b.player1.score, 601, 70);
ctx.fillText("P" + indices[1] + " Score:", 601, 100);
ctx.fillText(b.player2.score, 601, 120);
}
function drawScoreNum(s, i) {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.font = "20px Arial";
ctx.fillStyle = "#000000";
ctx.textAlign = "left";
ctx.fillText("P" + i + " Score:", 601, i * 50 + 50);
ctx.fillText(s, 601, i * 50 + 70);
}
function redraw(b, g) {
drawEmptyRect();
drawGrid(g);
drawScore(b);
drawAllTokens(b.tokens);
drawPlayer1(b, g);
drawPlayer2(b, g);
drawScoreNum()
}
function nextPair(pair, total) {
var pairRes = [pair[0], (pair[1] + 1) % total];
if (pairRes[1] == 0) {
pairRes[0] = pairRes[0] + 1;
}
return pairRes;
}
function nextGameUnfair() {
scores[indices[0]] = scores[indices[0]] + currBoard.player1.score;
scores[indices[1]] = scores[indices[1]] + currBoard.player2.score;
if (matchCount >= matches) {
matchCount = 0;
indices = nextPair(indices, aiArray.length);
if (indices[0] == indices[1]) {
indices = nextPair(indices, aiArray.length);
}
if (indices[0] >= aiArray.length) {
return false;
}
player1Turn = true;
timeWithoutEating = 0;
firstAI = new aiArray[indices[0]](true);
secondAI = new aiArray[indices[1]](false);
currBoard = genBoard();
} else {
matchCount++;
player1Turn = true;
timeWithoutEating = 0;
firstAI = new aiArray[indices[0]](true);
secondAI = new aiArray[indices[1]](false);
currBoard = genBoard();
}
return true;
}
function nextGameFair() {
scores[indices[0]] = scores[indices[0]] + currBoard.player1.score;
scores[indices[1]] = scores[indices[1]] + currBoard.player2.score;
indices = nextPair(indices, aiArray.length);
if (indices[0] == indices[1]) {
indices = nextPair(indices, aiArray.length);
}
if (indices[0] >= aiArray.length) {
indices = [0, 1];
matchCount++;
if (matchCount >= matches) {
return false;
}
player1Turn = true;
timeWithoutEating = 0;
currBoard = genBoard();
} else {
player1Turn = true;
timeWithoutEating = 0;
currBoard = savedBoard;
}
firstAI = new aiArray[indices[0]](true);
secondAI = new aiArray[indices[1]](false);
savedBoard = copyBoard(currBoard);
return true;
}
function genBoard() {
var rand = Math.floor(Math.random() * 11 + 5);
var randX = Math.floor(Math.random() * rand);
var randY = Math.floor(Math.random() * rand);
return new board(new player([randX, randY], 0, 0, "#000000"),
new player([randX, randY], 0, 0, "#000000"),
genRandomTokenArray(rand));
}
var running = true;
var indices = [0, 1];
var scores = []
for (i = 0; i < aiArray.length; i++) {
scores.push(0);
}
var matchCount = 0;
var firstAI = new aiArray[0](true);
var secondAI = new aiArray[1](false);
var player1Turn = true;
var currBoard = genBoard();
var savedBoard = copyBoard(currBoard);
var timeWithoutEating = 0;
return function() {
if (running) {
var numTokens = countTokens(currBoard.tokens);
if (numTokens <= 0) {} else if (player1Turn) {
actPlayer(currBoard, currBoard.player1, firstAI.yourMove(copyBoard(currBoard)));
} else {
actPlayer(currBoard, currBoard.player2, secondAI.yourMove(copyBoard(currBoard)));
}
player1Turn = !player1Turn;
redraw(currBoard, currBoard.tokens.length);
if (numTokens > countTokens(currBoard.tokens)) {
timeWithoutEating = 0;
} else {
timeWithoutEating += 1;
}
if (numTokens <= 0 || timeWithoutEating > 4 * currBoard.tokens.length) {
running = nextGameFair();
}
} else {
drawEmptyRect();
for (i = 0; i < scores.length; i++) {
drawScoreNum(scores[i], i);
}
}
}
})(), interval);
</script>
</body>
</html>
player1
booléen est le constructeur de votre IA, qui aura uneyourMove
fonction qui prend la carte actuelle en entrée, commeb
.Réponses:
HungryBot
Utilise un système de points pour ajouter du poids à la valeur de la poursuite de chaque jeton. Utilise une variété de facteurs différents dans son examen et les réévalue à chaque tour pour s'assurer qu'il suit la meilleure stratégie.
la source
self
:)self
? N'est-ce pasthis
suffisant?Bot PATH
Acronyme signifie Bot Pathfinding et Tree Heuristics
EDIT: Pour l'instant, voici le classement des IA, avec les points
Lien vers le contrôleur complet sur github
Description: Comme NaiveAI, ce bot trouve le jeton le plus proche qui lui donnera le plus de points. Cependant, il simule également les résultats de chacun de ses mouvements, jusqu'à 6 fois.
Justification: Parce que NaiveAI est déjà assez bon, je pensais que je ferais mieux. Sans regarder d'abord le code (grosse erreur).
Battements: tous sauf HungryBot
Perd à: aucun sauf HungryBot
Problèmes:
Peut se téléporterJe ne sais toujours pas pourquoi il se téléportait, mais je l'ai corrigé. Ancienne vidéo ici: https://youtu.be/BIhSKycF9iA
Code complet:
la source
NaiveAI
Commencez par
r=0
regarder tous les jetons dont la distance en taxi estr
éloignée de votre position. S'il y en a, choisissez-en un qui vous donnerait le meilleur score si vous l'aviez déjà. Sinon, augmentezr
de 1 et réessayez.la source
KindaRandomAI
À chaque tour, procédez comme suit: S'il y a un jeton à votre position, "EAT". Sinon, déplacez-vous dans une direction viable aléatoire, c'est-à-dire que si vous êtes sur le bord gauche, ne dites pas "GAUCHE".
la source
LazyBot
Ne mange quelque chose que s'il apparaît dessus. Cela n'a aucune chance de gagner, mais le défi n'en avait pas, alors pourquoi pas.
la source
MirrorBot
Doit être appelé "chair à canon"
Description: déplace exactement le contraire de ce que l'autre joueur a fait
Justification: Je voulais à nouveau obtenir une programmation confortable dans JS. Cela ne devrait pas gagner
Will beat: Personne
Perdra face à: Tout le monde
la source
var out = "EAT";
place deout = "EAT";
, car ce dernier définit une variable globale. Nitpicking un peu, les troisième et quatrième lignes ne font rien et peuvent également être supprimées, etop
peuvent être une variable locale commeout
au lieu d'une propriété.OneTarget
Trouve le jeton qui donnera le plus de points en moins de temps et ira pour celui-là. Classe les jetons de même couleur un peu plus haut en raison de l'effet cumulatif.
la source
QuantityPlayer
Tout ce que QuantityPlayer se soucie est la quantité de points qu'il mange, pas la valeur ou la couleur des points. Il sait que même si tous les points sont différents, ils doivent être traités de la même manière.
la source