Created
May 9, 2016 13:08
-
-
Save sebastientromp/be521585e784080b4f115f566e066955 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import java.util.HashSet; | |
import java.util.List; | |
import java.util.Random; | |
import java.util.Scanner; | |
import java.util.Set; | |
/** | |
* Auto-generated code below aims at helping you parse the standard input | |
* according to the problem statement. | |
**/ | |
class Player { | |
private static final int TIME_SAFETY = 80; | |
public static final int NUMBER_OF_ROWS = 12; | |
public static final int NUMBER_OF_COLUMNS = 6; | |
public static final int NUMBER_OF_ROTATIONS = 4; | |
public static final int GENOME_LENGTH = 4 * 2; | |
public static final int POPULATION_SIZE = 600; | |
public static final int TOURNAMENT_SIZE = 20; | |
public static final int NUMBER_OF_GENERATIONS = 5; | |
public static final float SURVIVAL_RATE = 0.1f; | |
private static final float MUTATION_RATE = 0.2f; | |
private static final float MUTATION_IMPACT = 1; | |
public static Random rnd; | |
// public static final float DANGER_MALUS = 10; | |
public static final float FUTURE_RISK_FACTOR = 1f; | |
// private static final float GRID_FILL_FACTOR = 0f; | |
private static final float SKULL_FILL_FACTOR = 1f; | |
private static final float GROUPING_FACTOR = 4f; | |
public static final int CARRY_OVER = 15; | |
public static final int CARRY_REPLICATION = 20; | |
public static long startTime = getSystemTime(); | |
public static List<Genome> carriedOver = new ArrayList<>(); | |
public static void main(String args[]) { | |
Scanner in = new Scanner(System.in); | |
if (rnd == null) { | |
rnd = new Random(); | |
} | |
// game loop | |
while (true) { | |
startTime = getSystemTime(); | |
Pair[] blocks = new Pair[8]; | |
for (int i = 0; i < 8; i++) { | |
// color of the first block | |
char colorA = Character.forDigit(in.nextInt(), 10); | |
// color of the attached block | |
char colorB = Character.forDigit(in.nextInt(), 10); | |
blocks[i] = new Pair(colorA, colorB); | |
} | |
System.err | |
.println(getSystemTime() - startTime + " - Computation to decide where to put block " + blocks[0]); | |
// My grid. One line of the map ('.' = empty, '0' = skull block, '1' | |
// to '5' = colored block) | |
Grid myGrid = new Grid(); | |
int currentRow = 0; | |
for (int i = 0; i < 12; i++) { | |
String row = in.next(); | |
myGrid.addRow(NUMBER_OF_ROWS - currentRow - 1, row); | |
currentRow++; | |
} | |
// The opponent | |
for (int i = 0; i < 12; i++) { | |
String row = in.next(); | |
} | |
execute(blocks, myGrid); | |
} | |
} | |
public static void execute(Pair[] blocks, Grid myGrid) { | |
// Compute the state of the grid - create groups mainly | |
myGrid.computeGroups(); | |
System.err.println(getSystemTime() - startTime + " - Computed state. Groups are " + myGrid.groups); | |
System.err.println(getSystemTime() - startTime + " - Pairs are " + Arrays.toString(blocks)); | |
System.err.println(myGrid); | |
// Create a population of genomes | |
List<Genome> population = buildInitialPopulation(blocks); | |
System.err.println(getSystemTime() - startTime + " - Built initial population"); | |
// Perform genetic algorithm + selection | |
List<Genome> fittest = evolve(myGrid, population, blocks); | |
for (int i = 0; i < 10; i++) { | |
System.err.println(getSystemTime() - startTime + " - Genome " + i + ": " + fittest.get(i)); | |
} | |
carryOver(fittest); | |
// "x": the column in which to drop your blocks | |
System.out.println(fittest.get(0).genes[0] + " " + fittest.get(0).genes[1]); | |
} | |
private static void carryOver(List<Genome> fittest) { | |
carriedOver = new ArrayList<>(); | |
for (int i = 0; i < CARRY_OVER; i++) { | |
carriedOver.add(fittest.get(i)); | |
} | |
} | |
private static List<Genome> evolve(Grid grid, List<Genome> population, Pair[] blocks) { | |
int currentGeneration = 1; | |
// System.err.println(getSystemTime() - startTime + " - Computing score | |
// for " + population.size() + " genomes "); | |
for (Genome genome : population) { | |
// System.err.println("\t" + (getSystemTime() - startTime) + " - | |
// Computing score for one genome"); | |
int score = computeScore(grid, genome, blocks); | |
// System.err.println("\t\t" + (System.nanoTime() - | |
// startTime) + " - Computed genome score"); | |
genome.score = score; | |
} | |
Collections.sort(population); | |
System.err.println(getSystemTime() - startTime + " - Computed initial score"); | |
while (currentGeneration < NUMBER_OF_GENERATIONS && System.currentTimeMillis() - startTime < TIME_SAFETY) { | |
// System.err | |
// .println(getSystemTime() - startTime + " - Starting breeding for | |
// generation " + currentGeneration); | |
population = breed(population); | |
// System.err.println("\t" + (getSystemTime() - startTime) + " - | |
// Bred"); | |
// Add a score to each genome | |
for (Genome genome : population) { | |
int score = computeScore(grid, genome, blocks); | |
genome.score = score; | |
} | |
Collections.sort(population); | |
System.err.println("\t" + (getSystemTime() - startTime) + " - Bred generation " + currentGeneration); | |
// System.err.println("\t" + (getSystemTime() - startTime) + " - Top | |
// Genome " + population.get(0)); | |
// System.err.println("\t" + (getSystemTime() - startTime) + " - | |
// Computed score"); | |
currentGeneration++; | |
} | |
return population; | |
} | |
// The meat of the processing | |
public static int computeScore(Grid grid, Genome genome, Pair[] blocks) { | |
int score = 0; | |
grid = grid.copy(); | |
// Compute the score after each placement | |
// System.err.println("\tLooking at genome " + genome); | |
for (int i = 0; i < GENOME_LENGTH / 2; i++) { | |
// System.err.println("\t\t" + (getSystemTime() - startTime) + " - | |
// Computing score for gene"); | |
// System.err.println("\tLooking at block " + blocks[i] + " and | |
// genes " + genome.genes[2 * i] + " " | |
// + genome.genes[2 * i + 1]); | |
try { | |
boolean valid = grid.addPair(blocks[i], genome.genes[2 * i], genome.genes[2 * i + 1]); | |
System.err.println("\t\t\t" + (getSystemTime() - startTime) + " - Added pair for " + genome); | |
System.err.println(grid); | |
System.err.println(grid.debug()); | |
if (!valid) { return -1; } | |
} | |
catch (ArrayIndexOutOfBoundsException e) { | |
System.err.println(genome); | |
throw e; | |
} | |
// int filled = grid.filled(); | |
// float gridFillFactor = Math.max(1, GRID_FILL_FACTOR * filled / | |
// (NUMBER_OF_ROWS * NUMBER_OF_COLUMNS)); | |
// float skullFillFactor = Math.max(1, SKULL_FILL_FACTOR * | |
// grid.skulls()); | |
int baseScore = grid.computeScore(genome, blocks); | |
int nuisancePoints = baseScore / 70; | |
int skullsDropped = nuisancePoints / 6; | |
// System.err.println("Dropped " + skullsDropped + " skulls at " + i | |
// + " with score " + baseScore); | |
// Only consider strong combos, unless we're losing | |
// if (skullsDropped == 0) { | |
// baseScore *= grid.skulls() * SKULL_FILL_FACTOR; | |
// } | |
// else { | |
// baseScore *= 2 * skullsDropped; | |
// } | |
// baseScore = Math.min(baseScore, 8000); | |
score += Math.max(0, 1 - FUTURE_RISK_FACTOR * i / GENOME_LENGTH) * baseScore; | |
// score += baseScore; | |
// System.err.println("\t\t\t" + (getSystemTime() - startTime) + " - | |
// New grid for " + blocks[i] + " and genes " | |
// + genome.genes[2 * i] + " " + genome.genes[2 * i + 1]); | |
// System.err.println("\t\t\tscore is " + score); | |
// System.err.println(grid); | |
genome.skullsDropped[i] = skullsDropped; | |
} | |
genome.finalGrid = grid; | |
return score; | |
} | |
private static List<Genome> breed(List<Genome> population) { | |
// Collections.sort(population); | |
// System.err.println("\t\t" + (System.nanoTime() - startTime) | |
// + " - Sorted population"); | |
List<Genome> fittest = population.subList(0, (int) (population.size() * SURVIVAL_RATE)); | |
// System.err.println("\t\t" + (System.nanoTime() - startTime) | |
// + " - Kept fittest individuals"); | |
List<Genome> winners = new ArrayList<>(fittest); | |
while (winners.size() < POPULATION_SIZE) { | |
Genome parent1 = tournamentSelection(fittest); | |
Genome parent2 = tournamentSelection(fittest); | |
Genome child = crossOver(parent1, parent2); | |
if (child.isValid()) { | |
winners.add(child); | |
} | |
Genome mutated = child.copy(); | |
mutate(mutated); | |
if (mutated.isValid()) { | |
winners.add(mutated); | |
} | |
} | |
return winners; | |
} | |
private static Genome crossOver(Genome parent1, Genome parent2) { | |
Genome child = new Genome(); | |
for (int i = 0; i < GENOME_LENGTH; i++) { | |
child.genes[i] = Math.random() < 0.5 ? parent1.genes[i] : parent2.genes[i]; | |
} | |
return child; | |
} | |
private static void mutate(Genome child) { | |
for (int i = 0; i < GENOME_LENGTH; i++) { | |
if (Math.random() < MUTATION_RATE) { | |
int modulo = i % 2 == 0 ? NUMBER_OF_COLUMNS : NUMBER_OF_ROTATIONS; | |
child.genes[i] = (child.genes[i] + (int) ((rnd.nextInt(3) - 1) * MUTATION_IMPACT) + modulo) % modulo; | |
} | |
} | |
} | |
private static Genome tournamentSelection(List<Genome> fittest) { | |
List<Genome> participants = new ArrayList<>(); | |
for (int i = 0; i < TOURNAMENT_SIZE; i++) { | |
participants.add(fittest.get(rnd.nextInt(fittest.size()))); | |
} | |
Collections.sort(participants); | |
return participants.get(0); | |
} | |
private static List<Genome> buildInitialPopulation(Pair[] blocks) { | |
List<Genome> population = new ArrayList<>(); | |
carryFrom(population); | |
for (int i = 0; i < NUMBER_OF_COLUMNS; i++) { | |
for (int j = 0; j < NUMBER_OF_ROTATIONS; j++) { | |
Genome genome = new Genome(); | |
genome.genes[0] = i; | |
genome.genes[1] = j; | |
for (int k = 2; k < GENOME_LENGTH; k++) { | |
if (k % 2 == 0) { | |
genome.genes[k] = rnd.nextInt(NUMBER_OF_COLUMNS); | |
} | |
else { | |
genome.genes[k] = rnd.nextInt(NUMBER_OF_ROTATIONS); | |
} | |
} | |
// Filter out invalid genomes | |
if (genome.isValid()) { | |
population.add(genome); | |
} | |
} | |
} | |
while (population.size() < POPULATION_SIZE) { | |
Genome genome = new Genome(); | |
for (int j = 0; j < GENOME_LENGTH; j++) { | |
if (j % 2 == 0) { | |
genome.genes[j] = rnd.nextInt(NUMBER_OF_COLUMNS); | |
} | |
else { | |
genome.genes[j] = rnd.nextInt(NUMBER_OF_ROTATIONS); | |
} | |
} | |
// Filter out invalid genomes | |
if (genome.isValid()) { | |
population.add(genome); | |
} | |
} | |
return population; | |
} | |
private static void carryFrom(List<Genome> population) { | |
for (Genome old : carriedOver) { | |
for (int j = 0; j < CARRY_REPLICATION; j++) { | |
Genome clone = old.copy(); | |
for (int i = 0; i < GENOME_LENGTH - 2; i++) { | |
clone.genes[i] = old.genes[i + 2]; | |
} | |
clone.genes[GENOME_LENGTH - 2] = rnd.nextInt(NUMBER_OF_COLUMNS); | |
clone.genes[GENOME_LENGTH - 1] = rnd.nextInt(NUMBER_OF_ROTATIONS); | |
if (clone.isValid()) { | |
population.add(clone); | |
} | |
} | |
} | |
} | |
private static long getSystemTime() { | |
// return System.nanoTime(); | |
return System.currentTimeMillis(); | |
} | |
public static class Genome implements Comparable<Genome> { | |
int[] genes = new int[GENOME_LENGTH]; | |
int score; | |
Grid finalGrid; | |
List<String> debugInfo = new ArrayList<>(); | |
int[] skullsDropped = new int[GENOME_LENGTH / 2]; | |
@Override | |
public int compareTo(Genome o) { | |
// First look if anyone can drop 2 skulls | |
int oDropSkulls2 = o.dropSkulls(2); | |
int thisDropSkulls2 = dropSkulls(2); | |
if (oDropSkulls2 > -1 && thisDropSkulls2 == -1) { | |
return 1; | |
} | |
else if (oDropSkulls2 == -1 && thisDropSkulls2 > -1) { | |
return -1; | |
} | |
else if (oDropSkulls2 > -1 && thisDropSkulls2 > -1) { | |
if (oDropSkulls2 < thisDropSkulls2) { | |
return 1; | |
} | |
else if (oDropSkulls2 > thisDropSkulls2) { | |
return -1; | |
} | |
else { | |
return o.score - score; | |
} | |
} | |
else { | |
int oDropSkulls1 = o.dropSkulls(1); | |
int thisDropSkulls1 = dropSkulls(1); | |
if (oDropSkulls1 > -1 && thisDropSkulls1 == -1) { | |
return 1; | |
} | |
else if (oDropSkulls1 == -1 && thisDropSkulls1 > -1) { | |
return -1; | |
} | |
else if (oDropSkulls1 > -1 && thisDropSkulls1 > -1) { | |
if (oDropSkulls1 < thisDropSkulls1) { | |
return 1; | |
} | |
else if (oDropSkulls1 > thisDropSkulls1) { | |
return -1; | |
} | |
else { | |
return o.score - score; | |
} | |
} | |
} | |
return o.score - score; | |
} | |
private int dropSkulls(int amount) { | |
for (int i = 0; i < skullsDropped.length; i++) { | |
if (skullsDropped[i] >= amount) { return i; } | |
} | |
return -1; | |
} | |
public boolean isValid() { | |
for (int i = 0; i < GENOME_LENGTH; i = i + 2) { | |
if (genes[i] < 0 || genes[i] > 5 || genes[i] == 0 && genes[i + 1] == 2 | |
|| genes[i] == 5 && genes[i + 1] == 0) { return false; } | |
} | |
return true; | |
} | |
public Genome copy() { | |
Genome copy = new Genome(); | |
for (int i = 0; i < genes.length; i++) { | |
copy.genes[i] = genes[i]; | |
} | |
return copy; | |
} | |
public void addDebugInfo(int blocksCleared, int chainPower, int colorBonus, int groupBonus, | |
int blocksInGroups) { | |
String debug = blocksCleared + "," + chainPower + "," + colorBonus + "," + groupBonus; | |
debugInfo.add(debug); | |
} | |
@Override | |
public String toString() { | |
return "Genome [genes=" + Arrays.toString(genes) + ", score=" + score + ", skullsDropped=" | |
+ Arrays.toString(skullsDropped) + ", debugInfo=" + debugInfo + "]"; | |
} | |
// @Override | |
// public String toString() { | |
// return "Genome [genes=" + Arrays.toString(genes) + ", score=" + score | |
// + ", debugInfo=" + debugInfo | |
// + ", finalGrid=\n" + finalGrid + "]"; | |
// } | |
} | |
public static class Pair { | |
char colorA, colorB; | |
public Pair(char colorA, char colorB) { | |
super(); | |
this.colorA = colorA; | |
this.colorB = colorB; | |
} | |
@Override | |
public String toString() { | |
return "Pair [colorA=" + colorA + ", colorB=" + colorB + "]"; | |
} | |
} | |
public static class Grid { | |
Cell[] rows = new Cell[NUMBER_OF_COLUMNS * NUMBER_OF_ROWS]; | |
Set<Group> groups = new HashSet<>(); | |
public void addRow(int currentRow, String row) { | |
for (int i = 0; i < NUMBER_OF_COLUMNS; i++) { | |
rows[currentRow * NUMBER_OF_COLUMNS + i] = new Cell(currentRow * NUMBER_OF_COLUMNS + i, row.charAt(i)); | |
} | |
} | |
public int skulls() { | |
int skulls = 0; | |
for (int i = 0; i < rows.length; i++) { | |
if (rows[i].block == '0') { | |
skulls++; | |
} | |
} | |
return skulls; | |
} | |
public int filled() { | |
int filled = 0; | |
for (int i = 0; i < rows.length; i++) { | |
if (rows[i].block != '.') { | |
filled++; | |
} | |
} | |
return filled; | |
} | |
public float highestColumn() { | |
for (int i = rows.length - 1; i >= 0; i--) { | |
if (rows[i].block != '.') { return i / NUMBER_OF_COLUMNS; } | |
} | |
return 0; | |
} | |
public int computeScore(Genome genome, Pair[] blocks) { | |
int chainSteps = -1; | |
int[] colorsCleared = new int[5]; | |
int blocksCleared = 0; | |
int groupBonus = 0; | |
int blocksInGroups = 0; | |
System.err | |
.println("Starting chain score iterations for " + genome + " and pairs " + Arrays.toString(blocks)); | |
// Each pass in the loop is a step in the score computation process | |
while (true) { | |
int blocksClearedThisStep = 0; | |
System.err.println("Iterating in chain score"); | |
// System.err.println("Current group map is \n" + debug()); | |
System.err.println(this); | |
// Add the score | |
for (Group group : groups) { | |
blocksInGroups += group.size(); | |
// Something happens! | |
if (group.size() >= 4) { | |
for (int index : group.nodes) { | |
// try { | |
colorsCleared[Character.getNumericValue(rows[index].block) - 1] = 1; | |
// } | |
// catch (ArrayIndexOutOfBoundsException e) { | |
// System.err.println("error getting numeric value | |
// from cell " + rows[index] | |
// + " for group " + group + " with groups " + | |
// groups); | |
// throw e; | |
// } | |
rows[index].block = '.'; | |
rows[index].group = null; | |
} | |
blocksClearedThisStep += group.size(); | |
Set<Integer> neighbours = getNeighbors(group.nodes); | |
for (int index : neighbours) { | |
if (rows[index].block == '0') { | |
// clusterSize++; | |
rows[index].block = '.'; | |
rows[index].group = null; | |
} | |
} | |
if (group.size() == 5) { | |
groupBonus += 1; | |
} | |
else if (group.size() == 6) { | |
groupBonus += 2; | |
} | |
else if (group.size() == 7) { | |
groupBonus += 3; | |
} | |
else if (group.size() == 8) { | |
groupBonus += 4; | |
} | |
else if (group.size() == 9) { | |
groupBonus += 5; | |
} | |
else if (group.size() == 10) { | |
groupBonus += 6; | |
} | |
else if (group.size() >= 11) { | |
groupBonus += 8; | |
} | |
blocksCleared += blocksClearedThisStep; | |
} | |
} | |
if (blocksClearedThisStep > 0) { | |
// Make the blocks fall down when applicable | |
falldownBlocks(); | |
// Recompute groups | |
computeGroups(); | |
chainSteps++; | |
} | |
else { | |
break; | |
} | |
} | |
int chainPower = 0; | |
if (chainSteps > 0) { | |
chainPower = 8; | |
chainSteps--; | |
while (chainSteps > 0) { | |
chainPower = 2 * chainPower; | |
chainSteps--; | |
} | |
} | |
int numberOfColors = 0; | |
for (int i = 0; i < colorsCleared.length; i++) { | |
numberOfColors += colorsCleared[i]; | |
} | |
int colorBonus = 0; | |
if (numberOfColors == 2) { | |
colorBonus = 2; | |
} | |
else if (numberOfColors == 3) { | |
colorBonus = 4; | |
} | |
else if (numberOfColors == 4) { | |
colorBonus = 8; | |
} | |
else if (numberOfColors == 5) { | |
colorBonus = 16; | |
} | |
int multiplier = Math.min(Math.max(1, chainPower + colorBonus + groupBonus), 999); | |
int score = (int) (10 * (blocksCleared * multiplier + GROUPING_FACTOR * blocksInGroups)); | |
genome.addDebugInfo(blocksCleared, chainPower, colorBonus, groupBonus, blocksInGroups); | |
// System.err.println("\t" + (getSystemTime() - startTime) + " - | |
// Updated grid"); | |
// If there is something in the top row, decrease score | |
// for (int i = rows.length - NUMBER_OF_COLUMNS - 1; i < | |
// rows.length; i++) { | |
// if (rows[i].block != '.') { | |
// score -= DANGER_MALUS; | |
// } | |
// } | |
// System.err.println("\t" + (getSystemTime() - startTime) + " - | |
// Updated danger malus"); | |
return score; | |
} | |
private Set<Integer> getNeighbors(Set<Integer> nodes) { | |
Set<Integer> neighbors = new HashSet<>(); | |
for (int index : nodes) { | |
if (rows[index].isNotLeft() && !nodes.contains(rows[index - 1])) { | |
neighbors.add(index - 1); | |
} | |
if (rows[index].isNotRight() && !nodes.contains(rows[index + 1])) { | |
neighbors.add(index + 1); | |
} | |
if (rows[index].isNotBottom() && !nodes.contains(rows[index - NUMBER_OF_COLUMNS])) { | |
neighbors.add(index - NUMBER_OF_COLUMNS); | |
} | |
if (rows[index].isNotTop() && !nodes.contains(rows[index + NUMBER_OF_COLUMNS])) { | |
neighbors.add(index + NUMBER_OF_COLUMNS); | |
} | |
} | |
return neighbors; | |
} | |
private void falldownBlocks() { | |
boolean fellDown = true; | |
while (fellDown) { | |
fellDown = false; | |
for (int i = 0; i < rows.length - NUMBER_OF_COLUMNS; i++) { | |
if (rows[i].block == '.' && rows[i + NUMBER_OF_COLUMNS].block != '.') { | |
fellDown = true; | |
rows[i].block = rows[i + NUMBER_OF_COLUMNS].block; | |
rows[i + NUMBER_OF_COLUMNS].block = '.'; | |
} | |
} | |
} | |
} | |
public boolean addPair(Pair pair, int position, int rotation) { | |
boolean valid = false; | |
System.err.println("adding pair " + pair); | |
System.err.println(this); | |
// Get back to the case where the first element of the pair is | |
// always at the bottom | |
if (rotation == 3) { | |
char previousColorA = pair.colorA; | |
pair.colorA = pair.colorB; | |
pair.colorB = previousColorA; | |
rotation = 1; | |
} | |
// System.err.println("\t\t\tGroups before first attachment " + | |
// groups); | |
// Add the first element | |
for (int i = 0; i < NUMBER_OF_ROWS; i++) { | |
Cell cell = rows[i * NUMBER_OF_COLUMNS + position]; | |
if (cell.block == '.') { | |
// System.err.println("\t\t\tadding " + pair.colorA + " | |
// to " | |
// + position); | |
// System.err.println("\t\t\tattaching first element " + | |
// pair.colorA + " to " + cell); | |
cell.block = pair.colorA; | |
// reattach(cell); | |
valid = true; | |
break; | |
} | |
} | |
// System.err.println("\t\t\tGroups after first attachment " + | |
// groups); | |
// Add the second element | |
if (rotation == 0) { | |
position++; | |
} | |
else if (rotation == 2) { | |
position--; | |
} | |
for (int i = 0; i < NUMBER_OF_ROWS; i++) { | |
Cell cell = rows[i * NUMBER_OF_COLUMNS + position]; | |
if (cell.block == '.') { | |
// System.err.println("\t\t\tadding " + pair.colorB + " to " | |
// + position); | |
// System.err.println("\t\t\tattaching second element " + | |
// pair.colorB + " to " + cell); | |
cell.block = pair.colorB; | |
// reattach(cell); | |
valid = true; | |
break; | |
} | |
} | |
if (valid) { | |
computeGroups(); | |
} | |
// System.err.println("\t\t\tGroups after attachment " + groups); | |
return valid; | |
} | |
// private void reattach(Cell cell) { | |
// if (cell.block == '0' || cell.block == '.') { return; } | |
// | |
// List<Group> potentialGroups = new ArrayList<>(); | |
// | |
// if (cell.isNotLeft() && rows[cell.index - 1].block == cell.block) { | |
// if (rows[cell.index - 1].group != null) { | |
// potentialGroups.add(rows[cell.index - 1].group); | |
// } | |
// else { | |
// Group group = cell.group(rows[cell.index - 1]); | |
// groups.add(group); | |
// potentialGroups.add(group); | |
// } | |
// } | |
// if (cell.isNotBottom() && rows[cell.index - NUMBER_OF_COLUMNS].block | |
// == cell.block) { | |
// if (rows[cell.index - NUMBER_OF_COLUMNS].group != null) { | |
// potentialGroups.add(rows[cell.index - NUMBER_OF_COLUMNS].group); | |
// } | |
// else { | |
// Group group = cell.group(rows[cell.index - NUMBER_OF_COLUMNS]); | |
// groups.add(group); | |
// potentialGroups.add(group); | |
// } | |
// } | |
// if (cell.isNotRight() && rows[cell.index + 1].block == cell.block) { | |
// // System.err.println("\t\t\t\tHas right neighbor " + | |
// // rows[cell.index + 1].block + " " + cell.block); | |
// if (rows[cell.index + 1].group != null) { | |
// potentialGroups.add(rows[cell.index + 1].group); | |
// } | |
// else { | |
// Group group = cell.group(rows[cell.index + 1]); | |
// groups.add(group); | |
// potentialGroups.add(group); | |
// } | |
// } | |
// | |
// // Consolidate the groups | |
// Group newGroup = new Group(); | |
// for (Iterator<Group> iterator = potentialGroups.iterator(); | |
// iterator.hasNext();) { | |
// Group group = iterator.next(); | |
// for (Integer index : group.nodes) { | |
// newGroup.nodes.add(index); | |
// rows[index].group = newGroup; | |
// } | |
// iterator.remove(); | |
// groups.remove(group); | |
// } | |
// if (!newGroup.nodes.isEmpty()) { | |
// groups.add(newGroup); | |
// } | |
// } | |
public void computeGroups() { | |
groups = new HashSet<>(); | |
for (int i = 0; i < rows.length; i++) { | |
rows[i].group = null; | |
// If it's empty, it's not part of a group | |
if (rows[i].block == '.' || rows[i].block == '0') { | |
continue; | |
} | |
// If has a left neighbor | |
if (rows[i].isNotLeft() && rows[i - 1].block == rows[i].block) { | |
Group group = null; | |
if (rows[i - 1].group != null) { | |
group = rows[i - 1].group; | |
} | |
else { | |
group = new Group(); | |
} | |
group.add(rows[i - 1]); | |
group.add(rows[i]); | |
rows[i].group = group; | |
rows[i - 1].group = group; | |
groups.add(group); | |
} | |
// If has a bottom neighbor | |
if (rows[i].isNotBottom() && rows[i - NUMBER_OF_COLUMNS].block == rows[i].block) { | |
Group group = null; | |
if (rows[i - NUMBER_OF_COLUMNS].group != null) { | |
group = rows[i - NUMBER_OF_COLUMNS].group; | |
if (rows[i].group != null) { | |
Group oldGroup = rows[i].group; | |
for (Integer index : rows[i].group.nodes) { | |
group.nodes.add(index); | |
rows[index].group = group; | |
} | |
groups.remove(oldGroup); | |
} | |
} | |
else if (rows[i].group != null) { | |
group = rows[i].group; | |
} | |
else { | |
group = new Group(); | |
} | |
group.add(rows[i - NUMBER_OF_COLUMNS]); | |
group.add(rows[i]); | |
rows[i].group = group; | |
rows[i - NUMBER_OF_COLUMNS].group = group; | |
} | |
} | |
// for (int i = 0; i < rows.length; i++) { | |
// rows[i].computeGroupSize(); | |
// } | |
} | |
public Grid copy() { | |
Grid copy = new Grid(); | |
for (int i = 0; i < NUMBER_OF_COLUMNS * NUMBER_OF_ROWS; i++) { | |
copy.rows[i] = rows[i].copy(); | |
} | |
for (Group group : groups) { | |
copy.groups.add(group.copy()); | |
} | |
for (Group group : copy.groups) { | |
for (int index : group.nodes) { | |
copy.rows[index].group = group; | |
} | |
} | |
return copy; | |
} | |
public String debug() { | |
String groups = this.groups.toString() + "\n"; | |
String ret = "" + groups; | |
for (int i = 0; i < NUMBER_OF_ROWS; i++) { | |
String row = ""; | |
for (int j = 0; j < NUMBER_OF_COLUMNS; j++) { | |
int groupSize = rows[i * NUMBER_OF_COLUMNS + j].groupSize(); | |
row += (groupSize == 0 ? "." : groupSize) + " "; | |
} | |
ret = row + "\n" + ret; | |
} | |
ret += "\n\n"; | |
return ret; | |
} | |
@Override | |
public String toString() { | |
String ret = ""; | |
for (int i = 0; i < NUMBER_OF_ROWS; i++) { | |
String row = ""; | |
for (int j = 0; j < NUMBER_OF_COLUMNS; j++) { | |
row += rows[i * NUMBER_OF_COLUMNS + j].block + " "; | |
} | |
ret = row + "\n" + ret; | |
} | |
ret += "\n\n"; | |
// for (int i = 0; i < NUMBER_OF_ROWS; i++) { | |
// for (int j = 0; j < NUMBER_OF_COLUMNS; j++) { | |
// int groupSize = rows[i * NUMBER_OF_COLUMNS + j].groupSize; | |
// ret += (groupSize == 0 ? " " : groupSize) + " "; | |
// } | |
// ret += "\n"; | |
// } | |
return ret; | |
} | |
} | |
public static class Cell { | |
char block; | |
int index; | |
Group group = null; | |
public Cell(int index, char block) { | |
super(); | |
this.index = index; | |
this.block = block; | |
} | |
public boolean isNotBottom() { | |
return index >= NUMBER_OF_COLUMNS; | |
} | |
public boolean isNotTop() { | |
return index <= NUMBER_OF_ROWS * (NUMBER_OF_COLUMNS - 1); | |
} | |
public boolean isNotLeft() { | |
return index % NUMBER_OF_COLUMNS > 0; | |
} | |
public boolean isNotRight() { | |
return index % NUMBER_OF_COLUMNS < NUMBER_OF_COLUMNS - 1; | |
} | |
public Cell copy() { | |
Cell copy = new Cell(index, block); | |
// copy.groupSize = groupSize; | |
return copy; | |
} | |
public int groupSize() { | |
return group == null || block == '.' ? 0 : group.size(); | |
} | |
// public Group group(Cell cell) { | |
// if (cell.block == '0' || cell.block == '.') { return null; } | |
// if (cell.group == null) { | |
// cell.group = new Group(); | |
// cell.group.add(cell); | |
// } | |
// cell.group.add(this); | |
// group = cell.group; | |
// return group; | |
// } | |
@Override | |
public String toString() { | |
return "Cell [block=" + block + ", index=" + index + ", group=" + group + "]"; | |
} | |
} | |
public static class Group { | |
Set<Integer> nodes = new HashSet<>(); | |
public void add(Cell node) { | |
nodes.add(node.index); | |
} | |
public Group copy() { | |
Group copy = new Group(); | |
for (int index : nodes) { | |
copy.nodes.add(index); | |
} | |
return copy; | |
} | |
public int size() { | |
return nodes.size(); | |
} | |
@Override | |
public String toString() { | |
return "Group [nodes=" + nodes + "]"; | |
} | |
} | |
} |
Un algo génétique ça donne de jolis noms de fonctions :)
Je connais pas du tout le principe par contre, donc impossible de commenter là dessus.
D'un point de vue perf, j'ai l'impression que ça va être moyen car quand tu fais une copie de Grid, beaucoup de copies de structures à tailles variables et de "new". Tu tournais à combien en 100 ms ?
J'imagine qu'avec ce type d'algo la qualité du résultat dépend quand même bien du nombre de simuls.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Le computescore à la L159 a un peu changé par rapport à ce que j'avais submit - j'ai tenté un truc pour ne pas utiliser le score en tant que tel, mais plus les nuisance points que ça générait, mais j'ai pas réussi à faire un truc efficace