2

When trying to test for reachability on a straight line without looping, you can use Bitboard representation. Imagine chess, a row or column of the board represented as a byte and the question if a Rook on square X can capture a target on square T.

Let T: Target, X: Start, O: other occupied squares, _: empty square

I found it convenient to visualize the possible scenarios, using those symbols:

// __OXO___ -> X is between others on both sides
// __XOO___ -> X is leftmost of others
// __OOX___ -> X is rightmost of other others
//A: T_OOX___ -> Target left of X x not leftmost -> T > X and O > X -> NOT reachable
//B: T_XOO___ -> Target left of X, X leftmost    -> T > X and O < X -> reachable
//C: __OXO_T_ -> Target right of X, X embedded   -> T < X and ???? -> NOT reachable
//D: __OOX_T_ -> Target right of X, X rightmost  -> T < X and ???? -> reachable

There are 4 cases of interest here - labeled from A-D. Cases A and B are easy to handle.

But cases C and D are not. You cannot simply test for > or < to find out if the target is reachable or if the path is blocked.

What one could do, though is to transform C,D to A,B by mirroring the bits in the byte. So Bit 0 -> Bit 7, Bit 1 -> Bit 6, ...

Does anyone know if there is an optimized implementation for doing that flipping?

EDIT: I just noticed that another case is: E: OT__X___ ... my theory goes bad, yet the question remains. :)

BitTickler
  • 10,905
  • 5
  • 32
  • 53
  • If you ever only use a byte (8 bits) and not a longer integer type, a simple lookup table would probably be most efficient. I'm sure it can be accomplished via some sequence of bit operations (even if it's just a naive bit-by-bit shift), but that's not likely to be much faster unless your CPU has a native instruction set for that type of bit shuffling... – twalberg Jan 22 '15 at 20:14
  • Yes, the cheapest bit shuffling I can think of involves a loop with 8 iterations, an and and a or and 2 shifts. Nothing cheaper possible? – BitTickler Jan 22 '15 at 20:19
  • This seems only complicated to me but maybe I don't get it. There is no difference between; `can T moving to X` and `can X moving to T`. In either case they are always equal so instead of trying to reverse the bits, reverse the logic. – Erik Philips Jan 22 '15 at 20:23
  • If you were trying to perform a [bit-reversal permutation](http://en.wikipedia.org/wiki/Bit-reversal_permutation) then there would be an efficient way, but I don't think there's a way to reverse bits efficiently. – user541686 Jan 22 '15 at 20:26

4 Answers4

2

You can do this test without reversing. If x, t, and o are bitmasks as in your example (where x and t have exactly one bit set, and these bits are not set in o), then you can create a bitmask containing the bits between t and x as follows:

tx = t | x
txhi = tx & (tx - 1)
return (txhi - 1) ^ (tx - 1)

This uses that subtracting 1 replaces a bit pattern ending in "100...000" by "011...111". You can then simply check whether this mask intersects with o.

Falk Hüffner
  • 4,942
  • 19
  • 25
1

Instead of

// __OXO___ -> X is between others on both sides
// __XOO___ -> X is leftmost of others
// __OOX___ -> X is rightmost of other others
//A: T_OOX___ -> Target left of X x not leftmost -> T > X and O > X -> NOT reachable
//B: T_XOO___ -> Target left of X, X leftmost    -> T > X and O < X -> reachable
//C: __OXO_T_ -> Target right of X, X embedded   -> T < X and ???? -> NOT reachable
//D: __OOX_T_ -> Target right of X, X rightmost  -> T < X and ???? -> reachable

It Seems logical to reverse X and T in cases C and D:

//C: __OTO_X_ -> Target left of X x not leftmost -> T > X and O > X -> NOT reachable
//D: __OOT_X_ -> Target left of X, X leftmost    -> T > X and O < X -> reachable

I realize that if T was a bishop and X was a Rook, that T may not move the way X can, but all we're doing is seeing if T was a Rook if it could move to X. Answering that question is the same as X moving to T. Either it can or it can't which answers the opposite (can X move to T).

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
0

There are some bit-hackery solutions to this (one example listed below), but if you're only ever dealing with a single byte, a simple 256-entry lookup table is probably the most efficient.

Here's an example from my personal code library from my FFT hacking days (not guaranteeing it's up to modern C standards or anything - in particular, uint8 is a typedef for unsigned char, but modern C has, IIRC, a native uint8_t that would serve the purpose):

inline uint8 bitrev8(uint8 n)
{ uint8 tmp = 0x55;
  n = (((n >> 1) & tmp) | ((n & tmp) << 1));

  tmp = 0x33;
  n = (((n >> 2) & tmp) | ((n & tmp) << 2));

  return ((n >> 4) | (n << 4));
}

Due to the impracticality of large lookup tables, the corresponding routines for 16-, 32- and 64-bit data are much more of a win than the 8-bit version, which obviously resolves to a lot more instructions than a simple v = reversed[n]; would...

twalberg
  • 59,951
  • 11
  • 89
  • 84
0

Make a 256-byte lookup table for each byte and use that instead of performing any computation.

user541686
  • 205,094
  • 128
  • 528
  • 886