diff --git a/sketches/snake.js b/sketches/snake.js index 188f28c..8f3c9ac 100644 --- a/sketches/snake.js +++ b/sketches/snake.js @@ -98,18 +98,28 @@ function buttonUpgradeAIPressed(){ break; case "wallDetection": + AItype = "nearApple"; + UpgradeAIPrice = 12; + break; + + case "nearApple": AItype = "appleBias"; - UpgradeAIPrice = 15; + UpgradeAIPrice = 25; break; case "appleBias": AItype = "snakeDetection"; - UpgradeAIPrice = 35; + UpgradeAIPrice = 50; break; case "snakeDetection": AItype = "smart"; - UpgradeAIPrice = 100; + UpgradeAIPrice = 120; + break; + + case "smart": + AItype = "survival"; + UpgradeAIPrice = 300; break; } } @@ -120,15 +130,15 @@ function buttonAddMoreApplesPressed(){ } eatenApples -= AddMoreApplesPrice; if (appleAmount < 5){ - AddMoreApplesPrice = 1; + AddMoreApplesPrice += 1; }else if (appleAmount < 10){ - AddMoreApplesPrice = 2; + AddMoreApplesPrice += 2; }else if (appleAmount < 20){ - AddMoreApplesPrice = 3; + AddMoreApplesPrice += 3; }else if (appleAmount < 50){ - AddMoreApplesPrice = 4; + AddMoreApplesPrice += 4; }else{ - AddMoreApplesPrice = ceil(AddMoreApplesPrice*0.15); + AddMoreApplesPrice += ceil(AddMoreApplesPrice*0.15); } summonApple(); appleAmount++; @@ -339,6 +349,10 @@ function snakeAI(){ snakeAIwallDetection(); break; + case "nearApple": + snakeAInearApple(); + break; + case "appleBias": snakeAIappleBias(); break; @@ -350,9 +364,28 @@ function snakeAI(){ case "smart": snakeAIsmart(); break; + + case "survival": + snakeAIsurvival(); + break; } } +function getAppleVisionDistance(){ + + if (appleAmount < 5){ + return 2; + }else if (appleAmount < 15){ + return 3; + }else if (appleAmount < 30){ + return 5; + }else if (appleAmount < 60){ + return 8; + } + + return 12; +} + function directionToVector(direction){ switch(direction){ @@ -418,6 +451,36 @@ function getClosestApple(){ return bestApple; } +function countFreeNeighbors(x, y){ + + let count = 0; + + let dirs = [ + [1,0], + [0,1], + [-1,0], + [0,-1] + ]; + + for (let i = 0; i < dirs.length; i++) { + + let nx = x + dirs[i][0]; + let ny = y + dirs[i][1]; + + if ( + nx >= 0 && + ny >= 0 && + nx < grid.length && + ny < grid[0].length && + grid[nx][ny] != 2 + ){ + count++; + } + } + + return count; +} + function snakeAIrandom() { snakeDirection = floor(random(0, 4)); } @@ -439,6 +502,59 @@ function snakeAIwallDetection(){ } } +function snakeAInearApple(){ + + let visionDistance = getAppleVisionDistance(); + + for (let i = 0; i < grid.length; i++) { + for (let j = 0; j < grid[0].length; j++) { + + if (grid[i][j] != 1){ + continue; + } + + let distance = + abs(snake[0][0] - i) + + abs(snake[0][1] - j); + + if (distance <= visionDistance){ + + let possibleDirections = []; + + if (i > snake[0][0]){ + possibleDirections.push(0); + } + + if (j > snake[0][1]){ + possibleDirections.push(1); + } + + if (i < snake[0][0]){ + possibleDirections.push(2); + } + + if (j < snake[0][1]){ + possibleDirections.push(3); + } + + for (let k = 0; k < possibleDirections.length; k++) { + + if (isDirectionSafe(possibleDirections[k])){ + + // 70% chance to follow apple + if (random() < 0.7){ + snakeDirection = possibleDirections[k]; + return; + } + } + } + } + } + } + + snakeAIwallDetection(); +} + function snakeAIappleBias(){ let apple = getClosestApple(); @@ -448,28 +564,37 @@ function snakeAIappleBias(){ return; } - let possibleDirections = []; + let preferredDirections = []; if (apple[0] > snake[0][0]){ - possibleDirections.push(0); + preferredDirections.push(0); } if (apple[1] > snake[0][1]){ - possibleDirections.push(1); + preferredDirections.push(1); } if (apple[0] < snake[0][0]){ - possibleDirections.push(2); + preferredDirections.push(2); } if (apple[1] < snake[0][1]){ - possibleDirections.push(3); + preferredDirections.push(3); } - for (let i = 0; i < possibleDirections.length; i++) { + // 65% chance to go toward apple + if ( + preferredDirections.length > 0 && + random() < 0.65 + ){ - if (isDirectionSafe(possibleDirections[i])){ - snakeDirection = possibleDirections[i]; + let direction = + preferredDirections[ + floor(random(preferredDirections.length)) + ]; + + if (isDirectionSafe(direction)){ + snakeDirection = direction; return; } } @@ -508,7 +633,7 @@ function snakeAIsmart(){ } let bestDirection = snakeDirection; - let bestDistance = Infinity; + let bestScore = -999999; for (let i = 0; i < 4; i++) { @@ -525,9 +650,16 @@ function snakeAIsmart(){ abs(newX - apple[0]) + abs(newY - apple[1]); - if (distance < bestDistance){ + let freeNeighbors = + countFreeNeighbors(newX, newY); - bestDistance = distance; + let score = + (-distance * 3) + + (freeNeighbors * 8); + + if (score > bestScore){ + + bestScore = score; bestDirection = i; } } @@ -535,6 +667,52 @@ function snakeAIsmart(){ snakeDirection = bestDirection; } -function snakeAIrandomNowallHit(){ - snakeDirection = floor(random(0, 4)); +function snakeAIsurvival(){ + + let bestDirection = snakeDirection; + let bestScore = -999999; + + let apple = getClosestApple(); + + for (let i = 0; i < 4; i++) { + + if (!isDirectionSafe(i)){ + continue; + } + + let vec = directionToVector(i); + + let newX = snake[0][0] + vec[0]; + let newY = snake[0][1] + vec[1]; + + let freeNeighbors = + countFreeNeighbors(newX, newY); + + let score = freeNeighbors * 20; + + if (apple != null){ + + let distance = + abs(newX - apple[0]) + + abs(newY - apple[1]); + + score -= distance; + } + + // avoid tunnels + if (freeNeighbors <= 1){ + score -= 100; + } + + // slight randomness + score += random(-3, 3); + + if (score > bestScore){ + + bestScore = score; + bestDirection = i; + } + } + + snakeDirection = bestDirection; } \ No newline at end of file