Package kakuro.server.tablefactory

Source Code of kakuro.server.tablefactory.BipartiteTableFactory

package kakuro.server.tablefactory;

import java.io.FileWriter;
import java.util.HashMap;
import java.util.Random;
import java.util.Vector;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import kakuro.server.ISkeletonTable;
import kakuro.server.ITableFactory;
import kakuro.table.ITable;
import kakuro.table.Position;

/** Tábla kitöltő osztály. A játéktáblát max. kilenc fokszámú páros gráffá alakítja, ahol a tábla egy szava
* felel meg egy csúcsnak a gráfban, és él fut két csúcs között, ha a két szó metszi egymást a táblában.
* A gráf páros, mert egy szó vagy vízszintes, vagy függőleges, és két vízszintes szó nem tudja metszeni egymást.
* A max. fokszám pedig a szavak max. hosszából következik.
* <p>
* Páros gráfok élszínezésére vonatkozik a Kőnig tétel, aminek a bizonyítása szerencsére konstruktív. Ezt a
* javító utas algoritmust használja a kód. Lassú implementáció, léteznek sokkal gyorsabb módszerek, de
* ekkor gráf méretekben gyakorlatilag mindegy a gyorsasága.
* */
public class BipartiteTableFactory implements ITableFactory {
  /** Singleton példány */
  private static final BipartiteTableFactory _instance = new BipartiteTableFactory();
  /** Random generátor, az aktuális idő a seed */
  private Random _random = new Random(System.currentTimeMillis());

  /** Privát egyetlen konstruktor */
  private BipartiteTableFactory() { 
  }
 
  /** Singleton példány getter */
  public static ITableFactory getInstance() {
    return _instance;
  }

  /** Nem használt fv, tesztelésre csak! */
  public ITable generateTable(int skillLevel) {
    if (skillLevel == ITable.DIFFICULTY_EASY) {
      return populateSkeleton(new SimpleSkeletonTable());
    }
    return populateSkeleton(new FixedSkeletonTable());
  }
 
  /** Adott nehézségi szinthez Skeletont generál, a {@link SkeletonGenerator} segítségével */
  public ISkeletonTable generateSkeleton(int skillLevel) {
        return new SkeletonGenerator().generateSkeleton(skillLevel);
  }
 
  /** Adott Skeletont kitölt, és visszaadja a kész táblát. */
  public ITable populateSkeleton(ISkeletonTable skeleton) {   
    int sorokszama = skeleton.getRowCount();
    int oszlopokszama = skeleton.getColumnCount();
    System.out.println("sorok.sz: "+sorokszama+" # oszlopok.sz: "+oszlopokszama);
   
    int[][] bwMatrix = new int[oszlopokszama][sorokszama];
    int[][] horizontalWords = new int[oszlopokszama][sorokszama];
    int[][] verticalWords = new int[oszlopokszama][sorokszama];
    int[][] result = new int[oszlopokszama][sorokszama];
   
    /* Eloszor osszegyujtom egy matrixba a fekete-feher ertekeket, 1, ha fekete,
     * ebbol varazsolom ki a graf csomopontjait majd   */
    for (int y=0; y<sorokszama; y++) {
      for (int x=0; x<oszlopokszama; x++) {
        int celltipus = skeleton.getType(new Position(x, y));
        bwMatrix[x][y] = (celltipus==ISkeletonTable.CELL_BLACK?1:0);
      }
    }
    print(bwMatrix, "bwMatrix");
   
    /* Eloallitom a vizszintes szavakat tartalmazo matrixot, a szavakat megszamozom 1-tol */
    int hcounter = 1;
    boolean inword = false;
    HashMap<Integer, Integer> hCount = new HashMap<Integer, Integer>();
    for (int y=0; y<sorokszama; y++) {
      for (int x=0; x<oszlopokszama; x++) {
            if (bwMatrix[x][y]==0) {
                  horizontalWords[x][y] = hcounter;
                  inword = true;
                  hCount.put(hcounter, hcounter);
            } else {
                  if (inword) hcounter++;
                  inword = false;
            }
      }
    }
    print(horizontalWords, "horizontal words");
   
    /* Ugyanez, csak fuggolegesre is. itt meg annyit csinalok, hogy lementem egy mapbe azt, hogy
     * egy adott fuggoleges es vizszintes szo metszetehez, ami ugye valojaban egy cella az eredeti
     * tablaban, pontosan milyen Position is tartozik. igy tehat a graf egy elehez majd siman meg tudom
     * mondani, hogy az valojaban melyik cella volt. */
    int vcounter = 1;
    inword = false;
    HashMap<Integer, Integer> vCount = new HashMap<Integer, Integer>();
    HashMap<Position, Position> wordToCellMap = new HashMap<Position, Position>();
    for (int x=0; x<oszlopokszama; x++) {
          for (int y=0; y<sorokszama; y++) {
            if (bwMatrix[x][y]==0) {
                  verticalWords[x][y] = vcounter;
                  inword = true;
                  vCount.put(vcounter, vcounter);
                  wordToCellMap.put(new Position(horizontalWords[x][y], vcounter), new Position(x, y));
            } else {
                  if (inword) vcounter++;
                  inword = false;
            }
      }
    }
    print(verticalWords, "vertical words");
   
    /* Fo ciklus. Az innerCycle fuggveny egy maximalis (maximum!) parositast ad vissza. Ezt elmentem, kiosztom az
     * eleknek egy szamot, es a kovetkezo korben kiveszem a generalasbol ezeket az eleket - erre szolgal az exclusions
     * map.  */
    ArrayList<Position> maximumMatching = null;
    HashMap<Position, Boolean> exclusions = new HashMap<Position, Boolean>();
    int run = 0;
    int[] statistics = new int[10];
    do {
          maximumMatching = innerCycle(sorokszama, oszlopokszama, hCount.size(), vCount.size(), bwMatrix, horizontalWords, verticalWords, wordToCellMap, exclusions, run);
          statistics[run] = maximumMatching.size();
          run++;
          for (int i=0; i<maximumMatching.size(); i++) {
                exclusions.put(maximumMatching.get(i), true);
                Position p = wordToCellMap.get(maximumMatching.get(i));
                result[p.x][p.y] = run;
          }
    } while ((maximumMatching.size()>0) && (run<9));
   
    HashMap<Position, Boolean> problematicEdgesMap = new HashMap<Position, Boolean>();
    Iterator<Position> it = wordToCellMap.keySet().iterator();
    while (it.hasNext()) {
          Position e = it.next();
          if (!exclusions.containsKey(e)) problematicEdgesMap.put(e, true);
    }
   
    /* problemas elek a 9-dik futas utan: ezek azok az elek a grafban, ahol nem tudott a moho algoritmus szint kiosztani
     * ezekre javito ut kell */
    Iterator<Position> problematicEdges = problematicEdgesMap.keySet().iterator();
    while (problematicEdges.hasNext()) {
        statistics[9]++;
          Position edge = problematicEdges.next();
          int[] freeColors = getFreeColors(edge, wordToCellMap, hCount.size(), vCount.size(), result);
          System.out.println("Left out: "+edge+", free colors: "+freeColors[0]+" - "+freeColors[1]);
          Position p = wordToCellMap.get(edge);
              result[p.x][p.y] = freeColors[0];
          Position next = edge;
          HashMap<Position, Boolean> visited = new HashMap<Position, Boolean>();
          boolean horizontalToVertical = false;
          while ((next = getNextEnRoute(next, freeColors[0], freeColors[1], wordToCellMap, result, hCount.size(), vCount.size(), horizontalToVertical, visited))!=null) {
                //invertColors(next, freeColors[0], freeColors[1], wordToCellMap, result);
                horizontalToVertical ^= true;
          }
    }
   
    try {
      String s = "";
      for (int i=0; i<statistics.length; i++) {
        s += (i==0?"":"\t")+statistics[i];
      }
      FileWriter writer = new FileWriter("statistics.txt", true);
      writer.append(s+"\r\n");
      writer.close();
    } catch (Throwable t) {
      t.printStackTrace();
    }
   
    boolean[] digitsVisited = new boolean[9];
    int[] digits = new int[10];
    for (int i=1; i<10; i++) {
      digits[i] = nextFree(digitsVisited)+1;
    }
    return new GeneralTable(skeleton, getPermuted(result, digits));
  }
 
  /** Megpermutálja a kapott kétdimenziós mátrix számait a segédtömb alapján
   *
   * @param mx permutálandó mátrix, itt cserélgeti meg a számjegyeket
   * @param digits számjegyek, kevert sorrendben, 1-9 között
   * @return új mátrix
   * */
  protected int[][] getPermuted(int[][] mx, int[] digits) {
    int[][] result = new int[mx.length][mx[0].length];
    for (int j=0; j<mx[0].length; j++) {
        for (int i=0; i<mx.length; i++) {
          result[i][j] = digits[mx[i][j]];
        }
    }
    return result;
  }
 
  /** Adott élből elindul, és keresi a következő invertálandó élet. Javító-alternáló út.
   *
   * @param current kiinduló él
   * @param color1 első szín
   * @param color2 második szín
   * @param wordToCellMap map, melyik vízszintes és függőleges szavak metszetéhez melyik pozíció tartozik
   * az eredeti táblában
   * @param colors aktális színek az összes táblabeli cellához
   * @param hCount vízszintes szavak száma
   * @param vCount függőleges szavak száma
   * @param horizontalToVertical vízszintes vagy függőlegesből jövünk
   * @param visited már meglátogatott cellák, hogy ne mehessünk visszafele
   * @return következő invertálandó él
   * */
  private Position getNextEnRoute(Position current, int color1, int color2, HashMap<Position, Position> wordToCellMap, int[][] colors, int hCount, int vCount, boolean horizontalToVertical, HashMap<Position, Boolean> visited) {
        System.out.println("keresem a kovetkezojet: "+current+" irany: "+horizontalToVertical+", szinek: "+color1+"-"+color2);
        visited.put(current, true);
        if (!horizontalToVertical) {
              int vWord = current.y;
              for (int i=1; i<=hCount; i++) {
                  if (visited.containsKey(new Position(i, vWord))) continue;
                    if (wordToCellMap.containsKey(new Position(i, vWord))) {
                          Position actual = wordToCellMap.get(new Position(i, vWord));
                          int color = colors[actual.x][actual.y];
                          System.out.println("\ttalaltam elet "+new Position(i, vWord)+" -> "+actual+", szine: "+color);
                          if (color==color1) {
                                colors[actual.x][actual.y] = color2;
                                return new Position(i, vWord);
                          }
                    }
              }
        } else {
              int hWord = current.x;
              for (int i=1; i<=vCount; i++) {
                  if (visited.containsKey(new Position(hWord, i))) continue;
                    if (wordToCellMap.containsKey(new Position(hWord, i))) {
                          Position actual = wordToCellMap.get(new Position(hWord, i));
                          int color = colors[actual.x][actual.y];
                          if (color==color2) {
                                colors[actual.x][actual.y] = color1;
                                return new Position(hWord, i);
                          }
                    }
              }
        }
        return null;
  }
 
  /**
   *
   * @param e vízszintes és függőleges szaval sorszáma, ahol épp vagyunk (őket összekötő élen vagyunk most)
   * @param wordToCellMap map, melyik vízszintes és függőleges szavak metszetéhez melyik pozíció tartozik
   * az eredeti táblában
   * @param hCount vízszintes szavak listája
   * @param vCount függőleges szavak listája
   * @param colors aktális színek az összes táblabeli cellához
   * @return egy színpár, egy-egy szabad szín az él két végéről, ilyenek biztosan vannak, hiszen az él
   * színezetlen
   * */
  private int[] getFreeColors(Position e, HashMap<Position, Position> wordToCellMap, int hCount, int vCount, int[][] colors) {
        int hWord = e.x;
        int vWord = e.y;
        int[] result = new int[2];
        result[0] = result[1] = -1;
        boolean[] used = new boolean[10];
        for (int i=1; i<=vCount; i++) {
              if (wordToCellMap.containsKey(new Position(hWord, i))) {
                    Position actual = wordToCellMap.get(new Position(hWord, i));
                    if (colors[actual.x][actual.y]>0) used[colors[actual.x][actual.y]] = true;
              }
        }
        result[0] = getFirst(used);
        used = new boolean[10];
        for (int i=1; i<=hCount; i++) {
              if (wordToCellMap.containsKey(new Position(i, vWord))) {
                    Position actual = wordToCellMap.get(new Position(i, vWord));
                    if (colors[actual.x][actual.y]>0) used[colors[actual.x][actual.y]] = true;
              }
        }
        result[1] = getFirst(used);
        return result;
  }
 
  /** A kapott bool tömbből visszaadja az első false helyét, -1 különben */
  private int getFirst(boolean[] used) {
        for (int i=1; i<used.length; i++) {  //szandekosan 1
              if (!used[i]) {
                    return i;
              }
        }
        return -1;
  }
 
  /** Belső ciklus, fedő élhalmazt ad vissza.
   *
   * @param sorokszama sorok száma
   * @param oszlopokszama oszlopok száma
   * @param hCount vízszintes szavak száma
   * @param vCount függőleges szavak száma
   * @param bwMatrix fekete-fehér eredeti tábla mátrixa
   * @param horizontalWords eredeti tábla mátrixa, beszámozva a vízszintes szavak
   * @param verticalWords eredeti tábla mátrixa, beszámozva a függőleges szavak
   * @param wordToCellMap map, melyik vízszintes és függőleges szavak metszetéhez melyik pozíció tartozik
   * az eredeti táblában
   * @param exclusions az előző körök éleit már nem vizsgáljuk
   * @param run hanyadik kör (max. 9)
   * @return minimális fedő élek
   * */
  private ArrayList<Position> innerCycle(int sorokszama, int oszlopokszama, int hCount, int vCount, int[][] bwMatrix, int[][] horizontalWords, int[][] verticalWords, HashMap<Position, Position> wordToCellMap, HashMap<Position, Boolean> exclusions, int run) {
        ArrayList<Position> result = new ArrayList<Position>();
        Vector<Vector<Integer>> neighbours = new Vector<Vector<Integer>>(150);
       
        /* Felepitem a szomszedossagi listakat, kiveve a mar feldolgozott eleket */
    for (int i=0; i<=hCount; i++) neighbours.add(new Vector<Integer>());
    for (int y=0; y<sorokszama; y++) {
      for (int x=0; x<oszlopokszama; x++) {
            if (bwMatrix[x][y]==1) continue;
            int hWordId = horizontalWords[x][y];
            int vWordId = verticalWords[x][y];
            if (exclusions.containsKey(new Position(hWordId, vWordId))) {
                  continue;
            }
            neighbours.get(hWordId).add(vWordId);
      }
    }
    for (int i=0; i<neighbours.size(); i++) Collections.shuffle(neighbours.get(i));
    System.out.println("vCount: "+vCount+", hCount: "+hCount);
   
    /* minden vizszintes szora lefuttatom a melysegi keresest */
    int[] markedNodes = new int[Math.max(vCount, hCount)+1];
    int[] visitedNodes = new int[Math.max(vCount, hCount)+1];
    boolean[] visited = new boolean[hCount];
    for (int i=1; i<=hCount; i++) {
        int j = nextFree(visited)+1;
        //System.out.println("Doing run j "+j);
          depthFirstSearch(neighbours, markedNodes, visitedNodes, j, j);
    }
   
    /* es visszaadom az eredmenyt */
    int all = 0;
    for (int i=1; i<=vCount; i++) {
          all += (markedNodes[i] != 0)?1:0;
          if (markedNodes[i]!=0) {
                System.out.println("Run "+run+", found edge "+markedNodes[i]+"--->"+i+" : "+wordToCellMap.get(new Position(markedNodes[i], i)));
                result.add(new Position(markedNodes[i], i));
          }
    }
    System.out.println("all: "+all);
    return result;
  }
 
  /** A kapott visited tömbből véletlenszerűen választ egy szabadot. */
  protected int nextFree(boolean[] visited) {
    int rnd = _random.nextInt(visited.length);
    for (int i=0; i<visited.length; i++) {
      int j = (i+rnd)%visited.length;
      if (!visited[j]) {
        visited[j] = true;
        return j;
      }
    }
    return -1;
  }
 
  /** Trükkös depth first search, rekurzív. Inkább lassú, de a BFS implementálása sokkal genyóbb
   *
   * @param neighbours szomszédok
   * @param markedNodes itt azt jelöljük, hogy eljutottunk-e már a jelölt node-okba az mostani körben
   * @param visitedNodes ahol már jártunk
   * @param currentNode aktuális node
   * @param startingNode kiinduló node
   * */
  private boolean depthFirstSearch(Vector<Vector<Integer>> neighbours, int[] markedNodes, int[] visitedNodes, int currentNode, int startingNode) {
        if (visitedNodes[currentNode]!=startingNode) {
        visitedNodes[currentNode] = startingNode;
              for (int j=0; j<neighbours.get(currentNode).size(); j++) {
                    if (markedNodes[neighbours.get(currentNode).get(j)]==0 || depthFirstSearch(neighbours, markedNodes, visitedNodes, markedNodes[neighbours.get(currentNode).get(j)], startingNode)) {
                          markedNodes[neighbours.get(currentNode).get(j)] = currentNode;
                          return true;
                    }
              }
        }
        return false;
  }
 
  /** Statikus debug függvény, a kapott mátrixot iratja ki a képernyőre olvashatóan */
  private static void print(int[][] m, String title) {
        System.out.println("matrix "+title+":");
        for (int j=0; j<m[0].length; j++) {
              for (int i=0; i<m.length; i++) {
                  System.out.print("\t"+m[i][j]);
              }
              System.out.println("");
        }
  }
}
TOP

Related Classes of kakuro.server.tablefactory.BipartiteTableFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.