เชœเชพเชตเชพเชฎเชพเช‚ "เชŸเซ‡เช—" - เชธเช‚เชชเซ‚เชฐเซเชฃ เชฐเชฎเชค เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เชตเชฟเช•เชธเชพเชตเชตเซ€

เชœเชพเชตเชพเชฎเชพเช‚ "เชŸเซ‡เช—" - เชธเช‚เชชเซ‚เชฐเซเชฃ เชฐเชฎเชค เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เชตเชฟเช•เชธเชพเชตเชตเซ€

"เชชเช‚เชฆเชฐ" เช…เชฅเชตเชพ "เชชเช‚เชฆเชฐ" เชเช• เชธเชฐเชณ เชคเชฐเซเช•เชถเชพเชธเซเชคเซเชฐเชจเซ€ เชฐเชฎเชคเชจเซเช‚ เช‰เชคเซเชคเชฎ เช‰เชฆเชพเชนเชฐเชฃ เช›เซ‡ เชœเซ‡ เชธเชฎเช—เซเชฐ เชตเชฟเชถเซเชตเชฎเชพเช‚ เชฒเซ‹เช•เชชเซเชฐเชฟเชฏ เช›เซ‡. เชชเชเชฒ เช‰เช•เซ‡เชฒเชตเชพ เชฎเชพเชŸเซ‡, เชคเชฎเชพเชฐเซ‡ เชจเชพเชจเชพเชฅเซ€ เชฎเซ‹เชŸเชพ เชธเซเชงเซ€เชจเชพ เชšเซ‹เชฐเชธเชจเซ‡ เชธเช‚เช–เซเชฏเชพเช“ เชธเชพเชฅเซ‡ เช—เซ‹เช เชตเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡. เชคเซ‡ เชธเชฐเชณ เชจเชฅเซ€, เชชเชฐเช‚เชคเซ เชคเซ‡ เชฐเชธเชชเซเชฐเชฆ เช›เซ‡.

เช†เชœเชจเชพ เชŸเซเชฏเซเชŸเซ‹เชฐเซ€เชฏเชฒเชฎเชพเช‚ เช…เชฎเซ‡ เชคเชฎเชจเซ‡ เชฌเชคเชพเชตเซ€เชถเซเช‚ เช•เซ‡ Eclipse เชธเชพเชฅเซ‡ Java 8 เชฎเชพเช‚ Fifteen เชจเซ‡ เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เชกเซ‡เชตเชฒเชช เช•เชฐเชตเซเช‚. UI เชตเชฟเช•เชธเชพเชตเชตเชพ เชฎเชพเชŸเซ‡ เช…เชฎเซ‡ Swing API เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชถเซเช‚.

เช…เชฎเซ‡ เชฏเชพเชฆ เช•เชฐเชพเชตเซ€เช เช›เซ€เช: Habrเชจเชพ เชคเชฎเชพเชฎ เชตเชพเชšเช•เซ‹ เชฎเชพเชŸเซ‡ - Habr เชชเซเชฐเซ‹เชฎเซ‹ เช•เซ‹เชกเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เช•เซ‹เชˆเชชเชฃ เชธเซเช•เชฟเชฒเชฌเซ‹เช•เซเชธ เช•เซ‹เชฐเซเชธเชฎเชพเช‚ เชจเซ‹เช‚เชงเชฃเซ€ เช•เชฐเชคเซ€ เชตเช–เชคเซ‡ 10 เชฐเซ‚เชฌเชฒ เชกเชฟเชธเซเช•เชพเช‰เชจเซเชŸ.

เชธเซเช•เชฟเชฒเชฌเซ‹เช•เซเชธ เชญเชฒเชพเชฎเชฃ เช•เชฐเซ‡ เช›เซ‡: เชถเซˆเช•เซเชทเชฃเชฟเช• เช“เชจเชฒเชพเชˆเชจ เช•เซ‹เชฐเซเชธ "เชชเซเชฐเซ‹เชซเซ‡เชถเชจ เชœเชพเชตเชพ เชกเซ‡เชตเชฒเชชเชฐ".

เชฐเชฎเชค เชกเชฟเชเชพเช‡เชจ

เช† เชคเชฌเช•เซเช•เซ‡ เชคเชฎเชพเชฐเซ‡ เช—เซเชฃเชงเชฐเซเชฎเซ‹เชจเซ‡ เชตเซเชฏเชพเช–เซเชฏเชพเชฏเชฟเชค เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡:

  • เช•เชฆ - เชฐเชฎเชคเชจเชพ เช•เซเชทเซ‡เชคเซเชฐเชจเซเช‚ เช•เชฆ;
  • nbTiles โ€” เช•เซเชทเซ‡เชคเซเชฐเชฎเชพเช‚ เชŸเซ…เช—เซเชธเชจเซ€ เชธเช‚เช–เซเชฏเชพ. nbTiles = เช•เชฆ*เช•เชฆ - 1;
  • เชŸเชพเช‡เชฒเซเชธ เช เชเช• เชŸเซ…เช— เช›เซ‡ เชœเซ‡ เชชเซ‚เชฐเซเชฃเชพเช‚เช•เซ‹เชจเซ€ เชเช•-เชชเชฐเชฟเชฎเชพเชฃเซ€เชฏ เชถเซเชฐเซ‡เชฃเซ€ เช›เซ‡. เชฆเชฐเซ‡เช• เชŸเซ‡เช— เชถเซเชฐเซ‡เชฃเซ€ [0, nbTiles] เชฎเชพเช‚ เช…เชจเชจเซเชฏ เชฎเซ‚เชฒเซเชฏ เชชเซเชฐเชพเชชเซเชค เช•เชฐเชถเซ‡. เชถเซ‚เชจเซเชฏ เช–เชพเชฒเซ€ เชšเซ‹เชฐเชธ เชธเซ‚เชšเชตเซ‡ เช›เซ‡;
  • blankPos โ€” เช–เชพเชฒเซ€ เชšเซ‹เชฐเชธเชจเซ€ เชธเซเชฅเชฟเชคเชฟ.

เชฐเชฎเชค เชคเชฐเซเช•

เช…เชฎเชพเชฐเซ‡ เชจเชตเซ€ เชฐเชฎเชคเชจเซ€ เชธเซเชฅเชฟเชคเชฟ เชถเชฐเซ‚ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เช‰เชชเชฏเซ‹เช—เชฎเชพเช‚ เชฒเซ‡เชตเชพเชคเซ€ เชฐเซ€เชธเซ‡เชŸ เชชเชฆเซเชงเชคเชฟเชจเซ‡ เชตเซเชฏเชพเช–เซเชฏเชพเชฏเชฟเชค เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡. เช† เชฐเซ€เชคเซ‡ เช†เชชเชฃเซ‡ เชŸเซ…เช—เซเชธ เชเชฐเซ‡เชจเชพ เชฆเชฐเซ‡เช• เช˜เชŸเช• เชฎเชพเชŸเซ‡ เชฎเซ‚เชฒเซเชฏ เชธเซ‡เชŸ เช•เชฐเซ€เช เช›เซ€เช. เชธเชพเชฐเซเช‚, เชชเช›เซ€ เช†เชชเชฃเซ‡ เชเชฐเซ‡เชจเซ€ เช›เซ‡เชฒเซเชฒเซ€ เชธเซเชฅเชฟเชคเชฟเชฎเชพเช‚ เช–เชพเชฒเซ€ เชชเซ‹เชธ เชฎเซ‚เช•เซ€เช เช›เซ€เช.

เชŸเซ…เช—เซเชธเชจเซ€ เชเชฐเซ‡เชจเซ‡ เชถเชซเชฒ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เช…เชฎเชจเซ‡ เชถเชซเชฒ เชชเชฆเซเชงเชคเชฟเชจเซ€ เชชเชฃ เชœเชฐเซ‚เชฐ เช›เซ‡. เช…เชฎเซ‡ เชถเชซเชฒเชฟเช‚เช— เชชเซเชฐเช•เซเชฐเชฟเชฏเชพเชฎเชพเช‚ เช–เชพเชฒเซ€ เชŸเซ…เช—เชจเซ‡ เชคเซ‡ เชœ เชธเซเชฅเชฟเชคเชฟเชฎเชพเช‚ เช›เซ‹เชกเชตเชพ เชฎเชพเชŸเซ‡ เชถเชพเชฎเซ‡เชฒ เช•เชฐเชคเชพ เชจเชฅเซ€.

เช•เชพเชฐเชฃ เช•เซ‡ เชชเชเชฒเชจเซ€ เชธเช‚เชญเชตเชฟเชค เชชเซเชฐเชพเชฐเช‚เชญเชฟเช• เชธเซเชฅเชฟเชคเชฟเช“เชฎเชพเช‚เชฅเซ€ เชฎเชพเชคเซเชฐ เช…เชกเชงเชพเชฎเชพเช‚ เช‰เช•เซ‡เชฒ เช›เซ‡, เชคเชฎเชพเชฐเซ‡ เชตเชฐเซเชคเชฎเชพเชจ เชฒเซ‡เช†เช‰เชŸ เชชเชฃ เช‰เช•เซ‡เชฒเซ€ เชถเช•เชพเชฏ เชคเซ‡เชตเซเช‚ เช›เซ‡ เชคเซ‡เชจเซ€ เช–เชพเชคเชฐเซ€ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชชเชฐเชฟเชฃเชพเชฎเซ€ เชถเชซเชฒ เชชเชฐเชฟเชฃเชพเชฎ เชคเชชเชพเชธเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡. เช† เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡, เช…เชฎเซ‡ isSolvable เชชเชฆเซเชงเชคเชฟ เชตเซเชฏเชพเช–เซเชฏเชพเชฏเชฟเชค เช•เชฐเซ€เช เช›เซ€เช.

เชœเซ‹ เช•เซ‹เชˆ เชšเซ‹เช•เซเช•เชธ เชŸเซ…เช— เชŠเช‚เชšเชพ เชฎเซ‚เชฒเซเชฏเชตเชพเชณเชพ เชŸเซ…เช—เชจเซ€ เช†เช—เชณ เชนเซ‹เชฏ, เชคเซ‹ เชคเซ‡เชจเซ‡ เชตเซเชฏเซเชคเซเช•เซเชฐเชฎ เช—เชฃเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡. เชœเซเชฏเชพเชฐเซ‡ เช–เชพเชฒเซ€ เชœเช—เซเชฏเชพ เชœเช—เซเชฏเชพเช เชนเซ‹เชฏ, เชคเซเชฏเชพเชฐเซ‡ เชชเชเชฒ เช‰เช•เซ‡เชฒเซ€ เชถเช•เชพเชฏ เชคเซ‡ เชฎเชพเชŸเซ‡ เชตเซเชฏเซเชคเซเช•เซเชฐเชฎเซ‹เชจเซ€ เชธเช‚เช–เซเชฏเชพ เชธเชฎเชพเชจ เชนเซ‹เชตเซ€ เชœเซ‹เชˆเช. เชคเซ‡เชฅเซ€ เช†เชชเชฃเซ‡ เชตเซเชฏเซเชคเซเช•เซเชฐเชฎเซ‹เชจเซ€ เชธเช‚เช–เซเชฏเชพ เช—เชฃเซ€เช เช›เซ€เช เช…เชจเซ‡ เชœเซ‹ เชธเช‚เช–เซเชฏเชพ เชธเชฎ เชนเซ‹เชฏ เชคเซ‹ เชธเชพเชšเชพ เชชเชฐเชค เช•เชฐเซ€เช เช›เซ€เช.

เชชเช›เซ€ เช…เชฎเชพเชฐเซ€ เช—เซ‡เชฎ เช“เชซ เชซเชฟเชซเซเชŸเซ€เชจ เชฒเซ‡เช†เช‰เชŸ เชธเซ‹เชฒเซเชต เชฅเชพเชฏ เช›เซ‡ เช•เซ‡ เช•เซ‡เชฎ เชคเซ‡ เชคเชชเชพเชธเชตเชพ เชฎเชพเชŸเซ‡ isSolved เชชเชฆเซเชงเชคเชฟเชจเซ‡ เชตเซเชฏเชพเช–เซเชฏเชพเชฏเชฟเชค เช•เชฐเชตเซ€ เชฎเชนเชคเซเชตเชชเซ‚เชฐเซเชฃ เช›เซ‡. เชชเชนเซ‡เชฒเชพ เช†เชชเชฃเซ‡ เชœเซ‹เชˆเช เช•เซ‡ เช–เชพเชฒเซ€ เชœเช—เซเชฏเชพ เช•เซเชฏเชพเช‚ เช›เซ‡. เชœเซ‹ เชชเซเชฐเชพเชฐเช‚เชญเชฟเช• เชธเซเชฅเชฟเชคเชฟเชฎเชพเช‚, เชคเซ‹ เชตเชฐเซเชคเชฎเชพเชจ เชธเช‚เชฐเซ‡เช–เชฃ เชจเชตเซเช‚ เช›เซ‡, เช…เช—เชพเช‰ เชจเช•เซเช•เซ€ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเซเช‚ เชจเชฅเซ€. เชชเช›เซ€ เช…เชฎเซ‡ เชŸเชพเช‡เชฒเซเชธ เชฆเซเชตเชพเชฐเชพ เชตเชฟเชชเชฐเซ€เชค เช•เซเชฐเชฎเชฎเชพเช‚ เชชเซเชจเชฐเชพเชตเชฐเซเชคเชฟเชค เช•เชฐเซ€เช เช›เซ€เช, เช…เชจเซ‡ เชœเซ‹ เชŸเซ‡เช—เชจเซ€ เช•เชฟเช‚เชฎเชค เช…เชจเซเชฐเซ‚เชช เช…เชจเซเช•เซเชฐเชฎเชฃเชฟเช•เชพ +1 เช•เชฐเชคเชพ เช…เชฒเช— เชนเซ‹เชฏ, เชคเซ‹ เช…เชฎเซ‡ เช–เซ‹เชŸเชพ เชชเชฐเชค เช•เชฐเซ€เช เช›เซ€เช. เชจเชนเชฟเช‚เชคเชฐ, เชชเชฆเซเชงเชคเชฟเชจเชพ เช…เช‚เชคเซ‡ เชคเซ‡ เชธเชพเชšเซเช‚ เชชเชฐเชค เช•เชฐเชตเชพเชจเซ‹ เชธเชฎเชฏ เช›เซ‡ เช•เชพเชฐเชฃ เช•เซ‡ เชชเชเชฒ เชชเชนเซ‡เชฒเซ‡เชฅเซ€ เชœ เช‰เช•เซ‡เชฒเชพเชˆ เช—เชˆ เช›เซ‡.

เชฌเซ€เชœเซ€ เชชเชฆเซเชงเชคเชฟ เช•เซ‡ เชœเซ‡เชจเซ‡ เชตเซเชฏเชพเช–เซเชฏเชพเชฏเชฟเชค เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡ เชคเซ‡ เช›เซ‡ newGame. เชฐเชฎเชคเชจเซ‹ เชจเชตเซ‹ เชฆเชพเช–เชฒเซ‹ เชฌเชจเชพเชตเชตเซ‹ เชœเชฐเซ‚เชฐเซ€ เช›เซ‡. เช† เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡, เช…เชฎเซ‡ เชชเซเชฒเซ‡เช‡เช‚เช— เชซเชฟเชฒเซเชก เชฐเซ€เชธเซ‡เชŸ เช•เชฐเซ€เช เช›เซ€เช, เชชเช›เซ€ เชคเซ‡เชจเซ‡ เชถเชซเชฒ เช•เชฐเซ€เช เช›เซ€เช เช…เชจเซ‡ เชœเซเชฏเชพเช‚ เชธเซเชงเซ€ เชชเซเชฒเซ‡เช‡เช‚เช— เชชเซ‹เชเชฟเชถเชจ เชฐเชฟเชเซ‹เชฒเซเชตเซ‡เชฌเชฒ เชจ เชฅเชพเชฏ เชคเซเชฏเชพเช‚ เชธเซเชงเซ€ เชšเชพเชฒเซ เชฐเชพเช–เซ‹.

เช…เชนเซ€เช‚ เชŸเซ‡เช—เชจเชพ เชฎเซเช–เซเชฏ เชคเชฐเซเช• เชธเชพเชฅเซ‡เชจเซ‹ เชเช• เช‰เชฆเชพเชนเชฐเชฃ เช•เซ‹เชก เช›เซ‡:

private void newGame() {
  do {
    reset(); // reset in initial state
    shuffle(); // shuffle
  } while(!isSolvable()); // make it until grid be solvable
 
  gameOver = false;
}
 
private void reset() {
  for (int i = 0; i < tiles.length; i++) {
    tiles[i] = (i + 1) % tiles.length;
  }
 
  // we set blank cell at the last
  blankPos = tiles.length - 1;
}
 
private void shuffle() {
  // don't include the blank tile in the shuffle, leave in the solved position
  int n = nbTiles;
 
  while (n > 1) {
    int r = RANDOM.nextInt(n--);
    int tmp = tiles[r];
    tiles[r] = tiles[n];
    tiles[n] = tmp;
  }
}
 
// Only half permutations of the puzzle are solvable/
// Whenever a tile is preceded by a tile with higher value it counts
// as an inversion. In our case, with the blank tile in the solved position,
// the number of inversions must be even for the puzzle to be solvable
private boolean isSolvable() {
  int countInversions = 0;
 
  for (int i = 0; i < nbTiles; i++) {
    for (int j = 0; j < i; j++) {
      if (tiles[j] > tiles[i])
        countInversions++;
    }
  }
 
  return countInversions % 2 == 0;
}
 
private boolean isSolved() {
  if (tiles[tiles.length - 1] != 0) // if blank tile is not in the solved position ==> not solved
    return false;
 
  for (int i = nbTiles - 1; i >= 0; i--) {
    if (tiles[i] != i + 1)
      return false;
  }
 
  return true;
}

เช›เซ‡เชฒเซเชฒเซ‡, เชคเชฎเชพเชฐเซ‡ เชเชฐเซ‡เชฎเชพเช‚ เชŸเซ…เช—เซเชธเชจเซ€ เชนเชฟเชฒเชšเชพเชฒเชจเซ‡ เชชเซเชฐเซ‹เช—เซเชฐเชพเชฎ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡. เช•เชฐเซเชธเชฐเชจเซ€ เชนเชฟเชฒเชšเชพเชฒเชจเซ‡ เชชเซเชฐเชคเชฟเชธเชพเชฆ เช†เชชเชตเชพ เชฎเชพเชŸเซ‡ เช† เช•เซ‹เชกเชจเซ‡ เชชเชพเช›เชณเชฅเซ€ เช•เซ‰เชฒเชฌเซ‡เช• เชฆเซเชตเชพเชฐเชพ เช•เซ‰เชฒ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡. เช…เชฎเชพเชฐเซ€ เชฐเชฎเชค เชเช• เชœ เชธเชฎเชฏเซ‡ เชฌเชนเซเชตเชฟเชง เชŸเชพเช‡เชฒ เชนเชฒเชจเชšเชฒเชจเชจเซ‡ เชธเชชเซ‹เชฐเซเชŸ เช•เชฐเชถเซ‡. เช†เชฎ, เช…เชฎเซ‡ เชธเซเช•เซเชฐเซ€เชจ เชชเชฐ เชฆเชฌเชพเชฏเซ‡เชฒเซ€ เชธเซเชฅเชฟเชคเชฟเชจเซ‡ เชŸเซ‡เช—เชฎเชพเช‚ เชฐเซ‚เชชเชพเช‚เชคเชฐเชฟเชค เช•เชฐเซเชฏเชพ เชชเช›เซ€, เช…เชฎเซ‡ เช–เชพเชฒเซ€ เชŸเซ‡เช—เชจเซ€ เชธเซเชฅเชฟเชคเชฟ เชฎเซ‡เชณเชตเซ€เช เช›เซ€เช เช…เชจเซ‡ เชเช• เชœ เชธเชฎเชฏเซ‡ เชคเซ‡เชจเซ€ เช˜เชฃเซ€ เชนเชฟเชฒเชšเชพเชฒเชจเซ‡ เชŸเซ‡เช•เซ‹ เช†เชชเชตเชพ เชฎเชพเชŸเซ‡ เชนเชฒเชจเชšเชฒเชจเชจเซ€ เชฆเชฟเชถเชพ เชถเซ‹เชงเซ€เช เช›เซ€เช.

เช…เชนเซ€เช‚ เชเช• เช•เซ‹เชก เช‰เชฆเชพเชนเชฐเชฃ เช›เซ‡:

// get position of the click
int ex = e.getX() - margin;
int ey = e.getY() - margin;
 
// click in the grid ?
if (ex < 0 || ex > gridSize  || ey < 0  || ey > gridSize)
  return;
 
// get position in the grid
int c1 = ex / tileSize;
int r1 = ey / tileSize;
 
// get position of the blank cell
int c2 = blankPos % size;
int r2 = blankPos / size;
 
// we convert in the 1D coord
int clickPos = r1 * size + c1;
 
int dir = 0;
 
// we search direction for multiple tile moves at once
if (c1 == c2  &&  Math.abs(r1 - r2) > 0)
  dir = (r1 - r2) > 0 ? size : -size;
else if (r1 == r2 && Math.abs(c1 - c2) > 0)
  dir = (c1 - c2) > 0 ? 1 : -1;
 
if (dir != 0) {
  // we move tiles in the direction
  do {
    int newBlankPos = blankPos + dir;
    tiles[blankPos] = tiles[newBlankPos];
    blankPos = newBlankPos;
  } while(blankPos != clickPos);
 
tiles[blankPos] = 0;

เช…เชฎเซ‡ เชธเซเชตเชฟเช‚เช— API เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ UI เชตเชฟเช•เชธเชพเชตเซ€เช เช›เซ€เช

เช‡เชจเซเชŸเชฐเชซเซ‡เชธ เชชเชฐ เช•เชพเชฎ เช•เชฐเชตเชพเชจเซ‹ เช† เชธเชฎเชฏ เช›เซ‡. เชชเชนเซ‡เชฒเชพ เช†เชชเชฃเซ‡ Jpanel เช•เซเชฒเชพเชธ เชฒเชˆเช. เชชเช›เซ€ เช…เชฎเซ‡ เชซเซ€เชฒเซเชก เชชเชฐ เชŸเซ…เช—เซเชธ เชฆเซ‹เชฐเซ€เช เช›เซ€เช - เชฆเชฐเซ‡เช•เชจเชพ เช•เชฆเชจเซ€ เช—เชฃเชคเชฐเซ€ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡, เช…เชฎเซ‡ เช—เซ‡เชฎ เช•เชจเซเชธเซเชŸเซเชฐเช•เซเชŸเชฐ เชชเซ‡เชฐเชพเชฎเซ€เชŸเชฐเชฎเชพเช‚ เช‰เชฒเซเชฒเซ‡เช–เชฟเชค เชกเซ‡เชŸเชพเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชถเซเช‚:

gridSize = (dimโ€Š - โ€Š2 * margin);
tileSize = gridSize / size;

เชฎเชพเชฐเซเชœเชฟเชจ เช เช—เซ‡เชฎ เช•เชจเซเชธเซเชŸเซเชฐเช•เซเชŸเชฐเชฎเชพเช‚ เชธเซ‡เชŸ เช•เชฐเซ‡เชฒ เชชเซ‡เชฐเชพเชฎเซ€เชŸเชฐ เชชเชฃ เช›เซ‡.

เชนเชตเซ‡ เชธเซเช•เซเชฐเซ€เชจ เชชเชฐ เช—เซเชฐเซ€เชก เช…เชจเซ‡ เชธเซเชชเซ‹เชŸเซเชธ เชฆเซ‹เชฐเชตเชพ เชฎเชพเชŸเซ‡ เช†เชชเชฃเซ‡ เชกเซเชฐเซ‹เช—เซเชฐเซ€เชก เชชเชฆเซเชงเชคเชฟเชจเซ‡ เชตเซเชฏเชพเช–เซเชฏเชพเชฏเชฟเชค เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡. เช…เชฎเซ‡ เชŸเซ…เช—เซเชธเชจเซ€ เชเชฐเซ‡เชจเซเช‚ เชตเชฟเชถเซเชฒเซ‡เชทเชฃ เช•เชฐเซ€เช เช›เซ€เช เช…เชจเซ‡ เช•เซ‹เช“เชฐเซเชกเชฟเชจเซ‡เชŸเซเชธเชจเซ‡ เชฏเซเชเชฐ เช‡เชจเซเชŸเชฐเชซเซ‡เชธ เช•เซ‹เช“เชฐเซเชกเชฟเชจเซ‡เชŸเซเชธเชฎเชพเช‚ เช•เชจเซเชตเชฐเซเชŸ เช•เชฐเซ€เช เช›เซ€เช. เชชเช›เซ€ เช•เซ‡เชจเซเชฆเซเชฐเชฎเชพเช‚ เช…เชจเซเชฐเซ‚เชช เชธเช‚เช–เซเชฏเชพ เชธเชพเชฅเซ‡ เชฆเชฐเซ‡เช• เชธเซเชฅเชณ เชฆเซ‹เชฐเซ‹:

private void drawGrid(Graphics2D g) {
  for (int i = 0; i < tiles.length; i++) {
    // we convert 1D coords to 2D coords given the size of the 2D Array
    int r = i / size;
    int c = i % size;
    // we convert in coords on the UI
    int x = margin + c * tileSize;
    int y = margin + r * tileSize;
 
    // check special case for blank tile
    if(tiles[i] == 0) {
      if (gameOver) {
        g.setColor(FOREGROUND_COLOR);
        drawCenteredString(g, "u2713", x, y);
      }
 
      continue;
    }
 
    // for other tiles
    g.setColor(getForeground());
    g.fillRoundRect(x, y, tileSize, tileSize, 25, 25);
    g.setColor(Color.BLACK);
    g.drawRoundRect(x, y, tileSize, tileSize, 25, 25);
    g.setColor(Color.WHITE);
 
    drawCenteredString(g, String.valueOf(tiles[i]), x , y);
  }
}

เช›เซ‡เชฒเซเชฒเซ‡, เชšเชพเชฒเซ‹ เชชเซ‡เช‡เชจเซเชŸ เช•เชฎเซเชชเซ‹เชจเชจเซเชŸ เชชเชฆเซเชงเชคเชฟเชจเซ‡ เช“เชตเชฐเชฐเชพเช‡เชก เช•เชฐเซ€เช, เชœเซ‡ JPane เช•เซเชฒเชพเชธเชฎเชพเช‚เชฅเซ€ เช‰เชคเชฐเซ€ เช†เชตเซ‡ เช›เซ‡. เช…เชฎเซ‡ เชชเช›เซ€ เชกเซเชฐเซ‹เช—เซเชฐเชฟเชก เชชเชฆเซเชงเชคเชฟเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เช เช›เซ€เช, เช…เชจเซ‡ เชชเช›เซ€ เชกเซเชฐเซ‹เชธเซเชŸเชพเชฐเซเชŸเชฎเซ‡เชธเซ‡เชœ เชชเชฆเซเชงเชคเชฟเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เชธเช‚เชฆเซ‡เชถ เชชเซเชฐเชฆเชฐเซเชถเชฟเชค เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เช…เชฎเชจเซ‡ เชฐเชฎเชค เชถเชฐเซ‚ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เช•เซเชฒเชฟเช• เช•เชฐเชตเชพเชจเซเช‚ เช•เชนเซ‡ เช›เซ‡:

private void drawStartMessage(Graphics2D g) {
  if (gameOver) {
    g.setFont(getFont().deriveFont(Font.BOLD, 18));
    g.setColor(FOREGROUND_COLOR);
    String s = "Click to start new game";
    g.drawString(s, (getWidth() - g.getFontMetrics().stringWidth(s)) / 2,
        getHeight() - margin);
  }
}
 
private void drawCenteredString(Graphics2D g, String s, int x, int y) {
  // center string s for the given tile (x,y)
  FontMetrics fm = g.getFontMetrics();
  int asc = fm.getAscent();
  int desc = fm.getDescent();
  g.drawString(s,  x + (tileSize - fm.stringWidth(s)) / 2,
      y + (asc + (tileSize - (asc + desc)) / 2));
}
 
@Override
protected void paintComponent(Graphics g) {
  super.paintComponent(g);
  Graphics2D g2D = (Graphics2D) g;
  g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  drawGrid(g2D);
  drawStartMessage(g2D);
}

UI เชฎเชพเช‚ เชตเชชเชฐเชพเชถเช•เชฐเซเชคเชพเชจเซ€ เช•เซเชฐเชฟเชฏเชพเช“ เชชเชฐ เชชเซเชฐเชคเชฟเช•เซเชฐเชฟเชฏเชพ

เชฐเชฎเชค เชคเซ‡เชจเชพ เช…เชญเซเชฏเชพเชธเช•เซเชฐเชฎเชจเซ‡ เชšเชฒเชพเชตเชตเชพ เชฎเชพเชŸเซ‡, UI เชฎเชพเช‚ เชตเชชเชฐเชพเชถเช•เชฐเซเชคเชพเชจเซ€ เช•เซเชฐเชฟเชฏเชพเช“ เชชเชฐ เชชเซเชฐเช•เซเชฐเชฟเชฏเชพ เช•เชฐเชตเซ€ เชœเชฐเซ‚เชฐเซ€ เช›เซ‡. เช† เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡, Jpanel เชชเชฐ MouseListener เชจเชพ เช…เชฎเชฒเซ€เช•เชฐเชฃ เช…เชจเซ‡ เชธเซเชฅเชณเชพเช‚เชคเชฐ เชฎเชพเชŸเซ‡เชจเซ‹ เช•เซ‹เชก เช‰เชฎเซ‡เชฐเซ‹, เชœเซ‡ เชชเชนเซ‡เชฒเชพเชฅเซ€ เช‰เชชเชฐ เชฌเชคเชพเชตเซ‡เชฒ เช›เซ‡:

addMouseListener(new MouseAdapter() {
  @Override
  public void mousePressed(MouseEvent e) {
    // used to let users to interact on the grid by clicking
    // it's time to implement interaction with users to move tiles to solve the game !
    if (gameOver) {
      newGame();
    } else {
      // get position of the click
      int ex = e.getX() - margin;
      int ey = e.getY() - margin;
 
      // click in the grid ?
      if (ex < 0 || ex > gridSize  || ey < 0  || ey > gridSize)
        return;
 
      // get position in the grid
      int c1 = ex / tileSize;
      int r1 = ey / tileSize;
 
      // get position of the blank cell
      int c2 = blankPos % size;
      int r2 = blankPos / size;
 
      // we convert in the 1D coord
      int clickPos = r1 * size + c1;
 
      int dir = 0;
 
      // we search direction for multiple tile moves at once
      if (c1 == c2  &&  Math.abs(r1 - r2) > 0)
        dir = (r1 - r2) > 0 ? size : -size;
      else if (r1 == r2 && Math.abs(c1 - c2) > 0)
        dir = (c1 - c2) > 0 ? 1 : -1;
 
      if (dir != 0) {
        // we move tiles in the direction
        do {
          int newBlankPos = blankPos + dir;
          tiles[blankPos] = tiles[newBlankPos];
          blankPos = newBlankPos;
        } while(blankPos != clickPos);
 
        tiles[blankPos] = 0;
      }
 
      // we check if game is solved
      gameOver = isSolved();
    }
 
    // we repaint panel
    repaint();
  }
});

เช…เชฎเซ‡ เช•เซ‹เชกเชจเซ‡ GameOfFifteen เชตเชฐเซเช—เชจเชพ เช•เชจเซเชธเซเชŸเซเชฐเช•เซเชŸเชฐเชฎเชพเช‚ เชฎเซ‚เช•เซ€เช เช›เซ€เช. เช–เซ‚เชฌ เชœ เช…เช‚เชคเซ‡, เช…เชฎเซ‡ เชจเชตเซ€ เชฐเชฎเชค เชถเชฐเซ‚ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ newGame เชชเชฆเซเชงเชคเชฟ เช•เชนเซ€เช เช›เซ€เช.

เชธเช‚เชชเซ‚เชฐเซเชฃ เชฐเชฎเชค เช•เซ‹เชก

เชฐเชฎเชคเชจเซ‡ เช•เซเชฐเชฟเชฏเชพเชฎเชพเช‚ เชœเซ‹เชคเชพ เชชเชนเซ‡เชฒเชพเชจเซเช‚ เช›เซ‡เชฒเซเชฒเซเช‚ เชชเช—เชฒเซเช‚ เช เชคเชฎเชพเชฎ เช•เซ‹เชก เช˜เชŸเช•เซ‹เชจเซ‡ เชเช•เชธเชพเชฅเซ‡ เชฎเซ‚เช•เชตเชพเชจเซเช‚ เช›เซ‡. เชถเซเช‚ เชฅเชพเชฏ เช›เซ‡ เชคเซ‡ เช…เชนเซ€เช‚ เช›เซ‡:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
 
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
 
// We are going to create a Game of 15 Puzzle with Java 8 and Swing
// If you have some questions, feel free to read comments ;)
public class GameOfFifteen extends JPanel { // our grid will be drawn in a dedicated Panel
 
  // Size of our Game of Fifteen instance
  private int size;
  // Number of tiles
  private int nbTiles;
  // Grid UI Dimension
  private int dimension;
  // Foreground Color
  private static final Color FOREGROUND_COLOR = new Color(239, 83, 80); // we use arbitrary color
  // Random object to shuffle tiles
  private static final Random RANDOM = new Random();
  // Storing the tiles in a 1D Array of integers
  private int[] tiles;
  // Size of tile on UI
  private int tileSize;
  // Position of the blank tile
  private int blankPos;
  // Margin for the grid on the frame
  private int margin;
  // Grid UI Size
  private int gridSize;
  private boolean gameOver; // true if game over, false otherwise
 
  public GameOfFifteen(int size, int dim, int mar) {
    this.size = size;
    dimension = dim;
    margin = mar;
    
    // init tiles
    nbTiles = size * size - 1; // -1 because we don't count blank tile
    tiles = new int[size * size];
    
    // calculate grid size and tile size
    gridSize = (dim - 2 * margin);
    tileSize = gridSize / size;
    
    setPreferredSize(new Dimension(dimension, dimension + margin));
    setBackground(Color.WHITE);
    setForeground(FOREGROUND_COLOR);
    setFont(new Font("SansSerif", Font.BOLD, 60));
    
    gameOver = true;
    
    addMouseListener(new MouseAdapter() {
      @Override
      public void mousePressed(MouseEvent e) {
        // used to let users to interact on the grid by clicking
        // it's time to implement interaction with users to move tiles to solve the game !
        if (gameOver) {
          newGame();
        } else {
          // get position of the click
          int ex = e.getX() - margin;
          int ey = e.getY() - margin;
          
          // click in the grid ?
          if (ex < 0 || ex > gridSize  || ey < 0  || ey > gridSize)
            return;
          
          // get position in the grid
          int c1 = ex / tileSize;
          int r1 = ey / tileSize;
          
          // get position of the blank cell
          int c2 = blankPos % size;
          int r2 = blankPos / size;
          
          // we convert in the 1D coord
          int clickPos = r1 * size + c1;
          
          int dir = 0;
          
          // we search direction for multiple tile moves at once
          if (c1 == c2  &&  Math.abs(r1 - r2) > 0)
            dir = (r1 - r2) > 0 ? size : -size;
          else if (r1 == r2 && Math.abs(c1 - c2) > 0)
            dir = (c1 - c2) > 0 ? 1 : -1;
            
          if (dir != 0) {
            // we move tiles in the direction
            do {
              int newBlankPos = blankPos + dir;
              tiles[blankPos] = tiles[newBlankPos];
              blankPos = newBlankPos;
            } while(blankPos != clickPos);
            
            tiles[blankPos] = 0;
          }
          
          // we check if game is solved
          gameOver = isSolved();
        }
        
        // we repaint panel
        repaint();
      }
    });
    
    newGame();
  }
 
  private void newGame() {
    do {
      reset(); // reset in intial state
      shuffle(); // shuffle
    } while(!isSolvable()); // make it until grid be solvable
    
    gameOver = false;
  }
 
  private void reset() {
    for (int i = 0; i < tiles.length; i++) {
      tiles[i] = (i + 1) % tiles.length;
    }
    
    // we set blank cell at the last
    blankPos = tiles.length - 1;
  }
 
  private void shuffle() {
    // don't include the blank tile in the shuffle, leave in the solved position
    int n = nbTiles;
    
    while (n > 1) {
      int r = RANDOM.nextInt(n--);
      int tmp = tiles[r];
      tiles[r] = tiles[n];
      tiles[n] = tmp;
    }
  }
 
  // Only half permutations of the puzzle are solvable.
  // Whenever a tile is preceded by a tile with higher value it counts
  // as an inversion. In our case, with the blank tile in the solved position,
  // the number of inversions must be even for the puzzle to be solvable
  private boolean isSolvable() {
    int countInversions = 0;
    
    for (int i = 0; i < nbTiles; i++) {
      for (int j = 0; j < i; j++) {
        if (tiles[j] > tiles[i])
          countInversions++;
      }
    }
    
    return countInversions % 2 == 0;
  }
 
  private boolean isSolved() {
    if (tiles[tiles.length - 1] != 0) // if blank tile is not in the solved position ==> not solved
      return false;
    
    for (int i = nbTiles - 1; i >= 0; i--) {
      if (tiles[i] != i + 1)
        return false;      
    }
    
    return true;
  }
 
  private void drawGrid(Graphics2D g) {
    for (int i = 0; i < tiles.length; i++) {
      // we convert 1D coords to 2D coords given the size of the 2D Array
      int r = i / size;
      int c = i % size;
      // we convert in coords on the UI
      int x = margin + c * tileSize;
      int y = margin + r * tileSize;
      
      // check special case for blank tile
      if(tiles[i] == 0) {
        if (gameOver) {
          g.setColor(FOREGROUND_COLOR);
          drawCenteredString(g, "u2713", x, y);
        }
        
        continue;
      }
      
      // for other tiles
      g.setColor(getForeground());
      g.fillRoundRect(x, y, tileSize, tileSize, 25, 25);
      g.setColor(Color.BLACK);
      g.drawRoundRect(x, y, tileSize, tileSize, 25, 25);
      g.setColor(Color.WHITE);
      
      drawCenteredString(g, String.valueOf(tiles[i]), x , y);
    }
  }
 
  private void drawStartMessage(Graphics2D g) {
    if (gameOver) {
      g.setFont(getFont().deriveFont(Font.BOLD, 18));
      g.setColor(FOREGROUND_COLOR);
      String s = "Click to start new game";
      g.drawString(s, (getWidth() - g.getFontMetrics().stringWidth(s)) / 2,
          getHeight() - margin);
    }
  }
 
  private void drawCenteredString(Graphics2D g, String s, int x, int y) {
    // center string s for the given tile (x,y)
    FontMetrics fm = g.getFontMetrics();
    int asc = fm.getAscent();
    int desc = fm.getDescent();
    g.drawString(s,  x + (tileSize - fm.stringWidth(s)) / 2,
        y + (asc + (tileSize - (asc + desc)) / 2));
  }
 
  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2D = (Graphics2D) g;
    g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    drawGrid(g2D);
    drawStartMessage(g2D);
  }
 
  public static void main(String[] args) {
    SwingUtilities.invokeLater(() -> {
      JFrame frame = new JFrame();
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setTitle("Game of Fifteen");
      frame.setResizable(false);
      frame.add(new GameOfFifteen(4, 550, 30), BorderLayout.CENTER);
      frame.pack();
      // center on the screen
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
    });
  }
 
 
}

เช›เซ‡เชฒเซเชฒเซ‡, เชšเชพเชฒเซ‹ เชฐเชฎเซ€เช!

เช† เชฐเชฎเชคเชจเซ‡ เชฒเซ‹เชจเซเชš เช•เชฐเชตเชพเชจเซ‹ เช…เชจเซ‡ เชคเซ‡เชจเซ‡ เช•เซเชฐเชฟเชฏเชพเชฎเชพเช‚ เชšเช•เชพเชธเชตเชพเชจเซ‹ เชธเชฎเชฏ เช›เซ‡. เช•เซเชทเซ‡เชคเซเชฐ เช†เชจเชพ เชœเซ‡เชตเซเช‚ เชฆเซ‡เช–เชพเชตเซเช‚ เชœเซ‹เชˆเช:

เชœเชพเชตเชพเชฎเชพเช‚ "เชŸเซ‡เช—" - เชธเช‚เชชเซ‚เชฐเซเชฃ เชฐเชฎเชค เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เชตเชฟเช•เชธเชพเชตเชตเซ€

เชšเชพเชฒเซ‹ เช•เซ‹เชฏเชกเซ‹ เช‰เช•เซ‡เชฒเชตเชพเชจเซ‹ เชชเซเชฐเชฏเชพเชธ เช•เชฐเซ€เช. เชœเซ‹ เชฌเชงเซเช‚ เชฌเชฐเชพเชฌเชฐ เชฐเชนเซเชฏเซเช‚, เชคเซ‹ เช…เชฎเชจเซ‡ เช† เชฎเชณเชถเซ‡:

เชœเชพเชตเชพเชฎเชพเช‚ "เชŸเซ‡เช—" - เชธเช‚เชชเซ‚เชฐเซเชฃ เชฐเชฎเชค เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เชตเชฟเช•เชธเชพเชตเชตเซ€

เชฌเชธ เชเชŸเชฒเซเช‚ เชœ. เชถเซเช‚ เชคเชฎเซ‡ เชตเชงเซ เช…เชชเซ‡เช•เซเชทเชพ เชฐเชพเช–เซ€ เชนเชคเซ€? ๐Ÿ™‚

เชธเซเช•เชฟเชฒเชฌเซ‹เช•เซเชธ เชญเชฒเชพเชฎเชฃ เช•เชฐเซ‡ เช›เซ‡:

เชธเซ‹เชฐเซเชธ: www.habr.com

เชเช• เชŸเชฟเชชเซเชชเชฃเซ€ เช‰เชฎเซ‡เชฐเซ‹