4
\$\begingroup\$

Given a wall maze of 50x50, exactly same generating algorithm, at different RNG, so every two positions are connected by exactly one path, etc.

Write two functions:

  • One takes the maze as input and returns a positive integer;
  • The other takes the integer and walk through the maze from left-top to right-bottom. At any time, it can only see if it's wall the nearest 4 positions.

A testing lib in js is provided here. Using other languages is fine but you may need to write your testlib. It's fine if your lib keep accessibility to seen walls and your program relies on it —— It's anyway possible to store seen walls by yourself so this shouldn't change anything.

Minimize walked steps and then the passed number.

Your program would get executed on multiple tests and average score is used.

Notes

  • Although the example doesn't go shortest path, you are automatically eliminated if you don't either.
  • I said I was to make score log2(the passed number) + walked steps for the similar question but that likely force a shortest path, so why not explicitly require so.
  • Average score doesn't mean average length. Actually if two solutions have same average length, then the more flat answer likely wins.
  • Thus, this scoring method encourages to focus on optimize when more information needed through the maze.
  • Testlib here I use bigint unlike the other question. Bigint likely helps here but not there.
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Frankly, I think requiring the shortest path rules out some interesting approaches, and placing a fixed bound on the amount of information would be a more interesting way to do things. That would mean that e.g. approaches have to decide where to provide information and when to allow the player to get lost for a few steps - rather than hardcoding and overfitting to pack the shortest path into as small a number as possible \$\endgroup\$ Commented Nov 9 at 20:27
  • \$\begingroup\$ @emanresuA If longer move has high penalty, then it equals to a long encode here; if low penalty, it's the other question in this set \$\endgroup\$ Commented Nov 10 at 0:14
  • 1
    \$\begingroup\$ Your two variants are too wide apart, frankly. The difference between stepping in each field once and the shortest path is 2500 – 240 = 2260 That leaves only 11 bits for a hint with plain-number scoring. If you use log2 scoring, my solution with 142 bits would be an easy win. Maybe an upper limit of 52 bits (which ensures save comparison of Javascript Numbers) and scoring with the number of steps would lead to competitive solutions. \$\endgroup\$ Commented Nov 16 at 19:13

1 Answer 1

0
\$\begingroup\$

For a maze that connects two points by only one path it is relatively easy to find it: start at the end point and build a complete graph of the maze: it will be a tree. At each vertex, take all the open paths exept the one where you just came from. Link the new vertices to the previous one.

Select the wanted start point, and follow the links until you reach the end point at the root of the tree.

The shortest possible hint stores only information if a walk through the maze has really to make a decision between alternating paths. If a vertex has only a direction you came from, and one to go to, nothing needs to be stored.

If there is a choice, the smallest amount of information needed is the number of alternatives and the index of the one to take. These are stored in the hint number as a series of multipliers and remainders. The rest is writing and reading them in the right order.

Edit: As it turns out, it is advantageous to do the backlinking in the opposite order. That way, you have the walkthrough backwards, but in the right order to record the hint number with the start point as the lowest multiplier and remainder, last.

function HintMaker(map) {
  const info = [];
  const dirs = 'LURD';

  // start at the start vertex and go in all directions
  function explore (x = 1, y = 1, from) {
    const b = [...dirs];
    // exclude where you came from
    delete b[b.indexOf(from)];

    // loop all other directions
    b.forEach((d, i) => {
      // the direction you go is the opposite of where you came from
      const to = dirs[i + 2 & 3];

      const dx = i & 1 ? 0 : i - 1;
      const dy = i & 1 ? i - 2 : 0;

      // which directions are open?
      const open = map[y - (i == 1)][x + dx] != '|_'[i & 1];
      if (open) {
        // identify a new position
        const xx = x + 2 * dx;
        const yy = y + dy;

        info[xx * 100 + yy] = {
          link: x * 100 + y,              // where from
          to,                             // direction
          alternates: explore(xx, yy, to) // possible further directions
        }

      // otherwide exclude from the list of directions
      } else {
        delete b[i];
      }
    });

    return b.filter(d=>d);
  }

  // note possible directions at start
  const atstart = explore();

  let hint = 0n;
  function write (a, f) {
    // the hint needs to encode only cases where there are actually alternatives
    if (a.length < 2) return;
    // store the number of alternatives and the index of the one taken
    hint = hint * BigInt(a.length) + BigInt(a.indexOf(f));
  }

  // start at the end vertex;
  // the info has stored how you got there from the start vertex as a linked list,
  // this is always the direct (shortest) path
  let vertex = info[9950];
  let from;
  while (vertex = info[vertex.link]) {
    write(vertex.alternates, from)

    // the direction you go is the opposite of where you came from
    from = dirs[dirs.indexOf(vertex.to) + 2 & 3]
  }

  // add the decision at start vertex
  write(atstart, from)

  return hint;
}

function SolveWithHint(hint, step) {
  const dirs = 'LURD';
  let from;

  while(true) {
    const b = [...dirs];
    // exclude where you came from
    delete b[dirs.indexOf(from)];

    // only consider open directions
    const alternates = b.filter(d => step[d]);

    let dir = alternates[0];

    // are there actually alternatives?
    const len = BigInt(alternates.length);
    if (alternates.length > 1) {
      // look in the hint
      dir = alternates[hint % len]
      // throw away what you have read
      hint = hint / len | 0n;
    }

    step(dir);

    // the direction you go is the opposite of where you came from
    from = dirs[dirs.indexOf(dir) + 2 & 3];
  }
}

Try it online!

A more pleasing grafical representation of the maze and the path.

For your test maze, the result is

Hint =  3749002908424008331902107714953749973951195
Step =  240
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Bigint division already truncates; no need to use | 0n. \$\endgroup\$ Commented Nov 16 at 19:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.