Created
February 11, 2020 13:37
-
-
Save embarq/30eadd435d6c7ce53316ad59f311e408 to your computer and use it in GitHub Desktop.
Solution to Codewars kata "Snake Collision" https://www.codewars.com/kata/5ac616ccbc72620a6a000096/javascript
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
// @ts-check | |
/** | |
* @param {Array<{ x: number, y: number }>} body snake body coords | |
* @param {{ x: number, y: number }} cell snake cell to check with | |
* @returns {{ x: number, y: number }} coords of the cell where collision occured(if any) | |
*/ | |
function findCollision(body, cell) { | |
const { x: x1, y: y1 } = cell; | |
return body.find(({ x: x0, y: y0 }) => (x0 === x1 && y0 === y1)); | |
} | |
/** | |
* @param {{ x: number, y: number }} cell snake body coords | |
* @param {[number, number]} limits | |
* @returns {boolean} | |
*/ | |
function doesCollideWithBorders(cell, limits) { | |
const [ xMax, yMax ] = limits; | |
const { x, y } = cell; | |
return ( | |
x < 0 || y < 0 || | |
x >= xMax || y >= yMax | |
); | |
} | |
/** | |
* @param {string[]} field | |
* @param {{ x: number, y: number }} coords | |
* @param {string} char | |
*/ | |
function printCell(field, coords, char) { | |
const row = field[coords.y].split(''); | |
row[coords.x] = char; | |
field[coords.y] = row.join(''); | |
} | |
/** | |
* @param {{ x: number, y: number }[]} snake | |
* @param {string[]} field | |
*/ | |
function printSnakeToField(snake, field) { | |
for (let cell of snake) { | |
printCell(field, cell, 'o') | |
} | |
} | |
/** | |
* Moves cell in a direction by 1 point | |
* @param {{ x: number, y: number }} cell | |
* @param {string} direction | |
* @returns {{ x: number, y: number }} | |
*/ | |
function moveCell(cell, direction) { | |
const { x: x0, y: y0 } = cell; | |
switch(direction) { | |
case 'U': return { x: x0, y: y0 - 1 }; | |
case 'D': return { x: x0, y: y0 + 1 }; | |
case 'L': return { x: x0 - 1, y: y0 }; | |
case 'R': return { x: x0 + 1, y: y0 }; | |
} | |
} | |
/** | |
* @param {string[]} field game field representation | |
* @param {string} moves snake moves | |
* @param {number} mvs ? | |
* @returns {[number, number, number] | number} [x, y, N] | |
* x, y - coords of the cell where a collision ocurred | |
* N - number of steps before the collision occured | |
*/ | |
function snakeCollision(field, moves, mvs=1) { | |
/** @type [number, number] */ | |
const FIELD_LIMITS = [ field[0].length, field.length ]; | |
let snake = [ { x: 0, y: 0 }, { x: 1, y: 0 }, { x: 2, y: 0 } ]; | |
let stepsTracked = 0; | |
let snakeMoves = moves; | |
// prepend default direction to get better structure for moves | |
if (/[A-Z]/.test(moves[0]) === false) { | |
snakeMoves = `R ${moves}`; | |
} | |
const moveSteps = snakeMoves.split(/\s/).reduce((_moves, move, i, allMoves) => { | |
if (/[A-Z]/.test(move)) { | |
const steps = Number(allMoves[i + 1]); | |
_moves.push({ direction: move, steps }); | |
} | |
return _moves; | |
}, []); | |
for (let {direction, steps} of moveSteps) { | |
let step = 0; | |
while(step < steps) { | |
stepsTracked++; | |
const head = snake[snake.length - 1]; | |
const nextCell = moveCell(head, direction); | |
const collision = findCollision(snake, nextCell); | |
if (collision != null) { | |
const { x, y } = collision; | |
return [ x, y, stepsTracked ]; | |
} | |
if (doesCollideWithBorders(nextCell, FIELD_LIMITS)) { | |
const { x, y } = head; | |
return [ x, y, stepsTracked ]; | |
} | |
const fieldCell = field[nextCell.y][nextCell.x]; | |
if (fieldCell == '$') { | |
// remove food token from the field | |
printCell(field, nextCell, '-'); | |
} else { | |
// If next snake cell is not a food cell - remove snake tail | |
const removedCell = snake.shift(); | |
printCell(field, removedCell, '-'); | |
} | |
printSnakeToField(snake, field); | |
field = field; | |
snake.push(nextCell); | |
step++; | |
}; | |
} | |
return -1; | |
} | |
if (require) { | |
const { runTests } = require('./test'); | |
runTests(snakeCollision); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment