Created
September 30, 2020 07:47
-
-
Save shrinktofit/10a8330db47545167e3d2784474b57a3 to your computer and use it in GitHub Desktop.
Simple snake implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface IVec2 { | |
x: number; | |
y: number; | |
} | |
interface IBounds { | |
min: IVec2; | |
max: IVec2; | |
} | |
/** | |
* 方向。 | |
*/ | |
enum Direction { | |
up, | |
down, | |
left, | |
right, | |
} | |
interface ISnakeBodySegment { | |
/** | |
* 身子的方向。起点是头部或者上一段身子的尾部。 | |
*/ | |
direction: Direction; | |
/** | |
* 身子的长度。 | |
*/ | |
length: number; | |
} | |
/** | |
* 贪吃蛇的抽象定义。 | |
*/ | |
interface ISnake { | |
/** | |
* 蛇的头部点。 | |
*/ | |
head: IVec2; | |
/** | |
* 蛇身子的每一段。 | |
*/ | |
body: ISnakeBodySegment[]; | |
} | |
/** | |
* 让贪吃蛇前进一步。 | |
* @param snake | |
*/ | |
function step(snake: ISnake) { | |
const firstBody = snake.body[0]; | |
switch (firstBody.direction) { | |
case Direction.left: --snake.head.x; break; | |
case Direction.right: ++snake.head.y; break; | |
case Direction.up: ++snake.head.y; break; | |
case Direction.down: --snake.head.y; break; | |
} | |
} | |
/** | |
* 让贪吃蛇转个弯。 | |
* @param snake | |
* @param direction | |
*/ | |
function turn(snake: ISnake, direction: Direction) { | |
const firstBody = snake.body[0]; | |
let isValid = false; | |
switch (direction) { | |
case Direction.left: | |
case Direction.right: | |
if (firstBody.direction !== Direction.left && | |
firstBody.direction !== Direction.right) { | |
isValid = true; | |
} | |
break; | |
case Direction.up: | |
case Direction.down: | |
if (firstBody.direction !== Direction.up && | |
firstBody.direction !== Direction.down) { | |
isValid = true; | |
} | |
break; | |
} | |
if (isValid) { | |
// 添加一节身体到最前面,长度是 1 | |
snake.body.unshift({ direction, length: 1 }); | |
// 减去最后一节身体的长度 | |
const lastBody = snake.body[snake.body.length - 1]; | |
--lastBody.length; | |
// 如果最后一节身体长度是 0,就销毁它 | |
if (lastBody.length === 0) { | |
snake.body.pop(); | |
} | |
} | |
} | |
/** | |
* 判断贪吃蛇是否自杀了(咬到自己了)。 | |
* @param snake | |
*/ | |
function selfKilled(snake: ISnake) { | |
return ifSomeBodyPoint(snake, (bodyPoint) => bodyPoint.x === snake.head.x && bodyPoint.y === snake.head.y); | |
} | |
/** | |
* 判断贪吃蛇是否触碰到边界了。 | |
* @param snake | |
* @param bounds | |
*/ | |
function touchBounds(snake: ISnake, bounds: IBounds) { | |
return ifSomeBodyPoint(snake, (bodyPoint) => isVec2OutOfBounds(bodyPoint, bounds)); | |
function isVec2OutOfBounds(point: IVec2, bounds: IBounds) { | |
return isNumberOutOfBounds(point.x, bounds.min.x, bounds.max.x) || | |
isNumberOutOfBounds(point.y, bounds.min.y, bounds.max.y); | |
} | |
function isNumberOutOfBounds(v: number, min: number, max: number) { | |
return v <= min || v >= max; | |
} | |
} | |
function ifSomeBodyPoint(snake: ISnake, visitor: (bodyPoint: IVec2) => boolean) { | |
let bodyPoint = Object.assign({}, snake.head); | |
for (const segment of snake.body) { | |
for (let i = 0; i < segment.length; ++i) { | |
switch (segment.direction) { | |
case Direction.left: | |
++bodyPoint.x; | |
break; | |
case Direction.right: | |
--bodyPoint.x; | |
break; | |
case Direction.up: | |
--bodyPoint.y; | |
break; | |
case Direction.down: | |
++bodyPoint.y; | |
break; | |
} | |
if (visitor(bodyPoint)) { | |
return true; | |
} | |
} | |
} | |
return false; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment