Bugs I've hit so far:
It's not looking so elegant anymore..
BUPMQLGRZJFGYEY7DI7YV7V3URUE5HVT2AFQHQBG2GORLNSRW7VAC -- moving crate without moving any other crates-- idea 1--? function find_path_for_crate(level, src, crate_to_move, dst)--? local done = {}--? local cands = {}--? table.insert(cands, {x=src.x, y=src.y})--? while #cands > 0 do--? local cand = table.remove(cands, 1)--? local x,y = cand.x, cand.y--? if x == dst.x and y == dst.y then return cand end--? if y >= 1 and y <= lh and x >= 1 and x <= lw and not done[y][x] then--? done[y][x] = true--? local curr = level[y][x]--? if curr ~= CELL_WALL and curr ~= CELL_CRATE and curr ~= CELL_CRATE_ON_TARGET then--? table.insert(cands, {x=x-1, y=y, dir='left', prev=cand})--? table.insert(cands, {x=x+1, y=y, dir='right', prev=cand})--? table.insert(cands, {x=x, y=y-1, dir='up', prev=cand})--? table.insert(cands, {x=x, y=y+1, dir='down', prev=cand})--? elseif curr == CELL_CRATE and crate_id[y][x] == cid then--? -- TODO: actually simulate a real move of a crate--? table.insert(cands, {x=x-1, y=y, dir='left', prev=cand})--? table.insert(cands, {x=x+1, y=y, dir='right', prev=cand})--? table.insert(cands, {x=x, y=y-1, dir='up', prev=cand})--? table.insert(cands, {x=x, y=y+1, dir='down', prev=cand})--? end end end endfunction find_path_for_crate(level, player, crate, dst)local done = initialize_array(false, lh, lw)local cands = {}table.insert(cands, {crate={x=crate.x, y=crate.y}, player={x=player.x, y=player.y}})while #cands > 0 dolocal cand = table.remove(cands, 1)local p, c = cand.player, cand.crateif c.x == dst.x and c.y == dst.y then return cand endif c.y >= 1 and c.y <= lh and c.x >= 1 and c.x <= lw and not done[c.y][c.x] thendone[c.y][c.x] = truelocal curr = level[c.y][c.x]if curr ~= CELL_WALL and (curr ~= CELL_CRATE or crate_id[c.y][c.x] ~= crate.id) and (curr ~= CELL_CRATE_ON_TARGET or crate_id[c.y][c.x] ~= crate.id) then-- try to push crate left, by first getting to the right of itlocal moves = find_almost_empty_path(level, p, {x=c.x+1, y=c.y}, crate.id)if moves or (p.x == c.x+1 and p.y == c.y) then-- after the push, both player and crate have movedtable.insert(cands, {player={x=c.x, y=c.y}, crate={x=c.x-1, y=c.y}, dir='left', moves=moves, prev=cand})end-- try to push crate rightmoves = find_almost_empty_path(level, p, {x=c.x-1, y=c.y}, crate.id)if moves or (p.x == c.x-1 and p.y == c.y) thentable.insert(cands, {player={x=c.x, y=c.y}, crate={x=c.x+1, y=c.y}, dir='right', moves=moves, prev=cand})end-- try to push crate uplocal moves = find_almost_empty_path(level, p, {x=c.x, y=c.y+1}, crate.id)if moves or (p.x == c.x and p.y == c.y+1) thentable.insert(cands, {player={x=c.x, y=c.y}, crate={x=c.x, y=c.y-1}, dir='up', moves=moves, prev=cand})end-- try to push crate downmoves = find_almost_empty_path(level, p, {x=c.x, y=c.y-1}, crate.id)if moves or (p.x == c.x and p.y == c.y-1) thentable.insert(cands, {player={x=c.x, y=c.y}, crate={x=c.x, y=c.y+1}, dir='down', moves=moves, prev=cand})endend end end end
function find_almost_empty_path(level, src, dst, cid)local done = initialize_array(false, lh, lw)local cands = {}table.insert(cands, {x=src.x, y=src.y})while #cands > 0 dolocal cand = table.remove(cands, 1)local x,y = cand.x, cand.yif x == dst.x and y == dst.y then return cand endif y >= 1 and y <= lh and x >= 1 and x <= lw and not done[y][x] thendone[y][x] = truelocal curr = level[y][x]if curr ~= CELL_WALL and (curr ~= CELL_CRATE or crate_id[y][x] ~= cid) and (curr ~= CELL_CRATE_ON_TARGET or crate_id[y][x] ~= cid) thentable.insert(cands, {x=x-1, y=y, dir='left', prev=cand})table.insert(cands, {x=x+1, y=y, dir='right', prev=cand})table.insert(cands, {x=x, y=y-1, dir='up', prev=cand})table.insert(cands, {x=x, y=y+1, dir='down', prev=cand})end end end endfunction unwind_steps_to_move_crate(c, out)-- insert steps in reverse orderwhile c dotable.insert(out, c.dir)unwind_steps(c.moves, out)c = c.prevendreturn outend
function plan_move_crate(y, x)local path = find_path_for_crate(level_state, player, crate_to_move, {y=y, x=x})if path == nil then return endpending_moves = unwind_steps_to_move_crate(path, {})for i=#pending_moves,1,-1 do print(pending_moves[i]) endlocal src = level_state[player.y][player.x]local crate_cell = level_state[crate_to_move.y][crate_to_move.x]local dest = level_state[y][x]local u = {{x=player.x, y=player.y, cell=src},{x=crate_to_move.x, y=crate_to_move.y, cell=crate_cell},{x=x, y=y, cell=dest}}print('player initial', player.x, player.y, src)print('crate initial', crate_to_move.x, crate_to_move.y, crate_cell)print('crate final', x, y, dest)-- we also need to save the initial state of the final position of the playerlocal fpy,fpx = compute_cell(y,x, opposite_dir(pending_moves[1]))print('player final', fpx, fpy, level_state[fpy][fpx])table.insert(u, {x=fpx, y=fpy, cell=level_state[fpy][fpx]})table.insert(undo_history, u) -- add to undo without making move yetnext_pending_move = Current_timeend
function compute_cell(y, x, dir)if dir == 'up' thenreturn y-1, xelseif dir == 'down' thenreturn y+1, xelseif dir == 'left' thenreturn y, x-1elseif dir == 'right' thenreturn y, x+1end endfunction opposite_dir(dir)if dir == 'up' thenreturn 'down'elseif dir == 'down' thenreturn 'up'elseif dir == 'left' thenreturn 'right'elseif dir == 'right' thenreturn 'left'end end