Package kakuro.server.tablefactory

Source Code of kakuro.server.tablefactory.SkeletonGenerator$MySkeletonImpl

package kakuro.server.tablefactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import kakuro.server.ISkeletonTable;
import kakuro.table.ITable;
import kakuro.table.Position;

/** Skeleton generátor osztály. Alapvetően véletlenszerűen szórja ki a fekete mezőket,
* kicsit nagyobb valószínűséggel a tábla szélére, és feketéket egymás mellé. Ügyel arra, hogy
* 9-nél hosszabb szavak ne maradjanak. */
class SkeletonGenerator {
  /** Random generátor, az aktális idő a seed. */
  private Random _random = new Random(System.currentTimeMillis());
 
  SkeletonGenerator() {
  }
 
  /** Az adott nehézségi szinthez generál egy üres táblavázat. */
  public ISkeletonTable generateSkeleton(int skillLevel) {
    int x;
    int y;
    switch (skillLevel) {
      case ITable.DIFFICULTY_EASY:
        x = y = 5;
        break;
      case ITable.DIFFICULTY_MEDIUM:
        x = 8;
        y = 10;
        break;
      case 3:
        x = 42;
        y = 33;
        skillLevel = ITable.DIFFICULTY_HARD;
        break;
      default:
      case ITable.DIFFICULTY_HARD:
        x = 14;
        y = 16;
        break;
    }
   
    List<Position> result = makeSkeleton(x-1, y-1);
    translate(result);
    for (int i=0; i<x; i++result.add(new Position(i, 0));
    for (int i=0; i<y; i++result.add(new Position(0, i));
      return new MySkeletonImpl(x, y, result, skillLevel);
  }
 
  /** Eggyel eltolja a kapott pozíciókat jobbra és lefele. Azért van szükség erre, mert így egyszerűbben
   * biztosítható az, hogy a bal és a felső sor mindig fekete maradjon. */
  protected void translate(List<Position> list) {
    for (int i=0; i<list.size(); i++) {
      Position p = list.get(i);
      list.set(i, new Position(p.x+1, p.y+1));
    }
  }
 
  /** Skeleton implementáció. A fekete mezőket tárolja egy map-ben. */
  public class MySkeletonImpl implements ISkeletonTable {
    /** Nehézségi szint */
    private int _diff;
    /** tábla mérete */
    private Position _bounds;
    /** fekete cellák helye */
    private HashMap<Position, Boolean> _blacks = new HashMap<Position, Boolean>();
   
    /** Letárolja a kapott paramétereket, és feltölti a fekete-mapet. */
    public MySkeletonImpl(int x, int y, List<Position> list, int diff) {
      _diff = diff;
      _bounds = new Position(x, y);
      for (int i=0; i<list.size(); i++)
        _blacks.put(list.get(i), true);
    }
   
    /** Oszlopok száma */
    public int getColumnCount() {
      return _bounds.x;
    }
   
    /** Nehézségi szint */
    public int getDifficulty() {
      return _diff;
    }
   
    /** azonosító */
    public int getId() {
      return -1;
    }
   
    /** Sorok száma */
    public int getRowCount() {
      return _bounds.y;
    }
   
    /** Cella típusa, fekete vagy fehér lehet. */
    public int getType(Position position) {
      return (_blacks.containsKey(position)?ISkeletonTable.CELL_BLACK:ISkeletonTable.CELL_WHITE);
    }
  }
 
  /** Statikus teszteléshez segítő függvény, a kapott kétdimenziós int tömböt kiírja a képernyőre. */
  private static void print(String title, int[][] mx) {
    System.out.println("Matrix "+title+":");
    for (int j=0; j<mx[0].length; j++) {
      for (int i=0; i<mx.length; i++) {
        System.out.print("\t"+mx[i][j]);
      }
      System.out.println();
    }
  }
 
  /** Skeleton gyártó főfüggvény, megkapja a tábla kiterjedését, és visszaadja a feketék helyét listában. */ 
  protected List<Position> makeSkeleton(int x, int y) {
    LinkedHashSet<Position> blacks = new LinkedHashSet<Position>();
    int[][] weights = fillWeights(blacks, x, y);
    int counter = 0;
    Position longPos = null;
    int rollbackCounter = 0;
    int[] rollbackStat = new int[3];
    while ((counter<((x*y)/10)) || (longPos = checkLongs(weights))!=null) {
          if (rollbackCounter>11) {
                //System.out.println("Rollback3333");
                rollbackCounter = 0;
                rollbackStat[0]++;
                Iterator<Position> it = blacks.iterator();
                Position p1 = null;
                Position p2 = null;
                while (it.hasNext()) {
                      p1 = p2;
                      p2 = it.next();
                }
                blacks.remove(p1);
                blacks.remove(p2);
                weights = fillWeights(blacks, x, y);
                continue;
          }
      if (counter>((x*y)/10)) {
        blacks.add(longPos);
        weights = fillWeights(blacks, x, y);
        if (!checkTree(weights, blacks)) {
          //System.out.println("rollback1!");
          rollbackCounter++;
          rollbackStat[1]++;
          blacks.remove(longPos);
          weights = fillWeights(blacks, x, y);
        }
        continue;
      }
      int rnd = _random.nextInt(getSum(weights));
      Position nextBlack = getPos(weights, rnd);
      blacks.add(nextBlack);
      weights = fillWeights(blacks, x, y);
      if (!checkTree(weights, blacks)) {
        //System.out.println("rollback2!");
        rollbackCounter++;
        rollbackStat[2]++;
        blacks.remove(nextBlack);
        weights = fillWeights(blacks, x, y);
        continue;
      }
      counter++;
    }
    int blackCount = blacks.size();
    fillOneways(blacks, x, y);
    System.out.println("Rollbacks: "+rollbackStat[0]+" for timeout, "+(rollbackStat[1]+rollbackStat[2])+" for connectness. "+(blacks.size()-blackCount)+" blacks added for oneway fields.");
   
    ArrayList<Position> result = new ArrayList<Position>();
    result.addAll(blacks);
    return result;
  }
 
  /** Addig megy a kapott kétdimenziós tömbben, és adja össze a cellákban található számokat, amíg el
   * nem ér a kért összegig. Ekkor visszaadja az elért pozíciót.
   *
   * @param mx kétdimenziós tömb súlyokkal
   * @param sum elérni kívánt összeg
   * @return pozíció, ahol elértük az összeget
   * */
  protected Position getPos(int[][] mx, int sum) {
    for (int j=0; j<mx[0].length; j++) {
      for (int i=0; i<mx.length; i++) {
        if (mx[i][j]==0) continue;
        sum -= mx[i][j];
        if (sum<1) return new Position(i, j);
      }
    }
    return null;
  }
 
  /** Összegzi a kapott kétdimenziós tömbben a súlyokat. */
  protected int getSum(int[][] mx) {
    int result = 0;
    for (int j=0; j<mx[0].length; j++)
      for (int i=0; i<mx.length; i++)
        result += mx[i][j];
    return result;
  }
 
  /** lekéri, hogy az adott pozíció körül hány darab fekete mező van. átlósan is számolja őket!
   *
   * @param blacks fekete cellák
   * @param x nézett x pozíció
   * @param y nézett y pozíció
   * @param mx mátrix szélessége
   * @param my mátrix magassága
   * @return fekete szomszédok száma
   * */
  protected int getBlackNeighbourCount(HashSet<Position> blacks, int x, int y, int mx, int my) {
        int result = 0;
        for (int i=-1; i<2; i++) {
              for (int j=-1; j<2; j++) {
                    if (x+i<0 || y+j<0 || x+i>=mx || y+j>=my) continue;
                    result += (blacks.contains(new Position(x+i, y+j))?1:0);
              }
        }
        return result;
  }
 
  /** Visszaad egy súlyokkal feltöltött mátrixot. A feketék helyét és a tábla méretét kapja meg,
   * fekete helye nulla lesz, a fehér mezőké pedig egy szám, hogy mekkora valószínűséggel essen oda a
   * következő fekete mező.
   * <p>
   * Tábla széle nagyobb esélyt jelent, és a szomszédos feketék is megnöveli egy fehér mező esélyét.
   *
   * @param blacks fekete mezők
   * @param x mátrix szélessége
   * @param y mátrix magassága
   * @return mátrix súlyokkal
   * */
  protected int[][] fillWeights(HashSet<Position> blacks, int x, int y) {
    int[][] weights = new int[x][y];
    for (int j=0; j<y; j++) {
      for (int i=0; i<x; i++) {
        if (i==0 || j==0 || i==x-1 || j==y-1) weights[i][j] += 100;
        weights[i][j] += 25;
        int n = getBlackNeighbourCount(blacks, i, j, x, y);
        weights[i][j] += 10*n*n;
      }
    }
    Iterator<Position> it = blacks.iterator();
    it = blacks.iterator();
    while (it.hasNext()) {
      Position p = it.next();
      weights[p.x][p.y] = 0;
    }
    return weights;
  }
 
  /** Túl hosszú szavak kiszűrése. Ha van túl hosszú szó, akkor visszaadja az első szó kilencedik pozícióját.
   *
   * @param mx kétdimenziós int tömb a súlyokkal
   * @return első olyan cella helye, ami túl hosszú szóban található, vagy null, ha nincs ilyen
   * */
  protected Position checkLongs(int[][] mx) {
    for (int j=0; j<mx[0].length; j++) {
      for (int i=0, w=0; i<mx.length; i++) {
        if (mx[i][j]!=0) w++;
        else w = 0;
        if (w>9) {
              int[][] row = new int[9][1];
              for (int l=0; l<9; l++) row[l][0] = mx[i-9+l][j];
              int sum = getSum(row);
              int rnd = _random.nextInt(sum);
              Position p = getPos(row, rnd);
              //System.out.println("RandomH: "+p);
              return new Position(i-9+p.x, j);
        }
      }
    }
    for (int i=0; i<mx.length; i++) {
      for (int j=0, w=0; j<mx[0].length; j++) {
        if (mx[i][j]!=0) w++;
        else w = 0;
        if (w>9) {
              //return new Position(i, j-_random.nextInt(9));
              int[][] row = new int[9][1];
              for (int l=0; l<9; l++) row[l][0] = mx[i][j-9+l];
              int sum = getSum(row);
              int rnd = _random.nextInt(sum);
              Position p = getPos(row, rnd);
              //System.out.println("RandomV: "+p);
              return new Position(i, j-9+p.x);
        }
      }
    }
    return null;
  }
 
  /** Összefüggőségi vizsgálat.
   *
   * @param mx súlyok kétdimenziós mátrixa
   * @param blacks fekete mezők
   * @return true ha összefüggő a tábla, false egyébként
   * */
  protected boolean checkTree(int[][] mx, HashSet<Position> blacks) {
        int total = mx[0].length*mx.length;
        total -= blacks.size();
        HashSet<Position> marked = new HashSet<Position>();
    for (int j=0; j<mx[0].length; j++) {
      for (int i=0; i<mx.length; i++) {
        if (blacks.contains(new Position(i, j))) continue;
        int visited = dfs(mx, marked, i, j);
        if (visited!=total) return false;
        return true;
      }
    }
    return false;
  }
 
  /** Mélységi keresés a fehér mezőkre
   *
   * @param mx súlyok mátrixa
   * @param marked már meglátogatott mezők
   * @param x kiinduló x pozíció
   * @param y kiinduló y pozíció
   * @return meglátogatt mezők száma, beleértve saját magát is
   * */
  protected int dfs(int[][] mx, HashSet<Position> marked, int x, int y) {
        Position p = new Position(x, y);
        if (marked.contains(p)) return 0;
        if (x<0 || y<0 || x>=mx.length || y>=mx[0].length) return 0;
        if (mx[x][y]==0) return 0;
        int result = 1;
        marked.add(p);
        result += dfs(mx, marked, x+1, y);
        result += dfs(mx, marked, x-1, y);
        result += dfs(mx, marked, x, y+1);
        result += dfs(mx, marked, x, y-1);
        return result;
  }
 
  /** Zsák fehérek kitöltése feketével. Zsák fehér: olyan fehér, akinek csak 1 darab fehér szomszédja van
   * (átlós nem számít). Az összeset befeketíti!
   *
   * @param blacks fekete mezők
   * @param x kiinduló x
   * @param y kiinduló y koordináta
   * */
  protected void fillOneways(LinkedHashSet<Position> blacks, int x, int y) {
        Position oneway = null;
        while ((oneway = getFirstOneway(blacks, x, y))!=null) blacks.add(oneway);
  }
 
  /** Megkeresi az első zsákutca fehér mezőt, ha nincs ilyen, akkor null-t ad vissza.
   *
   * @param blacks feketék
   * @param x kiinduló x
   * @param y kiinduló y koordináta
   * @return első zsákutca fehér mező helye, vagy null, ha nincs ilyen
   * */
  protected Position getFirstOneway(LinkedHashSet<Position> blacks, int x, int y) {
        int c = 0;
        for (int j=0; j<y; j++) {
              for (int i=0; i<x; i++) {
                    if (blacks.contains(new Position(i, j))) continue;
                    c = 0;
                    if (blacks.contains(new Position(i, j+1)))  c++;
                    if (blacks.contains(new Position(i, j-1)))  c++;
                    if (blacks.contains(new Position(i+1, j)))  c++;
                    if (blacks.contains(new Position(i-1, j)))  c++;
                    if (c==3) return new Position(i, j);
                    if ((i==0 || j==0 || i==x-1 || j==y-1) && c==2) return new Position(i, j);
                    if (c==1 && (i+j==0 || i+j==x+y-2 || (i==0 && j==y-1) || (i==x-1 && j==0))) return new Position(i, j);
              }
        }
        return null;
  }
}
TOP

Related Classes of kakuro.server.tablefactory.SkeletonGenerator$MySkeletonImpl

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.