package de.bwvaachen.beamoflightgame.logic.strategies;
/*
Copyright (C) 2013 - 2014 by Andreas Pauls, Georg Braun, Christian Frühholz, Marius Spix, Christopher Müller and Bastian Winzen Part of the Beam Of Lights Puzzle Project
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
import de.bwvaachen.beamoflightgame.helper.AbstractTileVisitor;
import de.bwvaachen.beamoflightgame.helper.BoardTraverser;
import de.bwvaachen.beamoflightgame.helper.BoardUtils;
import static de.bwvaachen.beamoflightgame.helper.MathHelper.*;
import de.bwvaachen.beamoflightgame.helper.TraverseDirection;
import de.bwvaachen.beamoflightgame.logic.UnsolvablePuzzleException;
import de.bwvaachen.beamoflightgame.model.ITile;
import de.bwvaachen.beamoflightgame.model.ITileState;
import de.bwvaachen.beamoflightgame.model.LightTile;
import de.bwvaachen.beamoflightgame.model.LightTileState;
import de.bwvaachen.beamoflightgame.model.NumberTile;
import de.bwvaachen.beamoflightgame.model.NumberTileState;
public class IntersectionStrategy extends AbstractStrategy<NumberTileState> {
private boolean doesCross(ITileState a, ITileState b) {
return !( (a==LightTileState.EMPTY) || (a.equals(b)) );
}
@Override
public double getComplexity() {
return (getNumberOfPossibleSolutions() == 1) ? 1.0 : 10.0;
}
private int getNumberOfPossibleSolutions() {
//TODO
return 0;
}
@Override
public boolean isApplicableForTile(ITile<?> t) {
return (t instanceof NumberTile && ((NumberTile) t).getRemainingLightRange() > 0);
}
@Override
public boolean tryToSolve() throws UnsolvablePuzzleException {
//Overshadow the tile
NumberTile tile = (NumberTile) super.tile;
int remainingLightRange = tile.getRemainingLightRange();
final int maxRange[] = new int[states.length];
int index = 0;
BoardTraverser traverser = tile.getTraverser();
for(LightTileState state : states) {
int counter = 0;
traverser.reset();
while(traverser.shift(state.getTraverseDirection())) {
ITileState nextState = traverser.get().getTileState();
//stop if tile is crossed
if (doesCross(nextState, state)) break;
//ignore tiles of the same state
if (nextState.equals(state)) continue;
counter ++;
}
//maxRange[state.ordinal()] = counter
//This is not save because a bad guy could change the order of the LightTileState enum
maxRange[index++] = counter;
}
int availableRange = (maxRange[0]+maxRange[1]+maxRange[2]+maxRange[3]);
//case 1: the remaining range of the NumberTile cannot be covered by the
// all four directions together
if(availableRange < remainingLightRange)
throw new UnsolvablePuzzleException(tile);
//case 2: there is only one possibility to cover the remaining range of the
// NumberTile
else if(availableRange == remainingLightRange) {
for(int i=states.length-1; i<=0; i--) {
final LightTileState currentState = states[i];
final int range = maxRange[i];
final TraverseDirection currentDirection = currentState.getTraverseDirection();
traverser.reset();
if(!traverser.shift(currentDirection)) continue;
traverser.get().accept(new AbstractTileVisitor() {
public void visitLightTile(LightTile lt) {
utils.fillBoard(lt, range, currentDirection, currentState);
}
});
}
}
//case 3: there is more than one possibility to cover the whole remaining range of
// the number tile
else {
final AtomicInteger distributedTiles = new AtomicInteger(0);
loopOverSearchPaths:
for(int[] searchPath: searchPaths) {
int sum = multiplicateVector(searchPath, maxRange);
if(sum < remainingLightRange-distributedTiles.get()) {
//Only sum light tiles can be distributed to the given searchPath
//Therefore some tiles are forced to the other directions
int[] otherDirections =
subtractVector(V(1,1,1,1),searchPath);
final int tilesToDistribute = remainingLightRange - sum - distributedTiles.get();
for(int i=0; i<otherDirections.length; i++) {
final LightTileState currentState = states[i];
//Too few tiles to distribute
if(tilesToDistribute <= 0)
throw new UnsolvablePuzzleException(tile);
//More than one possibility?
if(tilesToDistribute > remainingLightRange)
continue;
//Only for the other directions
if(otherDirections[i] == 0){ continue; }
else {
for(int j=0; j<otherDirections.length;j++){
int temp = multiplicateVector(otherDirections, maxRange);
if(getValue(otherDirections) != 1){
if(i!=j && maxRange[i] >= tilesToDistribute && maxRange[j] >= tilesToDistribute)
continue loopOverSearchPaths;
if(temp > tilesToDistribute)
continue loopOverSearchPaths;
}
}
}
final TraverseDirection currentDirection = currentState.getTraverseDirection();
final int range = Math.min(tilesToDistribute, maxRange[i]);
traverser.reset();
if(!traverser.shift(currentDirection)) continue;
if(range > 0) {
maxRange[i] -= range;
traverser.get().accept(new AbstractTileVisitor() {
public void visitLightTile(LightTile lt) {
int filledTiles =
utils.fillBoard(lt, range, currentDirection, currentState);
System.out.printf("Tile: (%d, %d):\n", lt.getX(), lt.getY());
System.out.printf("utils.fillBoard(%s, %d, %s, %s);\n", lt, range, currentDirection, currentState);
distributedTiles.addAndGet(filledTiles);
}
});
}
}
}
}
}
//We assume that this strategy will always solve a puzzle
return true;
}
private static LightTileState states[];
private static int[][] searchPaths;
private static HashMap<LightTileState,Integer> directions;
private final static BoardUtils<LightTileState> utils;
static {
states = (LightTileState[]) LightTileState.allDirections().toArray();
utils = BoardUtils.getInstance(LightTileState.class);
directions = new HashMap<LightTileState,Integer>(states.length);
for(int i=0; i<states.length; i++) {
directions.put(states[i],i);
}
int[] N = new int[4],
E = new int[4],
S = new int[4],
W = new int[4];
N[directions.get(LightTileState.NORTH)] = 1;
E[directions.get(LightTileState.EAST)] = 1;
S[directions.get(LightTileState.SOUTH)] = 1;
W[directions.get(LightTileState.WEST)] = 1;
searchPaths = new int[14][4];
int i = -1;
//Erst einmal rausgenommen!
//three directions
//searchPaths[++i]=vectorSum(E,S,W);
//searchPaths[++i]=vectorSum(N,S,W);
//searchPaths[++i]=vectorSum(N,E,W);
//searchPaths[++i]=vectorSum(N,E,S);
//two directions
searchPaths[++i]=vectorSum(N,E);
searchPaths[++i]=vectorSum(N,S);
searchPaths[++i]=vectorSum(N,W);
searchPaths[++i]=vectorSum(E,S);
searchPaths[++i]=vectorSum(E,W);
searchPaths[++i]=vectorSum(S,W);
//one direction
searchPaths[++i]=N;
searchPaths[++i]=E;
searchPaths[++i]=S;
searchPaths[++i]=W;
}
}