package shootout.meteor;/* The Computer Language Benchmarks Game
http://shootout.alioth.debian.org/
transliterated from C++ (Ben St. John) and D (Michael Deardeuff) by Amir K aka Razii
*/
public final class meteor
{
static final int X = 0;
static final int Y = 1;
static final int N_DIM = 2;
static final int EVEN = 0;
static final int ODD = 1;
static final int N_PARITY = 2;
static final int GOOD = 0;
static final int BAD = 1;
static final int ALWAYS_BAD = 2;
static final int OPEN = 0;
static final int CLOSED = 1;
static final int N_FIXED = 2;
static final int MAX_ISLAND_OFFSET = 1024;
static final int N_COL = 5;
static final int N_ROW = 10;
static final int N_CELL = N_COL * N_ROW;
static final int N_PIECE_TYPE = 10;
static final int N_ORIENT = 12;
//-- Globals -------------------------
static IslandInfo[] g_islandInfo = new IslandInfo [MAX_ISLAND_OFFSET];
static int g_nIslandInfo = 0;
static OkPieces[][] g_okPieces = new OkPieces [N_ROW][N_COL];
static final int g_firstRegion[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x01, 0x06, 0x07,
0x08, 0x01, 0x02, 0x03, 0x0c, 0x01, 0x0e, 0x0f,
0x10, 0x01, 0x02, 0x03, 0x04, 0x01, 0x06, 0x07,
0x18, 0x01, 0x02, 0x03, 0x1c, 0x01, 0x1e, 0x1f
};
static final int g_flip[] = {
0x00, 0x10, 0x08, 0x18, 0x04, 0x14, 0x0c, 0x1c,
0x02, 0x12, 0x0a, 0x1a, 0x06, 0x16, 0x0e, 0x1e,
0x01, 0x11, 0x09, 0x19, 0x05, 0x15, 0x0d, 0x1d,
0x03, 0x13, 0x0b, 0x1b, 0x07, 0x17, 0x0f, 0x1f,
};
static final int[] s_firstOne = {
0, 0, 1, 0, 2, 0, 1, 0,
3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0,
3, 0, 1, 0, 2, 0, 1, 0,
};
static int getMask(int iPos) {
return (1 << (iPos));
}
static int floor(int top, int bot) {
int toZero = top / bot;
// negative numbers should be rounded down, not towards zero;
if ((toZero * bot != top) && ((top < 0) != (bot <= 0)))
toZero--;
return toZero;
}
static int getFirstOne(int v) {
int startPos = 0;
if (v == 0)
return 0;
int iPos = startPos;
int mask = 0xff << startPos;
while ((mask & v) == 0) {
mask <<= 8;
iPos += 8;
}
int result = (mask & v) >> iPos;
int resultLow = result & 0x0f;
if (resultLow != 0)
iPos += s_firstOne[resultLow];
else
iPos += 4 + s_firstOne[result >> 4];
return iPos;
}
static int countOnes(int v) {
int n = 0;
while (v != 0) {
n++;
v = v & (v - 1);
}
return n;
}
static int flipTwoRows(int bits) {
int flipped = g_flip[bits >> N_COL] << N_COL;
return (flipped | g_flip[bits & Board.TOP_ROW]);
}
static void markBad(IslandInfo info, int mask, int eo, boolean always) {
info.hasBad[eo][OPEN] |= mask;
info.hasBad[eo][CLOSED] |= mask;
if (always)
info.alwaysBad[eo] |= mask;
}
static void initGlobals() {
for (int i = 0; i < MAX_ISLAND_OFFSET; i++)
{
g_islandInfo[i] = new IslandInfo();
}
for (int i = 0; i < N_ROW; i++)
{
for (int j = 0; j < N_COL; j++)
g_okPieces[i][j] = new OkPieces();
}
}
//-- Classes -------------------------;
static class OkPieces {
byte[] nPieces = new byte[N_PIECE_TYPE];
int[][] pieceVec = new int[N_PIECE_TYPE][N_ORIENT];
}
static class IslandInfo {
int[][] hasBad = new int[N_FIXED][N_PARITY];
int[][] isKnown = new int[N_FIXED][N_PARITY];
int[] alwaysBad = new int[N_PARITY];
}
static class Soln {
static final int NO_PIECE = -1;
boolean isEmpty() {
return (m_nPiece == 0);
}
void popPiece() {
m_nPiece--;
m_synched = false;
}
void pushPiece(int vec, int iPiece, int row) {
SPiece p = m_pieces[m_nPiece++];
p.vec = vec;
p.iPiece = (short) iPiece;
p.row = (short) row;
}
Soln() {
m_synched = false;
m_nPiece = 0;
init();
}
class SPiece {
int vec;
short iPiece;
short row;
SPiece() {}
SPiece(int avec, int apiece, int arow) {
vec = avec;
iPiece = (short)apiece;
row = (short)arow;
}
SPiece(SPiece other) {
vec = other.vec;
iPiece = other.iPiece;
row = other.row;
}
}
SPiece[] m_pieces = new SPiece [N_PIECE_TYPE];
int m_nPiece;
byte[][] m_cells = new byte [N_ROW][N_COL];
boolean m_synched;
void init() {
for (int i = 0; i < N_PIECE_TYPE; i++)
m_pieces[i] = new SPiece();
}
Soln (int fillVal) {
init();
m_nPiece = 0;
fill(fillVal);
}
public Soln clone2() {
Soln s = new Soln();
for (int i = 0; i < m_pieces.length; i++)
s.m_pieces[i] = new SPiece(m_pieces[i]);
s.m_nPiece = m_nPiece;
//System.arraycopy(m_cells, 0, s.m_cells, 0, N_CELL);
for (int i = 0; i < N_ROW; i++)
{
for (int j = 0; j < N_COL; j ++)
{
s.m_cells[i][j] = m_cells[i][j];
}
}
s.m_synched = m_synched;
return s;
}
void fill(int val) {
m_synched = false;
for (int i = 0; i < N_ROW; i++)
{
for (int j = 0; j < N_COL; j++)
m_cells[i][j] = (byte) val;
}
}
public String toString() {
StringBuffer result = new StringBuffer(N_CELL * 2);
for (int y = 0; y < N_ROW; y++) {
for (int x = 0; x < N_COL; x++) {
int val = m_cells[y][x];
//if (val == NO_PIECE) result.append('.');
{
result.append(val);
}
result.append(' ');
}
result.append('\n');
// indent every second line
if (y % 2 == 0)
result.append(" ");
}
return result.toString();
}
void setCells() {
if (m_synched)
return;
for (int iPiece = 0; iPiece < m_nPiece; iPiece++) {
SPiece p = m_pieces[iPiece];
int vec = p.vec;
byte pID = (byte) p.iPiece;
int rowOffset = p.row;
int nNewCells = 0;
for (int y = rowOffset; y < N_ROW; y++) {
for (int x = 0; x < N_COL; x++) {
if ((vec & 1) != 0) {
m_cells[y][x] = pID;
nNewCells++;
}
vec >>= 1;
}
if (nNewCells == Piece.N_ELEM)
break;
}
}
m_synched = true;
}
boolean lessThan(Soln r) {
if (m_pieces[0].iPiece != r.m_pieces[0].iPiece) {
return m_pieces[0].iPiece < r.m_pieces[0].iPiece;
}
setCells();
r.setCells();
for (int y = 0; y < N_ROW; y++) {
for (int x = 0; x < N_COL; x++) {
int lval = m_cells[y][x];
int rval = r.m_cells[y][x];
if (lval != rval)
return (lval < rval);
}
}
return false;
}
void spin(Soln spun) {
setCells();
for (int y = 0; y < N_ROW; y++) {
for (int x = 0; x < N_COL; x++) {
byte flipped = m_cells[N_ROW - y - 1][N_COL - x - 1];
spun.m_cells[y][x] = flipped;
}
}
spun.m_pieces[0].iPiece = m_pieces[N_PIECE_TYPE - 1].iPiece;
spun.m_synched = true;
}
}
//-----------------------
static class Board {
static final int L_EDGE_MASK =
((1 << 0) | (1 << 5) | (1 << 10) | (1 << 15) |
(1 << 20) | (1 << 25) | (1 << 30));
static final int R_EDGE_MASK = L_EDGE_MASK << 4;
static final int TOP_ROW = (1 << N_COL) - 1;
static final int ROW_0_MASK =
TOP_ROW | (TOP_ROW << 10) | (TOP_ROW << 20) | (TOP_ROW << 30);
static final int ROW_1_MASK = ROW_0_MASK << 5;
static final int BOARD_MASK = (1 << 30) - 1;
static int getIndex(int x, int y) {
return y * N_COL + x;
}
Soln m_curSoln;
Soln m_minSoln;
Soln m_maxSoln;
int m_nSoln;
Board () {
m_curSoln = new Soln(Soln.NO_PIECE);
m_minSoln = new Soln(N_PIECE_TYPE);
m_maxSoln = new Soln(Soln.NO_PIECE);
m_nSoln = (0);
}
static boolean badRegion(int[] toFill, int rNew)
{
// grow empty region, until it doesn't change any more;
int region;
do {
region = rNew;
// simple grow up/down
rNew |= (region >> N_COL);
rNew |= (region << N_COL);
// grow right/left
rNew |= (region & ~L_EDGE_MASK) >> 1;
rNew |= (region & ~R_EDGE_MASK) << 1;
// tricky growth
int evenRegion = region & (ROW_0_MASK & ~L_EDGE_MASK);
rNew |= evenRegion >> (N_COL + 1);
rNew |= evenRegion << (N_COL - 1);
int oddRegion = region & (ROW_1_MASK & ~R_EDGE_MASK);
rNew |= oddRegion >> (N_COL - 1);
rNew |= oddRegion << (N_COL + 1);
// clamp against existing pieces
rNew &= toFill[0];
}
while ((rNew != toFill[0]) && (rNew != region));
// subtract empty region from board
toFill[0] ^= rNew;
int nCells = countOnes(toFill[0]);
return (nCells % Piece.N_ELEM != 0);
}
static int hasBadIslands(int boardVec, int row)
{
// skip over any filled rows
while ((boardVec & TOP_ROW) == TOP_ROW) {
boardVec >>= N_COL;
row++;
}
int iInfo = boardVec & ((1 << 2 * N_COL) - 1);
IslandInfo info = g_islandInfo[iInfo];
int lastRow = (boardVec >> (2 * N_COL)) & TOP_ROW;
int mask = getMask(lastRow);
int isOdd = row & 1;
if ((info.alwaysBad[isOdd] & mask) != 0)
return BAD;
if ((boardVec & (TOP_ROW << N_COL * 3)) != 0)
return calcBadIslands(boardVec, row);
int isClosed = (row > 6) ? 1 : 0;
if ((info.isKnown[isOdd][isClosed] & mask) != 0)
return (info.hasBad[isOdd][isClosed] & mask);
if (boardVec == 0)
return GOOD;
int hasBad = calcBadIslands(boardVec, row);
info.isKnown[isOdd][isClosed] |= mask;
if (hasBad != 0)
info.hasBad[isOdd][isClosed] |= mask;
return hasBad;
}
static int calcBadIslands(int boardVec, int row)
{
int[] toFill = {~boardVec};
if ((row & 1) != 0) {
row--;
toFill[0] <<= N_COL;
}
int boardMask = BOARD_MASK;
if (row > 4) {
int boardMaskShift = (row - 4) * N_COL;
boardMask >>= boardMaskShift;
}
toFill[0] &= boardMask;
// a little pre-work to speed things up
int bottom = (TOP_ROW << (5 * N_COL));
boolean filled = ((bottom & toFill[0]) == bottom);
while ((bottom & toFill[0]) == bottom) {
toFill[0] ^= bottom;
bottom >>= N_COL;
}
int startRegion;
if (filled || (row < 4))
startRegion = bottom & toFill[0];
else {
startRegion = g_firstRegion[toFill[0] & TOP_ROW];
if (startRegion == 0) {
startRegion = (toFill[0] >> N_COL) & TOP_ROW;
startRegion = g_firstRegion[startRegion];
startRegion <<= N_COL;
}
startRegion |= (startRegion << N_COL) & toFill[0];
}
while (toFill[0] != 0) {
if (badRegion(toFill, startRegion))
return ((toFill[0]!=0) ? ALWAYS_BAD : BAD);
int iPos = getFirstOne(toFill[0]);
startRegion = getMask(iPos);
}
return GOOD;
}
static void calcAlwaysBad() {
for (int iWord = 1; iWord < MAX_ISLAND_OFFSET; iWord++) {
IslandInfo isleInfo = g_islandInfo[iWord];
IslandInfo flipped = g_islandInfo[flipTwoRows(iWord)];
for (int i = 0, mask = 1; i < 32; i++, mask <<= 1) {
int boardVec = (i << (2 * N_COL)) | iWord;
if ((isleInfo.isKnown[0][OPEN] & mask) != 0)
continue;
int hasBad = calcBadIslands(boardVec, 0);
if (hasBad != GOOD) {
boolean always = (hasBad==ALWAYS_BAD);
markBad(isleInfo, mask, EVEN, always);
int flipMask = getMask(g_flip[i]);
markBad(flipped, flipMask, ODD, always);
}
}
flipped.isKnown[1][OPEN] = -1;
isleInfo.isKnown[0][OPEN] = -1;
}
}
static boolean hasBadIslandsSingle(int boardVec, int row)
{
int[] toFill = {~boardVec};
boolean isOdd = ((row & 1) != 0);
if (isOdd) {
row--;
toFill[0] <<= N_COL; // shift to even aligned
toFill[0] |= TOP_ROW;
}
int startRegion = TOP_ROW;
int lastRow = TOP_ROW << (5 * N_COL);
int boardMask = BOARD_MASK; // all but the first two bits
if (row >= 4)
boardMask >>= ((row - 4) * N_COL);
else if (isOdd || (row == 0))
startRegion = lastRow;
toFill[0] &= boardMask;
startRegion &= toFill[0];
while (toFill[0] != 0) {
if (badRegion(toFill, startRegion))
return true;
int iPos = getFirstOne(toFill[0]);
startRegion = getMask(iPos);
}
return false;
}
void genAllSolutions(int boardVec, int placedPieces, int row)
{
while ((boardVec & TOP_ROW) == TOP_ROW) {
boardVec >>= N_COL;
row++;
}
int iNextFill = s_firstOne[~boardVec & TOP_ROW];
OkPieces allowed = g_okPieces[row][iNextFill];
int iPiece = getFirstOne(~placedPieces);
int pieceMask = getMask(iPiece);
for (; iPiece < N_PIECE_TYPE; iPiece++, pieceMask <<= 1)
{
if ((pieceMask & placedPieces) != 0)
continue;
placedPieces |= pieceMask;
for (int iOrient = 0; iOrient < allowed.nPieces[iPiece]; iOrient++) {
int pieceVec = allowed.pieceVec[iPiece][iOrient];
if ((pieceVec & boardVec) != 0)
continue;
boardVec |= pieceVec;
if ((hasBadIslands(boardVec, row)) != 0) {
boardVec ^= pieceVec;
continue;
}
m_curSoln.pushPiece(pieceVec, iPiece, row);
// recur or record solution
if (placedPieces != Piece.ALL_PIECE_MASK)
genAllSolutions(boardVec, placedPieces, row);
else
recordSolution(m_curSoln);
boardVec ^= pieceVec;
m_curSoln.popPiece();
}
placedPieces ^= pieceMask;
}
}
void recordSolution(Soln s) {
m_nSoln += 2;
if (m_minSoln.isEmpty()) {
m_minSoln = m_maxSoln = s.clone2();
return;
}
if (s.lessThan(m_minSoln))
m_minSoln = s.clone2();
else if (m_maxSoln.lessThan(s))
m_maxSoln = s.clone2();
Soln spun = new Soln();
s.spin(spun);
if (spun.lessThan(m_minSoln))
m_minSoln = spun;
else if (m_maxSoln.lessThan(spun))
m_maxSoln = spun;
}
}
//----------------------
static class Piece {
class Instance {
long m_allowed;
int m_vec;
int m_offset;
}
static final int N_ELEM = 5;
static final int ALL_PIECE_MASK = (1 << N_PIECE_TYPE) - 1;
static final int SKIP_PIECE = 5;
static final int BaseVecs[] = {
0x10f, 0x0cb, 0x1087, 0x427, 0x465,
0x0c7, 0x8423, 0x0a7, 0x187, 0x08f
};
static Piece[][] s_basePiece = new Piece [N_PIECE_TYPE][N_ORIENT];
Instance[] m_instance = new Instance [N_PARITY];
void init() {
for (int i = 0; i < N_PARITY; i++)
m_instance[i] = new Instance();
}
Piece() {
init();
}
static {
for (int i = 0; i < N_PIECE_TYPE; i++) {
for (int j = 0; j < N_ORIENT; j++)
s_basePiece[i][j] = new Piece();
}
}
static void setCoordList(int vec, int[][] pts) {
int iPt = 0;
int mask = 1;
for (int y = 0; y < N_ROW; y++) {
for (int x = 0; x < N_COL; x++) {
if ((mask & vec) != 0) {
pts[iPt][X] = x;
pts[iPt][Y] = y;
iPt++;
}
mask <<= 1;
}
}
}
static int toBitVector(int[][] pts) {
int y, x;
int result = 0;
for (int iPt = 0; iPt < N_ELEM; iPt++) {
x = pts[iPt][X];
y = pts[iPt][Y];
int pos = Board.getIndex(x, y);
result |= (1 << pos);
}
return result;
}
static void shiftUpLines(int[][] pts, int shift) {
for (int iPt = 0; iPt < N_ELEM; iPt++) {
if ((pts[iPt][Y] & shift & 0x1) != 0)
(pts[iPt][X])++;
pts[iPt][Y] -= shift;
}
}
static int shiftToX0(int[][] pts, Instance instance, int offsetRow)
{
int x, y, iPt;
int xMin = pts[0][X];
int xMax = xMin;
for (iPt = 1; iPt < N_ELEM; iPt++) {
x = pts[iPt][X];
y = pts[iPt][Y];
if (x < xMin)
xMin = x;
else if (x > xMax)
xMax = x;
}
int offset = N_ELEM;
for (iPt = 0; iPt < N_ELEM; iPt++) {
pts[iPt][X] -= xMin;
if ((pts[iPt][Y] == offsetRow) && (pts[iPt][X] < offset))
offset = pts[iPt][X];
}
instance.m_offset = offset;
instance.m_vec = toBitVector(pts);
return xMax - xMin;
}
void setOkPos(int isOdd, int w, int h) {
Instance p = m_instance[isOdd];
p.m_allowed = 0;
long posMask = 1L << (isOdd * N_COL);
for (int y = isOdd; y < N_ROW - h; y+=2, posMask <<= N_COL) {
if ((p.m_offset) != 0)
posMask <<= p.m_offset;
for (int xPos = 0; xPos < N_COL - p.m_offset; xPos++, posMask <<= 1) {
if (xPos >= N_COL - w)
continue;
int pieceVec = p.m_vec << xPos;
if (Board.hasBadIslandsSingle(pieceVec, y))
continue;
p.m_allowed |= posMask;
}
}
}
static void genOrientation(int vec, int iOrient, Piece target)
{
int[][] pts = new int[N_ELEM][N_DIM];
setCoordList(vec, pts);
int y, x, iPt;
int rot = iOrient % 6;
int flip = iOrient >= 6 ? 1 : 0;
if (flip != 0) {
for (iPt = 0; iPt < N_ELEM; iPt++)
pts[iPt][Y] = -pts[iPt][Y];
}
while ((rot--) != 0) {
for (iPt = 0; iPt < N_ELEM; iPt++) {
x = pts[iPt][X];
y = pts[iPt][Y];
int xNew = floor((2 * x - 3 * y + 1), 4);
int yNew = floor((2 * x + y + 1), 2);
pts[iPt][X] = xNew;
pts[iPt][Y] = yNew;
}
}
int yMin = pts[0][Y];
int yMax = yMin;
for (iPt = 1; iPt < N_ELEM; iPt++) {
y = pts[iPt][Y];
if (y < yMin)
yMin = y;
else if (y > yMax)
yMax = y;
}
int h = yMax - yMin;
Instance even = target.m_instance[EVEN];
Instance odd = target.m_instance[ODD];
shiftUpLines(pts, yMin);
int w = shiftToX0(pts, even, 0);
target.setOkPos(EVEN, w, h);
even.m_vec >>= even.m_offset;
shiftUpLines(pts, -1);
w = shiftToX0(pts, odd, 1);
odd.m_vec >>= N_COL;
target.setOkPos(ODD, w, h);
odd.m_vec >>= odd.m_offset;
}
static void genAllOrientations() {
for (int iPiece = 0; iPiece < N_PIECE_TYPE; iPiece++) {
int refPiece = BaseVecs[iPiece];
for (int iOrient = 0; iOrient < N_ORIENT; iOrient++) {
Piece p = s_basePiece[iPiece][iOrient];
genOrientation(refPiece, iOrient, p);
if ((iPiece == SKIP_PIECE) && (((iOrient / 3) & 1) != 0))
p.m_instance[0].m_allowed = p.m_instance[1].m_allowed = 0;
}
}
for (int iPiece = 0; iPiece < N_PIECE_TYPE; iPiece++) {
for (int iOrient = 0; iOrient < N_ORIENT; iOrient++) {
long mask = 1;
for (int iRow = 0; iRow < N_ROW; iRow++) {
Instance p = getPiece(iPiece, iOrient, (iRow & 1));
for (int iCol = 0; iCol < N_COL; iCol++) {
OkPieces allowed = g_okPieces[iRow][iCol];
if ((p.m_allowed & mask) != 0) {
allowed.pieceVec[iPiece][allowed.nPieces[iPiece]] = p.m_vec << iCol;
(allowed.nPieces[iPiece])++;
}
mask <<= 1;
}
}
}
}
}
static Instance getPiece(int iPiece, int iOrient, int iParity) {
return s_basePiece[iPiece][iOrient].m_instance[iParity];
}
}
//-- Main ---------------------------
public static void main(String[] args) {
if (args.length > 2)
System.exit(-1); // spec says this is an error;
long start = System.currentTimeMillis();
initGlobals();
Board b = new Board();
Piece.genAllOrientations();
Board.calcAlwaysBad();
b.genAllSolutions(0, 0, 0);
System.out.println(b.m_nSoln + " solutions found\n");
System.out.println(b.m_minSoln);
System.out.println(b.m_maxSoln);
long total = System.currentTimeMillis() - start;
System.out.println("[Meteor-Java Benchmark Result: " + total + "]");
}
}