package com.e2u.game;
import java.util.LinkedList;
import java.util.Random;
class LinkUpMap
{
public int x = 0, y = 0;
public int prev_x, prev_y;
public int picIndex = 0;
public int minCrossing = LinkUpFindPath.MAX_INT_VALUE;
public int minDistance = LinkUpFindPath.MAX_INT_VALUE;
public LinkUpMap(int x, int y)
{
this.x = x;
this.y = y;
picIndex = 0;
minCrossing = LinkUpFindPath.MAX_INT_VALUE;
minDistance = LinkUpFindPath.MAX_INT_VALUE;
}
public String toString()
{
return "x=" + x + ",y=" + y + ",minCrossing=" + minCrossing + ",minDistance=" +
minDistance + ",picIndex=" + picIndex;
}
}
public class LinkUpFindPath
{
private int rowNum = 3;
private int colNum = 4;
private static final int MAX_PIC_NUMBER = 6;
public static final int MAX_INT_VALUE = 99;
private static final int[][] directions = {
// left<--
{ -1, 0 },
// top
{ 0, -1 },
// -->right
{ 1, 0 },
// bottom
{ 0, 1 } };
private LinkUpMap[][] map;
private int[] picNumHash = null;
private LinkedList[] blockIndexList = null;
public LinkUpFindPath(int row, int col)
{
this.rowNum = row;
this.colNum = col;
map = new LinkUpMap[virtualRow()][virtualCol()];
for (int i = 0; i < virtualRow(); i++)
{
for (int j = 0; j < virtualCol(); j++)
{
map[i][j] = new LinkUpMap(j, i);
}
}
picNumHash = new int[MAX_PIC_NUMBER];
blockIndexList = new LinkedList[MAX_PIC_NUMBER];
for(int i = 0; i < MAX_PIC_NUMBER; i++)
{
blockIndexList[i] = new LinkedList();
}
}
public void initMap()
{
for (int i = 0; i < MAX_PIC_NUMBER; i++)
{
picNumHash[i] = 0;
}
Random random = new Random();
int rand = 0;
int numPerPic = (rowNum * colNum) / MAX_PIC_NUMBER;
for (int i = 1; i <= rowNum; i++)
{
for (int j = 1; j <= colNum; j++)
{
while (true)
{
rand = random.nextInt(MAX_PIC_NUMBER);
if (picNumHash[rand] < numPerPic)
{
map[i][j].picIndex = rand + 1;
picNumHash[rand]++;
blockIndexList[rand].add(map[i][j]);
break;
}
}
}
}
System.out.println();
}
public void showMap(int field)
{
for (int i = 0; i < virtualRow(); i++)
{
for (int j = 0; j < virtualCol(); j++)
{
// picIndex
if (field == 0)
{
if (map[i][j].picIndex < 10)
{
System.out.print(" ");
}
System.out.print(map[i][j].picIndex);
System.out.print(" ");
}
// minCrossing
else if (field == 1)
{
if (map[i][j].minCrossing < 10)
{
System.out.print(" ");
}
System.out.print(map[i][j].minCrossing);
System.out.print(" ");
}
// minDistance
else if (field == 2)
{
if (map[i][j].minDistance < 10)
{
System.out.print(" ");
}
System.out.print(map[i][j].minDistance);
System.out.print(" ");
}
}
System.out.println();
}
}
public void calMinCrosssing(int startX, int startY)
{
for (int i = 0; i < virtualRow(); i++)
{
for (int j = 0; j < virtualCol(); j++)
{
map[i][j].minCrossing = MAX_INT_VALUE;
map[i][j].minDistance = MAX_INT_VALUE;
}
}
// Temp make the minCrossing equals to -1
map[startY][startX].minCrossing = -1;
map[startY][startX].minDistance = 0;
map[startY][startX].prev_x = 0;
map[startY][startX].prev_y = 0;
LinkedList<LinkUpMap> que = new LinkedList<LinkUpMap>();
que.add(map[startY][startX]);
LinkUpMap linkUpMap = null;
int x, y, curMinCrossing, curMinDistance, distance;
while (!que.isEmpty())
{
linkUpMap = que.removeFirst();
x = linkUpMap.x;
y = linkUpMap.y;
curMinCrossing = linkUpMap.minCrossing;
curMinDistance = linkUpMap.minDistance;
// 4 directions
for (int d = 0; d < directions.length; d++)
{
distance = curMinDistance + 1;
x = linkUpMap.x + directions[d][0];
y = linkUpMap.y + directions[d][1];
while (checkBounds(x, y))
{
// visited, the previous block has less crossing. Better
if (map[y][x].minCrossing < curMinCrossing + 1)
{
break;
}
// visited, the previous block has the same crossing count.
if (map[y][x].minCrossing == curMinCrossing + 1)
{
// This means the current block has a bigger distance
// than the previous one
if (map[y][x].minDistance <= distance)
{
break;
}
else
{
System.out.println("[Update Distance]: " + map[y][x]);
}
}
map[y][x].minDistance = distance++;
map[y][x].minCrossing = curMinCrossing + 1;
map[y][x].prev_x = x - directions[d][0];
map[y][x].prev_y = y - directions[d][1];
if (map[y][x].picIndex != 0)
{
break;
}
que.add(map[y][x]);
x += directions[d][0];
y += directions[d][1];
}
}
}
// Restore
map[startY][startX].minCrossing = 0;
}
public void showPath(int fromX, int fromY, int toX, int toY)
{
int x = toX, y = toY;
int tempX, tempY;
while (x != fromX || y != fromY)
{
System.out.printf("(%d,%d) ", x, y);
tempX = map[y][x].prev_x;
tempY = map[y][x].prev_y;
x = tempX;
y = tempY;
}
System.out.printf("(%d,%d) ", fromX, fromY);
System.out.println();
}
public boolean findPath(int x1, int y1, int x2, int y2)
{
return findPath(x1, y1, x2, y2, 2);
}
public boolean findPath(int x1, int y1, int x2, int y2, int maxCrossing)
{
for (int i = 0; i < virtualRow(); i++)
{
for (int j = 0; j < virtualCol(); j++)
{
map[i][j].minCrossing = MAX_INT_VALUE;
map[i][j].minDistance = MAX_INT_VALUE;
map[i][j].prev_x = -1;
map[i][j].prev_y = -1;
}
}
// Temp make the minCrossing equals to -1
map[y1][x1].minCrossing = -1;
map[y1][x1].minDistance = 0;
LinkedList<LinkUpMap> que = new LinkedList<LinkUpMap>();
que.add(map[y1][x1]);
LinkUpMap linkUpMap = null;
int x, y, curMinCrossing, distance;
while (!que.isEmpty())
{
linkUpMap = que.removeFirst();
//No need to calculate the blocks that has more crossings.
if(linkUpMap.minCrossing >= maxCrossing)
{
que.clear();
break;
}
x = linkUpMap.x;
y = linkUpMap.y;
curMinCrossing = linkUpMap.minCrossing;
// 4 directions
for (int d = 0; d < directions.length; d++)
{
distance = linkUpMap.minDistance + 1;
x = linkUpMap.x + directions[d][0];
y = linkUpMap.y + directions[d][1];
while (checkBounds(x, y))
{
// visited, the previous block has less crossing. Better
if (curMinCrossing + 1 > map[y][x].minCrossing)
{
break;
}
// visited, the previous block has the same crossing count.
if (curMinCrossing + 1 == map[y][x].minCrossing)
{
// This means the current block has a bigger distance
// than the previous one
if (distance >= map[y][x].minDistance)
{
break;
}
}
map[y][x].minDistance = distance++;
map[y][x].minCrossing = curMinCrossing + 1;
map[y][x].prev_x = x - directions[d][0];
map[y][x].prev_y = y - directions[d][1];
if (map[y][x].picIndex != 0)
{
break;
}
que.add(map[y][x]);
x += directions[d][0];
y += directions[d][1];
}
}
}
// Restore
map[y1][x1].minCrossing = 0;
return (map[y2][x2].minCrossing <= maxCrossing);
}
public boolean hint(boolean autoClear)
{
boolean result = false;
for(int i = 0; i < MAX_PIC_NUMBER; i++)
{
LinkedList blockList = blockIndexList[i];
for(int j = 0; j < blockList.size(); j++)
{
LinkUpMap linkUpMap1 = (LinkUpMap)blockList.get(j);
for(int k = j + 1; k < blockList.size(); k++)
{
LinkUpMap linkUpMap2 = (LinkUpMap)blockList.get(k);
if(findPath(linkUpMap1.x, linkUpMap1.y, linkUpMap2.x, linkUpMap2.y))
{
System.out.print("Hint path: ");
showPath(linkUpMap1.x, linkUpMap1.y, linkUpMap2.x, linkUpMap2.y);
if(autoClear)
{
linkUpMap1.picIndex = 0;
linkUpMap2.picIndex = 0;
//System.out.print("Auto Cleared");
}
blockList.remove(k);
blockList.remove(j);
return true;
}
}
}
}
return result;
}
public void autoClear()
{
while(true)
{
boolean flag = false;
for(int i = 0; i < MAX_PIC_NUMBER; i++)
{
if(!blockIndexList[i].isEmpty())
{
flag = true;
break;
}
}
if(!flag)
{
System.out.println("Finished clear");
break;
}
if(!hint(true))
{
System.out.println("Can't clear now.");
break;
}
else
{
System.out.println();
showMap(0);
System.out.println();
}
}
}
public boolean checkLinkCondition(int x1, int y1, int x2, int y2)
{
return map[x1][y1].picIndex == map[x2][y2].picIndex;
}
private boolean checkBounds(int x, int y)
{
return (x >= 0 && x < virtualCol()) && (y >= 0 && y < virtualRow());
}
private int virtualRow()
{
return rowNum + 2;
}
private int virtualCol()
{
return colNum + 2;
}
public static void main(String[] args)
{
LinkUpFindPath linkUp = new LinkUpFindPath(6, 4);
linkUp.initMap();
linkUp.showMap(0);
System.out.println();
/*
linkUp.calMinCrosssing(2, 1);
linkUp.showMap(1);
System.out.println();
linkUp.showPath(2, 1, 4, 3);
System.out.println();
linkUp.showMap(2);
*/
linkUp.autoClear();
}
}