Skip to content

Commit df3d88e

Browse files
committed
updates
1 parent b365249 commit df3d88e

4 files changed

Lines changed: 128 additions & 28 deletions

File tree

‎README.md‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,49 @@ npx parcel src/index.html
3737

3838
Runs on http://localhost:1234
3939

40+
## AI Snake
41+
42+
If you want to control the snake via an AI algorithm see the ai-init.js and ai-example files.
43+
44+
Essentially all you have to do is run `params.startAIGame();` when initializing and pass in a `moveSnakeWithAI` method
45+
which is run before the snake does each move.
46+
47+
```js
48+
moveSnakeWithAI: ({
49+
grid,
50+
snakeHead,
51+
currentDirection,
52+
isFirstGameMove,
53+
setDirection,
54+
}) => {
55+
56+
/*
57+
Direction:
58+
0
59+
3 1
60+
2
61+
*/
62+
63+
// This is NOT a real hamiltonian cycle. It misses some values, I'm just including this here as an example of
64+
// a look-up type table that you could do.
65+
const hamiltonianCycleGrid = [
66+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
67+
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0],
68+
[0, 0, 2, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 0],
69+
[0, 0, 2, 0, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0],
70+
[0, 0, 2, 0, 2, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0],
71+
[0, 0, 3, 0, 3, 3, 3, 3, 0, 3, 0, 3, 0, 3, 0],
72+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
73+
];
74+
75+
const newDirection = hamiltonianCycleGrid[snakeHead.row][snakeHead.col];
76+
setDirection(newDirection);
77+
},
78+
onInit: (params) => {
79+
params.startAIGame(); // This start an AI game
80+
},
81+
```
82+
4083
## Contributors
4184

4285
Thanks goes to these people: ([emoji key](https://allcontributors.org/docs/en/emoji-key))

‎src/ai-example.html‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124

125125
<div id="game-area" tabindex="0"></div>
126126
<script type="text/javascript" src="js/snake.js"></script>
127-
<script type="text/javascript" src="js/init.js"></script>
127+
<script type="text/javascript" src="js/ai-init.js"></script>
128128
<script type="text/javascript">
129129
function go_full_screen() {
130130
const elem = document.documentElement;

‎src/js/ai-init.js‎

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,40 @@ const mySnakeBoard = new SNAKE.Board({
22
boardContainer: "game-area",
33
fullScreen: true,
44
premoveOnPause: false,
5+
moveSnakeWithAI: ({
6+
grid,
7+
snakeHead,
8+
currentDirection,
9+
isFirstGameMove,
10+
setDirection,
11+
}) => {
12+
13+
/*
14+
Direction:
15+
0
16+
3 1
17+
2
18+
*/
19+
20+
// This is NOT a real hamiltonian cycle. It misses some values, I'm just including this here as an example of
21+
// a look-up type table that you could do.
22+
const hamiltonianCycleGrid = [
23+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
24+
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0],
25+
[0, 0, 2, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 0],
26+
[0, 0, 2, 0, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0],
27+
[0, 0, 2, 0, 2, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0],
28+
[0, 0, 3, 0, 3, 3, 3, 3, 0, 3, 0, 3, 0, 3, 0],
29+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
30+
]
31+
32+
console.log(JSON.parse(JSON.stringify(grid)))
33+
console.log(snakeHead, currentDirection)
34+
35+
const newDirection = hamiltonianCycleGrid[snakeHead.row][snakeHead.col];
36+
console.log(newDirection);
37+
setDirection(newDirection);
38+
},
539
onLengthUpdate: (length) => {
640
console.log(`Length: ${length}`);
741
},
@@ -11,11 +45,14 @@ const mySnakeBoard = new SNAKE.Board({
1145
onInit: (params) => {
1246
console.log("init!");
1347
console.log(params);
48+
params.startAIGame();
1449
},
15-
onWin: () => {
16-
console.log("wn!");
50+
onWin: (params) => {
51+
console.log("win!");
52+
//params.startAIGame();
1753
},
18-
onDeath: () => {
54+
onDeath: (params) => {
1955
console.log("dead!");
56+
//params.startAIGame();
2057
},
2158
});

‎src/js/snake.js‎

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,22 @@ SNAKE.Snake =
277277
return isPaused;
278278
};
279279

280+
/**
281+
* This method sets the snake direction
282+
* @param direction
283+
*/
284+
me.setDirection = (direction) => {
285+
if (currentDirection !== lastMove) {
286+
// Allow a queue of 1 premove so you can turn again before the first turn registers
287+
preMove = direction;
288+
}
289+
if (Math.abs(direction - lastMove) !== 2 || isFirstGameMove) {
290+
// Prevent snake from turning 180 degrees
291+
currentDirection = direction;
292+
isFirstGameMove = false;
293+
}
294+
};
295+
280296
/**
281297
* This method is called when a user presses a key. It logs arrow key presses in "currentDirection", which is used when the snake needs to make its next move.
282298
* @method handleArrowKeys
@@ -314,15 +330,7 @@ SNAKE.Snake =
314330
directionFound = MOVE_DOWN;
315331
break;
316332
}
317-
if (currentDirection !== lastMove) {
318-
// Allow a queue of 1 premove so you can turn again before the first turn registers
319-
preMove = directionFound;
320-
}
321-
if (Math.abs(directionFound - lastMove) !== 2 || isFirstGameMove) {
322-
// Prevent snake from turning 180 degrees
323-
currentDirection = directionFound;
324-
isFirstGameMove = false;
325-
}
333+
me.setDirection(directionFound);
326334
};
327335

328336
/**
@@ -766,7 +774,7 @@ SNAKE.Board =
766774

767775
let myFood,
768776
mySnake,
769-
boardState = 1, // 0: in active, 1: awaiting game start, 2: playing game
777+
boardState = BOARD_READY, // 0: in active, 1: awaiting game start, 2: playing game
770778
myKeyListener,
771779
myWindowListener,
772780
isPaused = false; //note: both the board and the snake can be paused
@@ -830,9 +838,12 @@ SNAKE.Board =
830838
elmHighscorePanel.innerHTML =
831839
"Highscore: " + (localStorage[HIGH_SCORE_KEY] || 0);
832840

833-
elmWelcome = createWelcomeElement();
834-
elmTryAgain = createTryAgainElement();
835-
elmWin = createWinElement();
841+
// if it's not AI, show the dialogs
842+
if (!config.moveSnakeWithAI) {
843+
elmWelcome = createWelcomeElement();
844+
elmTryAgain = createTryAgainElement();
845+
elmWin = createWinElement();
846+
}
836847

837848
SNAKE.addEventListener(
838849
elmContainer,
@@ -859,9 +870,13 @@ SNAKE.Board =
859870
elmContainer.appendChild(elmAboutPanel);
860871
elmContainer.appendChild(elmLengthPanel);
861872
elmContainer.appendChild(elmHighscorePanel);
862-
elmContainer.appendChild(elmWelcome);
863-
elmContainer.appendChild(elmTryAgain);
864-
elmContainer.appendChild(elmWin);
873+
874+
// nothing to attach if using AI
875+
if (!config.moveSnakeWithAI) {
876+
elmContainer.appendChild(elmWelcome);
877+
elmContainer.appendChild(elmTryAgain);
878+
elmContainer.appendChild(elmWin);
879+
}
865880

866881
mySnake = new SNAKE.Snake({
867882
playingBoard: me,
@@ -872,7 +887,9 @@ SNAKE.Board =
872887
});
873888
myFood = new SNAKE.Food({ playingBoard: me });
874889

875-
elmWelcome.style.zIndex = 1000;
890+
if (elmWelcome) {
891+
elmWelcome.style.zIndex = 1000;
892+
}
876893
}
877894

878895
function maxBoardWidth() {
@@ -975,10 +992,12 @@ SNAKE.Board =
975992
getNextHighestZIndex(mySnake.snakeBody),
976993
getNextHighestZIndex({ tmp: { elm: myFood.getFoodElement() } }),
977994
);
978-
elmContainer.removeChild(elmDialog);
979-
elmContainer.appendChild(elmDialog);
980-
elmDialog.style.zIndex = index;
981-
elmDialog.style.display = "block";
995+
if (elmDialog) {
996+
elmContainer.removeChild(elmDialog);
997+
elmContainer.appendChild(elmDialog);
998+
elmDialog.style.zIndex = index;
999+
elmDialog.style.display = "block";
1000+
}
9821001
me.setBoardState(BOARD_NOT_READY);
9831002
}
9841003

@@ -1021,6 +1040,7 @@ SNAKE.Board =
10211040
config.onLengthUpdate(1);
10221041
elmLengthPanel.innerHTML = "Length: 1";
10231042
me.setupPlayingField();
1043+
me.grid[getStartRow()][getStartCol()] = 1; // snake head
10241044
};
10251045
/**
10261046
* Gets the current state of the playing board. There are 3 states: 0 - Welcome or Try Again dialog is present. 1 - User has pressed "Start Game" on the Welcome or Try Again dialog but has not pressed an arrow key to move the snake. 2 - The game is in progress and the snake is moving.
@@ -1065,8 +1085,8 @@ SNAKE.Board =
10651085
}
10661086
elmContainer = myContainer;
10671087
elmPlayingField = null;
1068-
10691088
me.setupPlayingField();
1089+
me.grid[getStartRow()][getStartCol()] = 1; // snake head
10701090
};
10711091
/**
10721092
* @method getBoardContainer
@@ -1182,7 +1202,6 @@ SNAKE.Board =
11821202
}
11831203
}
11841204
}
1185-
me.grid[getStartRow()][getStartCol()] = 1; // snake head
11861205

11871206
myFood.randomlyPlaceFood();
11881207
config.onLengthUpdate(1);
@@ -1334,8 +1353,9 @@ SNAKE.Board =
13341353
};
13351354

13361355
me.startAIGame = () => {
1356+
me.resetBoard();
13371357
mySnake.rebirth();
1338-
me.setBoardState(2); // start the game!
1358+
me.setBoardState(BOARD_IN_PLAY); // start the game!
13391359
mySnake.go();
13401360
};
13411361

0 commit comments

Comments
 (0)