Условие задачи такое(своими словами дабы не нарушать авторских прав):
Дается лабиринт в котором нужно найти путь к выходу. Лабиринт каждый раз при обновлении страницы генерируется новый.
Также даются 5 асинхронных методов. Двигаться вверх(up), вниз(down), влево(left), вправо(right), осмотреться(state).
Метод state возвращает объект (finish-если оно true, то это и есть выход)
{bottom: true
finish: false
left: true
right: true
start: false
top: false}
Решение нужно писать в определенной функции, которую после написания нужно отправить на проверку.
Также в этой функции было показан пример как пользоваться методами. В примере делаем 5 шагов вправо и возвращается координаты с выходом (но понятное дело тут будет finish=false)
export default function main(game, start) {
return game.right(start.x, start.y)
.then(() => game.right(start.x + 1, start.y))
.then(() => game.right(start.x + 2, start.y))
.then(() => game.right(start.x + 3, start.y))
.then(() => game.right(start.x + 4, start.y))
.then(() => game.right(start.x + 5, start.y))
.then(() => ({ x: start.x + 6, y: start.y }));
}
Результат можно посмотреть тут: https://labyrinth-y.web.app/
Решение:
Первое с чем столкнулся, это с тем что это не простые методы, а асинхронные, т.е. к ним можно обратиться как в примере цепочкой промисов(Promise), но в эту цепочку цикл не вставить.
Поэтому пойдем другим путем, нужно перейти в асинхронную функция и оттуда вызвать эти методы как асинхронные выражения, с ожиданием результата по каждому из них.
export default function main(game, start) {
let st = {};
const state = async (x,y) => {
return game.state(x, y)
.then((st1) => {
st=st1
})
.then(() => ({ x: x, y: y }));
}
const right = async (x,y) => {
return game.right(x, y);
}
const left = async (x,y) => {
return game.left(x, y);
}
const down = async (x,y) => {
return game.down(x, y);
}
const up = async (x,y) => {
return game.up(x, y);
}
const mainFunction = async () => {
await right(start.x + 1,start.y)
await right(start.x + 2,start.y)
await right(start.x + 3,start.y)
await right(start.x + 4,start.y)
await right(start.x + 5,start.y)
return await state(start.x + 6,start.y)
}
return mainFunction()
}
Добавим цикл. После получения объекта state (см. в условии задачи) записываю его в двумерный массив карты лабиринта 50*50(т.е. создаю свою карту лабиринта), также преобразую объект state в массив, исключаю из него finish и start и добавляю его к карте. Далее вместо второго элемента массива 'true' я начинаю записывать сколько раз ходил в данном направлении на этой клетке лабиринта.
[
['top', true]
['bottom', true]
['right', true]
]
const mainFunction = async () => {
let xx = 0;
let yy = 0;
const map = new Array(50);
for (var i = 0; i < map.length; i++) {
map[i] = new Array(50);
}
while (true) {
if (map[xx][yy]==undefined){
await state(xx,yy)
map[xx][yy] = Object.assign({}, st);
}
const ar_st = Object.entries(map[xx][yy]);
const ar_st2 = ar_st.filter((el)=>{
return el[1]===true && el[0]!='start' && el[0]!='finish'
})
if (map[xx][yy].arr==undefined){
map[xx][yy].arr = ar_st2
}
const step = 0;
//считаю сколько раз был на клетке
map[xx][yy].arr[step][1] = map[xx][yy].arr[step][1] + 1
if (st.finish) {
return await state(xx,yy)
}
else if (map[xx][yy].arr[step][0] == 'right' ) {
await right(xx,yy)
xx = xx + 1
}
else if (map[xx][yy].arr[step][0] == 'bottom' ) {
await down(xx,yy)
yy = yy + 1
}
else if (map[xx][yy].arr[step][0] == 'left' ) {
await left(xx,yy)
xx = xx - 1
}
else if (map[xx][yy].arr[step][0] == 'top' ) {
await up(xx,yy)
yy = yy - 1
}
else {
console.log(1)
}
}
}
Теперь немного ходим по лабиринту, но до первого тупика, в котором все зацикливается. Добавим функцию (blockDeadEnd) исключающую повторный заход в один и тот же тупик.
const blockDeadEnd = (stepOutOfDeadEnd,arr) => {
if (stepOutOfDeadEnd=='right') {
arr = arr.filter((el)=>{
return el[0]!='left'
})
} else if (stepOutOfDeadEnd=='left') {
arr = arr.filter((el)=>{
return el[0]!='right'
})
} else if (stepOutOfDeadEnd=='bottom') {
arr = arr.filter((el)=>{
return el[0]!='top'
})
} else if (stepOutOfDeadEnd=='top') {
arr = arr.filter((el)=>{
return el[0]!='bottom'
})
}
return arr
}
const mainFunction = async () => {
let xx = 0;
let yy = 0;
let stepOutOfDeadEnd = ''
const map = new Array(50);
for (var i = 0; i < map.length; i++) {
map[i] = new Array(50);
}
while (true) {
if (map[xx][yy]==undefined){
await state(xx,yy)
map[xx][yy] = Object.assign({}, st);
}
const ar_st = Object.entries(map[xx][yy]);
const ar_st2 = ar_st.filter((el)=>{
return el[1]===true && el[0]!='start' && el[0]!='finish'
})
if (map[xx][yy].arr==undefined){
map[xx][yy].arr = ar_st2
}
if (stepOutOfDeadEnd!='') {
map[xx][yy].arr = blockDeadEnd(stepOutOfDeadEnd,map[xx][yy].arr)
}
const step = 0;
//считаю сколько раз был на клетке
map[xx][yy].arr[step][1] = map[xx][yy].arr[step][1] + 1
//если тупик то запоминаю
if (map[xx][yy].arr.length==1){
stepOutOfDeadEnd = map[xx][yy].arr[step][0]
} else {
stepOutOfDeadEnd = ''
}
if (st.finish) {
return await state(xx,yy)
}
else if (map[xx][yy].arr[step][0] == 'right' ) {
await right(xx,yy)
xx = xx + 1
}
else if (map[xx][yy].arr[step][0] == 'bottom' ) {
await down(xx,yy)
yy = yy + 1
}
else if (map[xx][yy].arr[step][0] == 'left' ) {
await left(xx,yy)
xx = xx - 1
}
else if (map[xx][yy].arr[step][0] == 'top' ) {
await up(xx,yy)
yy = yy - 1
}
else {
console.log(1)
}
}
}
Теперь намного лучше ходим по лабиринту в тупики второй раз не заходим, но не знаем куда идти, случайным образом бродим по коридорам. Добавим функцию (firstToUndefined) поиска мест где мы еще небыли.
export default function main(game, start) {
// return game.right(start.x, start.y)
// .then(() => game.right(start.x + 1, start.y))
// .then(() => game.right(start.x + 2, start.y))
// .then(() => game.right(start.x + 3, start.y))
// .then(() => game.right(start.x + 4, start.y))
// .then(() => game.right(start.x + 5, start.y))
// .then(() => ({ x: start.x + 6, y: start.y }));
let st = {};
const state = async (x,y) => {
return game.state(x, y)
.then((st1) => {
st=st1
})
.then(() => ({ x: x, y: y }));
}
const right = async (x,y) => {
return game.right(x, y);
}
const left = async (x,y) => {
return game.left(x, y);
}
const down = async (x,y) => {
return game.down(x, y);
}
const up = async (x,y) => {
return game.up(x, y);
}
const blockDeadEnd = (stepOutOfDeadEnd,arr) => {
if (stepOutOfDeadEnd=='right') {
arr = arr.filter((el)=>{
return el[0]!='left'
})
} else if (stepOutOfDeadEnd=='left') {
arr = arr.filter((el)=>{
return el[0]!='right'
})
} else if (stepOutOfDeadEnd=='bottom') {
arr = arr.filter((el)=>{
return el[0]!='top'
})
} else if (stepOutOfDeadEnd=='top') {
arr = arr.filter((el)=>{
return el[0]!='bottom'
})
}
return arr
}
const firstToUndefined = (arr,map,xx,yy) => {
arr.forEach(el=>{
if (el[0]=='right' && map[xx+1][yy]==undefined) {
el[1]=0
}
else if (el[0]=='left' && map[xx-1][yy]==undefined) {
el[1]=0
}
else if (el[0]=='bottom' && map[xx][yy+1]==undefined) {
el[1]=0
}
else if (el[0]=='top' && map[xx][yy-1]==undefined) {
el[1]=0
}
})
if (yy==0) {
const bottom1 = arr.find(el=>el[0] === 'bottom')
if (bottom1!=undefined && bottom1[1]==0) {
arr.sort((a, b) => a[0].localeCompare(b[0]))
}
} else {
arr = arr.sort((a, b) => {
return a[1] > b[1] ? 1 : -1
})
}
return arr
}
const mainFunction = async () => {
let xx = 0;
let yy = 0;
let stepOutOfDeadEnd = ''
const map = new Array(50);
for (var i = 0; i < map.length; i++) {
map[i] = new Array(50);
}
while (true) {
if (map[xx][yy]==undefined){
await state(xx,yy)
map[xx][yy] = Object.assign({}, st);
}
const ar_st = Object.entries(map[xx][yy]);
const ar_st2 = ar_st.filter((el)=>{
return el[1]===true && el[0]!='start' && el[0]!='finish'
})
if (map[xx][yy].arr==undefined){
map[xx][yy].arr = ar_st2
}
if (stepOutOfDeadEnd!='') {
map[xx][yy].arr = blockDeadEnd(stepOutOfDeadEnd,map[xx][yy].arr)
}
const step = 0;
map[xx][yy].arr = firstToUndefined(map[xx][yy].arr,map,xx,yy)
//считаю сколько раз был на клетке
map[xx][yy].arr[step][1] = map[xx][yy].arr[step][1] + 1
//если тупик то запоминаю
if (map[xx][yy].arr.length==1){
stepOutOfDeadEnd = map[xx][yy].arr[step][0]
} else {
stepOutOfDeadEnd = ''
}
if (st.finish) {
return await state(xx,yy)
}
else if (map[xx][yy].arr[step][0] == 'right' ) {
await right(xx,yy)
xx = xx + 1
}
else if (map[xx][yy].arr[step][0] == 'bottom' ) {
await down(xx,yy)
yy = yy + 1
}
else if (map[xx][yy].arr[step][0] == 'left' ) {
await left(xx,yy)
xx = xx - 1
}
else if (map[xx][yy].arr[step][0] == 'top' ) {
await up(xx,yy)
yy = yy - 1
}
else {
console.log(1)
}
}
}
return mainFunction()
}
Упустил важное условие в задачи выход нужно найти за 10 секунд.
Поэтому исправил алгоритм добавил рекурсию. Теперь есть главный искатель, который идет по верхней линии и вызывает новых искателей (рекурсия).
export default function main(game, start) {
// return game.right(start.x, start.y)
// .then(() => game.right(start.x + 1, start.y))
// .then(() => game.right(start.x + 2, start.y))
// .then(() => game.right(start.x + 3, start.y))
// .then(() => game.right(start.x + 4, start.y))
// .then(() => game.right(start.x + 5, start.y))
// .then(() => ({ x: start.x + 6, y: start.y }));
let rezult = undefined;
const state = async (x,y) => {
return game.state(x, y)
.then((st1) => {
return st1
})
}
const state2 = async (x,y) => {
return game.state(x, y)
.then(() => ({ x: x, y: y }));
}
const right = async (x,y) => {
return game.right(x, y);
}
const left = async (x,y) => {
return game.left(x, y);
}
const down = async (x,y) => {
return game.down(x, y);
}
const up = async (x,y) => {
return game.up(x, y);
}
const blockDeadEnd = (stepOutOfDeadEnd,arr) => {
if (stepOutOfDeadEnd=='right') {
arr = arr.filter((el)=>{
return el[0]!='left'
})
} else if (stepOutOfDeadEnd=='left') {
arr = arr.filter((el)=>{
return el[0]!='right'
})
} else if (stepOutOfDeadEnd=='bottom') {
arr = arr.filter((el)=>{
return el[0]!='top'
})
} else if (stepOutOfDeadEnd=='top') {
arr = arr.filter((el)=>{
return el[0]!='bottom'
})
}
return arr
}
const firstToUndefined = (arr,map,xx,yy,main) => {
arr.forEach(el=>{
if (el[0]=='right' && map[xx+1][yy]==undefined) {
el[1]=0
}
else if (el[0]=='left' && map[xx-1][yy]==undefined) {
el[1]=0
}
else if (el[0]=='bottom' && map[xx][yy+1]==undefined) {
el[1]=0
}
else if (el[0]=='top' && map[xx][yy-1]==undefined) {
el[1]=0
}
})
if (yy==0 && !main) {
const bottom1 = arr.find(el=>el[0] === 'bottom')
if (bottom1!=undefined && bottom1[1]==0) {
arr.sort((a, b) => a[0].localeCompare(b[0]))
}
} else {
arr = arr.sort((a, b) => {
return a[1] > b[1] ? 1 : -1
})
}
return arr
}
const blockDown = (arr) => {
arr = arr.filter((el)=>{
return el[0]!='bottom'
})
return arr
}
const mainFunction = async (main,xx = 0,yy = 0,trash=false) => {
let stepOutOfDeadEnd = ''
while (true) {
if (main && rezult != undefined) {
return rezult
}
if ((!main && yy==0 && trash)||(!main && rezult)) {
return
}
if (yy==25) {
trash=true
}
let st = {};
if (map[xx][yy]==undefined){
st = await state(xx,yy)
map[xx][yy] = Object.assign({}, st);
}
if (st.bottom && main && yy==0) {
mainFunction(false,xx,yy)
}
const ar_st = Object.entries(map[xx][yy]);
const ar_st2 = ar_st.filter((el)=>{
return el[1]===true && el[0]!='start' && el[0]!='finish'
})
if (map[xx][yy].arr==undefined){
map[xx][yy].arr = ar_st2
}
if (stepOutOfDeadEnd!='') {
map[xx][yy].arr = blockDeadEnd(stepOutOfDeadEnd,map[xx][yy].arr)
}
if (yy>26) {
map[xx][yy].arr = blockDown(map[xx][yy].arr)
}
const step = 0;
map[xx][yy].arr = firstToUndefined(map[xx][yy].arr,map,xx,yy,main)
//считаю сколько раз был на клетке
map[xx][yy].arr[step][1] = map[xx][yy].arr[step][1] + 1
//если тупик то запоминаю
if (map[xx][yy].arr.length==1){
stepOutOfDeadEnd = map[xx][yy].arr[step][0]
} else {
stepOutOfDeadEnd = ''
}
if (st.finish) {
rezult = await state2(xx,yy)
return rezult
}
else if (map[xx][yy].arr[step][0] == 'right' ) {
await right(xx,yy)
xx = xx + 1
}
else if (map[xx][yy].arr[step][0] == 'bottom' ) {
await down(xx,yy)
yy = yy + 1
}
else if (map[xx][yy].arr[step][0] == 'left' ) {
await left(xx,yy)
xx = xx - 1
}
else if (map[xx][yy].arr[step][0] == 'top' ) {
await up(xx,yy)
yy = yy - 1
}
else {
console.log(1)
}
}
}
const map = new Array(50);
for (var i = 0; i < map.length; i++) {
map[i] = new Array(50);
}
return mainFunction(true)
}
Результат можно посмотреть тут: https://labyrinth-y.web.app/
Исходники лабиринта без решения: тут