671 lines
15 KiB
JavaScript
671 lines
15 KiB
JavaScript
let grid = [];
|
|
let snake = [];
|
|
let alive = true;
|
|
let snakeDirection = 0;
|
|
let AItype = "random";
|
|
let gridSize = 20;
|
|
let gridColumns = 10;
|
|
let gridRows = 10;
|
|
let eatenApples = 0;
|
|
let _textSize = 16;
|
|
let appleAmount = 1;
|
|
let gameSpeed = 10;
|
|
let buttonAddColoums = null;
|
|
let buttonAddRows = null;
|
|
let buttonUpgradeAI = null;
|
|
let buttonAddMoreApples = null;
|
|
let buttonSpeedUpgrade = null;
|
|
let SpeedUpgradePrice = 5;
|
|
let AddMoreApplesPrice = 1;
|
|
let AddColoumsPrice = 1;
|
|
let AddRowsPrice = 1;
|
|
let UpgradeAIPrice = 1;
|
|
let buttonSnifferUP = null;
|
|
let buttonSnifferDown = null;
|
|
let sniffingDistance = 1;
|
|
let buttonEyeUp = null;
|
|
let buttonEyeDown = null;
|
|
let eyeDistance = 1;
|
|
let snifferUpgradePrice = 1;
|
|
let eyeUpgradePrice = 1;
|
|
let snifferLevel = 1;
|
|
let eyeLevel = 1;
|
|
let sniffingIncrement = 1;
|
|
let eyeIncrement = 2;
|
|
let sniffingPriceIncrement = 2;
|
|
let eyePriceIncrement = 2;
|
|
let moulthOpenDistance = 2;
|
|
|
|
|
|
function setup() {
|
|
buttonAddColoums = createButton("Add coloums");
|
|
buttonAddRows = createButton("Add rows");
|
|
buttonAddMoreApples = createButton("Add more apples");
|
|
buttonSpeedUpgrade = createButton("Increase speed");
|
|
buttonSnifferUP = createButton("SnifferUp");
|
|
buttonSnifferDown = createButton("SnifferDown");
|
|
buttonEyeUp = createButton("EyeUp");
|
|
buttonEyeDown = createButton("EyeDown");
|
|
createCanvas(windowWidth, windowHeight);
|
|
background(255, 255, 255);
|
|
for (let i = 0; i < gridColumns; i++) {
|
|
grid[i] = [];
|
|
for (let j = 0; j < gridRows; j++) {
|
|
grid[i][j] = 0;
|
|
}
|
|
}
|
|
buttonAddColoums.mousePressed(buttonAddColoumsPressed);
|
|
buttonAddRows.mousePressed(buttonAddRowsPressed);
|
|
buttonAddMoreApples.mousePressed(buttonAddMoreApplesPressed);
|
|
buttonSpeedUpgrade.mousePressed(buttonSpeedUpgradePressed);
|
|
buttonSnifferUP.mousePressed(buttonSnifferUPPressed);
|
|
buttonSnifferDown.mousePressed(buttonSnifferDownPressed);
|
|
buttonEyeUp.mousePressed(buttonEyeUpPressed);
|
|
buttonEyeDown.mousePressed(buttonEyeDownPressed);
|
|
frameRate(gameSpeed);
|
|
summonSnake();
|
|
summonApple();
|
|
drawGrid();
|
|
}
|
|
|
|
|
|
function buttonSnifferUPPressed(){
|
|
if (sniffingDistance > gridRows) {
|
|
return;
|
|
}
|
|
if (snifferUpgradePrice > eatenApples) {
|
|
return;
|
|
}
|
|
if (!(snifferLevel * sniffingIncrement > sniffingDistance)) {
|
|
eatenApples -= snifferUpgradePrice;
|
|
snifferUpgradePrice += sniffingPriceIncrement;
|
|
snifferLevel ++;
|
|
}
|
|
|
|
sniffingDistance += sniffingIncrement;
|
|
}
|
|
function buttonSnifferDownPressed(){
|
|
if (sniffingDistance < 1) {
|
|
return;
|
|
}
|
|
sniffingDistance -= sniffingIncrement;
|
|
}
|
|
|
|
function buttonSpeedUpgradePressed(){
|
|
|
|
if (eatenApples < SpeedUpgradePrice) {
|
|
return;
|
|
}
|
|
|
|
eatenApples -= SpeedUpgradePrice;
|
|
|
|
gameSpeed += 2;
|
|
|
|
frameRate(gameSpeed);
|
|
|
|
SpeedUpgradePrice += 5;
|
|
}
|
|
|
|
function buttonEyeUpPressed(){
|
|
if (eyeDistance > gridColumns) {
|
|
return;
|
|
}
|
|
if (!(eyeLevel * eyeIncrement + 1 >= eyeDistance) && eyeUpgradePrice <= eatenApples) {
|
|
eatenApples -= eyeUpgradePrice;
|
|
eyeUpgradePrice += eyePriceIncrement;
|
|
eyeLevel ++;
|
|
}else if(eyeUpgradePrice > eatenApples){
|
|
return;
|
|
}
|
|
|
|
eyeDistance += eyeIncrement;
|
|
}
|
|
function buttonEyeDownPressed(){
|
|
if (eyeDistance < 1) {
|
|
return;
|
|
}
|
|
eyeDistance -= eyeIncrement;
|
|
}
|
|
|
|
function buttonAddColoumsPressed(){
|
|
if (eatenApples < AddColoumsPrice) {
|
|
return;
|
|
}
|
|
eatenApples -= AddColoumsPrice;
|
|
gridColumns++;
|
|
grid.push([]);
|
|
for (let i = 0; i < gridRows; i++) {
|
|
grid[grid.length - 1][i] = 0;
|
|
}
|
|
}
|
|
function buttonAddRowsPressed(){
|
|
if (eatenApples < AddRowsPrice) {
|
|
return;
|
|
}
|
|
eatenApples -= AddRowsPrice;
|
|
gridRows++;
|
|
for (let i = 0; i < gridColumns; i++) {
|
|
grid[i].push(0);
|
|
}
|
|
}
|
|
|
|
function buttonAddMoreApplesPressed(){
|
|
if (eatenApples < AddMoreApplesPrice) {
|
|
return;
|
|
}
|
|
eatenApples -= AddMoreApplesPrice;
|
|
if (appleAmount < 5){
|
|
AddMoreApplesPrice += 1;
|
|
}else if (appleAmount < 10){
|
|
AddMoreApplesPrice += 2;
|
|
}else if (appleAmount < 20){
|
|
AddMoreApplesPrice += 3;
|
|
}else if (appleAmount < 50){
|
|
AddMoreApplesPrice += 4;
|
|
}else{
|
|
AddMoreApplesPrice += ceil(AddMoreApplesPrice*0.15);
|
|
}
|
|
summonApple();
|
|
appleAmount++;
|
|
}
|
|
|
|
function summonApple(){
|
|
if (appleAmount >= gridColumns * gridRows - snake.length ) {
|
|
for (let i = 0; i < grid.length; i++) {
|
|
for (let j = 0; j < grid[0].length; j++) {
|
|
if (grid[i][j] == 2) {
|
|
grid[i][j] = 0;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
let x = grid.length - floor(random(1, grid.length +1));
|
|
let y = grid[0].length - floor(random(1, grid[0].length +1));
|
|
if(grid[x][y] == 2 || grid[x][y] == 1){
|
|
summonApple();//best practice coding right here
|
|
return;
|
|
}
|
|
grid[x][y] = 1;
|
|
}
|
|
|
|
|
|
|
|
function dontMoveBackwards(lastSnakeX, lastSnakeY, currentSnakeX, currentSnakeY, direction){
|
|
if (direction == 0) {
|
|
return lastSnakeX < currentSnakeX;
|
|
}else if (direction == 1) {
|
|
return lastSnakeY < currentSnakeY;
|
|
}else if (direction == 2) {
|
|
return lastSnakeX > currentSnakeX;
|
|
}else if (direction == 3) {
|
|
return lastSnakeY > currentSnakeY;
|
|
}
|
|
}
|
|
|
|
function restartGame(){
|
|
|
|
for (let i = 0; i < grid.length; i++) {
|
|
for (let j = 0; j < grid[0].length; j++) {
|
|
|
|
if (grid[i][j] == 2 || grid[i][j] == 1) {
|
|
grid[i][j] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
snake = [];
|
|
|
|
summonSnake();
|
|
|
|
for (let i = 0; i < appleAmount; i++) {
|
|
summonApple();
|
|
}
|
|
|
|
alive = true;
|
|
}
|
|
|
|
function runSnake(){
|
|
let x = snake[0][0];
|
|
let y = snake[0][1];
|
|
for (let i = snake.length - 1; i > 0; i--) {
|
|
snake[i][0] = snake[i - 1][0];
|
|
snake[i][1] = snake[i - 1][1];
|
|
}
|
|
snakeAI();
|
|
let lastSnakeX = snake[1][0]
|
|
let lastSnakeY = snake[1][1];
|
|
if (dontMoveBackwards(lastSnakeX, lastSnakeY, x, y, snakeDirection)) {
|
|
snakeDirection = (snakeDirection + 2) % 4;
|
|
}
|
|
if (snakeDirection == 0) {
|
|
snake[0][0] += 1;
|
|
}else if (snakeDirection == 1) {
|
|
snake[0][1] += 1;
|
|
}else if (snakeDirection == 2) {
|
|
snake[0][0] -= 1;
|
|
}else if (snakeDirection == 3) {
|
|
snake[0][1] -= 1;
|
|
}
|
|
if (snake[0][1] >= grid[0].length || snake[0][1] < 0 || snake[0][0] >= grid.length || snake[0][0] < 0) {
|
|
alive = false;
|
|
return;
|
|
}
|
|
if (grid[snake[0][0]][snake[0][1]] == 2) {
|
|
alive = false;
|
|
return;
|
|
}
|
|
if (grid[snake[0][0]][snake[0][1]] == 1) {
|
|
snake.push([snake[snake.length - 1][0], snake[snake.length - 1][1]]);
|
|
summonApple();
|
|
eatenApples++;
|
|
}
|
|
if (
|
|
snake.length >=
|
|
(gridColumns * gridRows) - appleAmount
|
|
){
|
|
restartGame();
|
|
return;
|
|
}
|
|
drawSnakeOnGrid();
|
|
}
|
|
|
|
function summonSnake(){
|
|
let x = grid.length - floor(random(1, grid.length));
|
|
let y = grid[0].length - floor(random(1, grid[0].length));
|
|
snake.push([x, y]);
|
|
snake.push([x - 1, y]);
|
|
snake.push([x - 2, y]);
|
|
drawSnakeOnGrid();
|
|
}
|
|
|
|
function drawSnakeOnGrid(){
|
|
for (let i = 0; i < grid.length; i++) {
|
|
for (let j = 0; j < grid[0].length; j++) {
|
|
if (grid[i][j] == 2) {
|
|
grid[i][j] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < snake.length; i++) {
|
|
|
|
let x = snake[i][0];
|
|
let y = snake[i][1];
|
|
|
|
// prevent crash
|
|
if (
|
|
x < 0 ||
|
|
y < 0 ||
|
|
x >= grid.length ||
|
|
y >= grid[0].length
|
|
){
|
|
alive = false;
|
|
return;
|
|
}
|
|
|
|
grid[x][y] = 2;
|
|
}
|
|
}
|
|
|
|
function drawGrid() {
|
|
for (let i = 0; i < grid.length; i++) {
|
|
for (let j = 0; j < grid[0].length; j++) {
|
|
switch (grid[i][j]) {
|
|
case 1:
|
|
strokeWeight(0)
|
|
fill(255, 0, 0);
|
|
circle(i*gridSize+gridSize/2, j*gridSize+gridSize/2, gridSize-2);
|
|
break;
|
|
case 2:
|
|
if (i == snake[0][0] && j == snake[0][1]) {
|
|
strokeWeight(0)
|
|
fill(0, 233, 0);
|
|
rect(i * gridSize, j * gridSize, gridSize, gridSize);
|
|
fill(100, 255, 100);
|
|
circle(i*gridSize+gridSize/2, j*gridSize+gridSize/2, gridSize-2);
|
|
switch (snakeDirection) {
|
|
case 0://right
|
|
makeEye(i*gridSize+gridSize/4, j*gridSize+gridSize/4);
|
|
makeEye(i*gridSize+gridSize/4, j*gridSize+gridSize/4*3);
|
|
break;
|
|
case 1://down
|
|
makeEye(i*gridSize+gridSize/4, j*gridSize+gridSize/4);
|
|
makeEye(i*gridSize+gridSize/4*3, j*gridSize+gridSize/4);
|
|
break;
|
|
case 2://left
|
|
makeEye(i*gridSize+gridSize/4*3, j*gridSize+gridSize/4);
|
|
makeEye(i*gridSize+gridSize/4*3, j*gridSize+gridSize/4*3);
|
|
break;
|
|
case 3://up
|
|
makeEye(i*gridSize+gridSize/4, j*gridSize+gridSize/4*3);
|
|
makeEye(i*gridSize+gridSize/4*3, j*gridSize+gridSize/4*3);
|
|
break;
|
|
default:
|
|
throw new Error("Invalid snakeDirection: " + snakeDirection);
|
|
break;
|
|
}
|
|
makeMoulth(snakeDirection,i,j);
|
|
}else{
|
|
const snakeIndex = getSnakePartIndex(i, j);
|
|
if (snakeIndex == -1) {
|
|
console.error("Snake part not found: " + i + ", " + j);
|
|
}
|
|
let SnakeGreen = 255-snakeIndex*10;
|
|
let SnakeBlue = 0;
|
|
let SnakeRed = 0;
|
|
if (SnakeGreen < 0) {
|
|
SnakeBlue = abs(SnakeGreen);
|
|
}
|
|
if (SnakeBlue > 255) {
|
|
SnakeRed = SnakeBlue-255;
|
|
}
|
|
|
|
strokeWeight(0)
|
|
fill(SnakeRed, SnakeGreen, SnakeBlue);
|
|
rect(i * gridSize, j * gridSize, gridSize, gridSize);
|
|
}
|
|
break;
|
|
default:
|
|
strokeWeight(0.3)
|
|
fill(255, 255, 255);
|
|
rect(i * gridSize, j * gridSize, gridSize, gridSize);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function makeEye(x,y){
|
|
if (countFreeNeighbors(snake[0][0], snake[0][1]) == 0) {
|
|
//make dead eyes
|
|
fill(0, 0, 0);
|
|
strokeWeight(2);
|
|
line(x-gridSize/16, y-gridSize/16, x+gridSize/16, y+gridSize/16);
|
|
line(x-gridSize/16, y+gridSize/16, x+gridSize/16, y-gridSize/16);
|
|
return;
|
|
}
|
|
fill(255, 255, 255);
|
|
circle(x, y, gridSize/4);
|
|
fill(25, 255, 25);
|
|
circle(x, y, gridSize/8);
|
|
fill(0, 0, 0);
|
|
circle(x, y, gridSize/16);
|
|
}
|
|
function makeMoulth(direction,i,j){
|
|
let x, y, w, h;
|
|
fill(0, 0, 0);
|
|
if (moulthOpenDistance >= getClosestAppleDistanceToSnake()) {
|
|
switch (direction) {
|
|
case 0://right
|
|
x = gridSize*i+gridSize/16*11;
|
|
y = gridSize*j+gridSize/2;
|
|
w = gridSize/8*5;
|
|
h = gridSize/4*3;
|
|
break;
|
|
case 1://down
|
|
x = gridSize*i+gridSize/2;
|
|
y = gridSize*j+gridSize/16*11;
|
|
w = gridSize/4*3;
|
|
h = gridSize/8*5;
|
|
break;
|
|
case 2://left
|
|
x = gridSize*i+gridSize/16*5;
|
|
y = gridSize*j+gridSize/2;
|
|
w = gridSize/8*5;
|
|
h = gridSize/4*3;
|
|
break;
|
|
case 3://up
|
|
x = gridSize*i+gridSize/2;
|
|
y = gridSize*j+gridSize/16*5;
|
|
w = gridSize/4*3;
|
|
h = gridSize/8*5;
|
|
break;
|
|
default:
|
|
throw new Error("Invalid direction SnakeDirection: " + snakeDirection);
|
|
break;
|
|
|
|
}
|
|
ellipse(x, y, w, h)
|
|
}else{
|
|
switch (direction) {
|
|
case 0://right
|
|
x = gridSize*i+gridSize/16*13;
|
|
y = gridSize*j+gridSize/2;
|
|
w = gridSize/16;
|
|
h = gridSize/4*3;
|
|
break;
|
|
case 1://down
|
|
x = gridSize*i+gridSize/2;
|
|
y = gridSize*j+gridSize/16*13;
|
|
w = gridSize/4*3;
|
|
h = gridSize/16;
|
|
break;
|
|
case 2://left
|
|
x = gridSize*i+gridSize/16;
|
|
y = gridSize*j+gridSize/2;
|
|
w = gridSize/16;
|
|
h = gridSize/4*3;
|
|
break;
|
|
case 3://up
|
|
x = gridSize*i+gridSize/2;
|
|
y = gridSize*j+gridSize/16;
|
|
w = gridSize/4*3;
|
|
h = gridSize/16;
|
|
break;
|
|
default:
|
|
throw new Error("Invalid direction SnakeDirection: " + snakeDirection);
|
|
break;
|
|
}
|
|
ellipse(x, y, w, h)
|
|
}
|
|
}
|
|
|
|
function getSnakePartIndex(x, y){
|
|
for (let i = 0; i < snake.length; i++) {
|
|
if (snake[i][0] == x && snake[i][1] == y) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
function draw() {
|
|
|
|
if (alive == false){
|
|
restartGame();
|
|
}
|
|
|
|
background(255);
|
|
|
|
runSnake();
|
|
drawGrid();
|
|
|
|
fill(0, 0, 0);
|
|
textSize(_textSize);
|
|
|
|
text("₴ " + eatenApples, gridColumns * gridSize + gridSize + _textSize*3, _textSize * 1);
|
|
text("More apples: " + appleAmount, gridColumns * gridSize + gridSize, _textSize * 2);
|
|
|
|
buttonAddMoreApples.html("Add more apples ₴ " + AddMoreApplesPrice);
|
|
buttonAddMoreApples.position(gridColumns * gridSize + gridSize + _textSize*7, _textSize * 1.1);
|
|
|
|
text("AI Upgrades ", gridColumns * gridSize + gridSize, _textSize * 3);
|
|
text("Better Eyes: " + eyeLevel, gridColumns * gridSize + gridSize, _textSize * 4);
|
|
|
|
buttonEyeUp.html("Upgrade Eyes ₴ " + eyeUpgradePrice);
|
|
buttonEyeUp.position(gridColumns * gridSize + gridSize + _textSize*7, _textSize * 3.1);
|
|
buttonEyeDown.html("Downgrade Eyes");
|
|
buttonEyeDown.position(gridColumns * gridSize + gridSize + _textSize*13, _textSize * 3.1);
|
|
|
|
text("Better Sniffer: " + snifferLevel, gridColumns * gridSize + gridSize, _textSize * 5);
|
|
|
|
buttonSnifferUP.mousePressed(buttonSnifferUPPressed);
|
|
buttonSnifferUP.html("Upgrade Sniffer ₴ " + snifferUpgradePrice);
|
|
buttonSnifferUP.position(gridColumns * gridSize + gridSize + _textSize*8, _textSize * 4.1);
|
|
buttonSnifferDown.html("Downgrade Sniffer");
|
|
buttonSnifferDown.position(gridColumns * gridSize + gridSize + _textSize*14, _textSize * 4.1);
|
|
}
|
|
function mousePressed() {
|
|
}
|
|
|
|
function snakeAI(){
|
|
direction = floor(random(0, 4));
|
|
count = 0;
|
|
while (!isDirectionSafe(direction)) {
|
|
count++;
|
|
if (count > 20) {
|
|
snakeDirection =0;
|
|
return;
|
|
}
|
|
direction = floor(random(0, 4));
|
|
if (sniffingDistance >= getClosestAppleDistanceToSnake()) {
|
|
console.log("sniffing apple");
|
|
direction = pathSnakeToPoint(getClosestApple()[0], getClosestApple()[1]);
|
|
|
|
}
|
|
if (isAppleInView(snakeDirection, eyeDistance)) {
|
|
console.log("apple in view");
|
|
direction = snakeDirection;
|
|
}
|
|
}
|
|
snakeDirection = direction;
|
|
}
|
|
|
|
function directionToVector(direction){
|
|
|
|
switch(direction){
|
|
case 0:
|
|
return [1, 0];
|
|
|
|
case 1:
|
|
return [0, 1];
|
|
|
|
case 2:
|
|
return [-1, 0];
|
|
|
|
case 3:
|
|
return [0, -1];
|
|
}
|
|
}
|
|
|
|
function pathSnakeToPoint(x,y){
|
|
let vec = [x - snake[0][0], y - snake[0][1]];
|
|
|
|
if (vec[0] > 0){
|
|
return 0;
|
|
}else if (vec[0] < 0){
|
|
return 2;
|
|
}else if (vec[1] > 0){
|
|
return 1;
|
|
}else if (vec[1] < 0){
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
function isDirectionSafe(direction){
|
|
|
|
let vec = directionToVector(direction);
|
|
|
|
let newX = snake[0][0] + vec[0];
|
|
let newY = snake[0][1] + vec[1];
|
|
|
|
if (
|
|
newX < 0 ||
|
|
newY < 0 ||
|
|
newX >= grid.length ||
|
|
newY >= grid[0].length
|
|
){
|
|
return false;
|
|
}
|
|
|
|
if (grid[newX][newY] == 2){
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function getClosestApple(){
|
|
|
|
let bestApple = null;
|
|
let bestDistance = Infinity;
|
|
|
|
for (let i = 0; i < grid.length; i++) {
|
|
for (let j = 0; j < grid[0].length; j++) {
|
|
|
|
if (grid[i][j] == 1){
|
|
|
|
let distance =
|
|
abs(snake[0][0] - i) +
|
|
abs(snake[0][1] - j);
|
|
|
|
if (distance < bestDistance){
|
|
bestDistance = distance;
|
|
bestApple = [i, j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestApple;
|
|
}
|
|
|
|
function getClosestAppleDistanceToPoint(x, y){
|
|
let apple = getClosestApple();
|
|
if (apple == null){
|
|
return Infinity;
|
|
}
|
|
return abs(x - apple[0]) + abs(y - apple[1]);
|
|
}
|
|
|
|
function getClosestAppleDistanceToSnake(){
|
|
return getClosestAppleDistanceToPoint(snake[0][0], snake[0][1]);
|
|
}
|
|
|
|
function isAppleInView(snakeDirection, viewDistance){
|
|
|
|
let vec = directionToVector(snakeDirection);
|
|
|
|
for (let i = 0; i < viewDistance; i++) {
|
|
if (i >= grid.length || i >= grid[0].length|| snake[0][0]+(vec[0] *i) >= grid.length || snake[0][0]+(vec[0] *i) < 0 || snake[0][1]+(vec[1] *i) >= grid[0].length || snake[0][1]+(vec[1] *i) < 0){
|
|
return false;
|
|
}
|
|
|
|
if (grid[snake[0][0] + (vec[0] * i)][snake[0][1] + (vec[1] * i)] == 1){
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
} |