sudden6 / bitlocksolver

A solver for the awesome puzzle game "Block in the Lock" (formerly known as B.i.t.Lock)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

B.i.t.Lock Solver

A solver for the awesome puzzle game Block in the Lock (formerly known as B.i.t.Lock).

Currently supported blocks:

  • 26 types of game tiles (13 normal, 13 reverse moving)
  • movable destroyer game tile
  • obstacle/wall
  • destroyer

Usage:

usage: bitlocksolve.py [-h] [-w] [-W arrows] [-t INT] [-f INT] mapfilename
  -h, --help            show this help message and exit
  -w, --walkthrough     Walkthrough after completion
  -W arrows, --walkthrough-path arrows
                        Walk through given direction arrows (do not solve)
  -t INT, --tree-size INT
                        Maximum number of tree levels to memorize before forgetful iteration.
  -f INT, --forgetful-size INT
                        Number of steps to simulate in forgetful iteration.

Mapfile is a text file representing the 2D game map and uses following conventions:

character meaning
a-z A starting point for a game tile. Use different letters for discriminable tiles.
A-Z A target point for a game tile. Corresponds to above lower-case letters.
# An obstacle (or a wall).
+, * A stationary/moving destroyer (game tiles landing in it will vanish)

Planned:

character meaning
Wall
Square Tile: normal, reverse, sticky, sleeping
Top-left Pie: normal, reverse, sticky, sleeping
Top-right Pie: normal, reverse, sticky, sleeping
Bottom-right Pie: normal, reverse, sticky, sleeping
Bottom-left Pie: normal, reverse, sticky, sleeping
Square target
Pie targets: Top-left, Top-right, Bottom-right, Bottom-left
Killers: fixed, moving, reverse, sticky, sleeping
Pushers: fixed, moving, reverse, sticky, sleeping
Lava, Lava crack
Hole
Electro: fixed, moving, reverse, sticky, sleeping
` , -`
Pipe curve: top-right, bottom-right, bottom-left, top-left
T-Pipe: Right, bottom, left, top
+ Pipe cross
Gate
Conveyor: Top, right, bottom, left
Square Dispenser: Top, right, bottom, left
Killer Dispenser: Top, right, bottom, left

Game rules

Given is a 2D tiled map containing blocks of various types:

  • game tiles
  • obstacle/wall
  • destroyers
  • pushers
  • many more are available in the game but not implemented in this solver.

All blocks are also available as tiles which move in the same or opposite direction (reverse tiles).

The player has 4 possible decisions in each step: Left, right, up or down. After selecting a direction, all game tiles (including destroyer tiles) move one step in this direction, if possible. They can e.g. be blocked by an obstacles or destroyed by a, you guessed it, destroyer block. Ideally, after as few steps as possible, the game tiles are in the target position.

Some additional rules:

  • moving destroyers are blocked by destroyer blocks.
  • moving destroyers move through game tiles and destroy them.

Solving concept

This is a brute-force solver which walks through all possible combinations of directions. This builds up a tree with 4 edges (directions) after each node (tile constellation). The solver iterates breadth-first through the tree, so if it finds a solution it is guaranteed to use a minimal number of steps (=tree levels). Generated nodes with already seen tile constellations are neglected (because we already know a shorter path).

Performance

Computational Complexity

The upper bound of computational complexity is an inconceivable O(4n), (n is the step count) i.e. 4 directions are added to all possible paths in each step. The real complexity, however, is lower but hard to define, since it depends on the map. This is because already seen tile constellations are rejected, which happens more often in smaller maps.

An infinitely expanded map with no obstacles is solved in O(n2), because only at the perimeter of new positions can be found, which grows with the square of the radius.

An infinitely expanded map with no obstacles and height 1 is solved in O(n).

Memory complexity

This is the hurtful part. Because all paths in the tree possibly lead to the solution we have to memorize the whole tree, whose memory complexity has an upper bound of O(4n). We even need to save the tile constellation of all nodes, to check if we already know a shorter path to a newly generated one.

Outlook (ToDo)

  • The bottleneck seems to be the memory complexity. Since the computations are, even in a non-optimized version, pretty low-cost, it might be advantageous to memorize the tree only up to a certain limit (tree level). Thereafter, we could compute all possibilities for the next k steps height-first. If we find a solution we save the path, the tree itself is not saved (forgetful iteration). Set k = pathlength - 1, to ensure new solutions are shorter.
  • Additionally to the forgetful iteration, we could sort the nodes of the last saved level by distance to the target to find more likely minimal solutions first.
  • Add multithreading support.
  • Investigate when exactly a level counts as solved. The current implementation is wrong (additional game tiles can in fact remain outside of the target).
  • Use extra sets of game tiles for different types (normal tile, destroyers, pushers, ...). Always filtering the TileList seems inefficient.
  • Instead of setting a fixed tree-size, set the available memory and dynamically choose if another tree level is possible.
  • Read .lvl file (json)
  • Use same naming as in .lvl file
  • Make preferred characters configurable via yaml

About

A solver for the awesome puzzle game "Block in the Lock" (formerly known as B.i.t.Lock)

License:GNU General Public License v3.0


Languages

Language:Python 100.0%