Β«ΠŸΡΡ‚Π½Π°ΡˆΠΊΠ°Β» Π½Π° Java β€” ΠΊΠ°ΠΊ Π΄Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΈΠΌ ΠΏΡŠΠ»Π½ΠΎΡ†Π΅Π½Π½Π° ΠΈΠ³Ρ€Π°

Β«ΠŸΡΡ‚Π½Π°ΡˆΠΊΠ°Β» Π½Π° Java β€” ΠΊΠ°ΠΊ Π΄Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΈΠΌ ΠΏΡŠΠ»Π½ΠΎΡ†Π΅Π½Π½Π° ΠΈΠ³Ρ€Π°

Β«ΠŸΠ΅Ρ‚Π½Π°Π΄Π΅ΡΠ΅Ρ‚Β», ΠΈΠ»ΠΈ Β«ΠŸΡΡ‚Π½Π°ΡˆΠΊΠ°Β» β€” ΠΎΡ‚Π»ΠΈΡ‡Π΅Π½ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π·Π° проста логичСска ΠΈΠ³Ρ€Π°, популярна Π² цСлия свят. Π—Π° Π΄Π° сС Ρ€Π΅ΡˆΠΈ ΠΏΡŠΠ·Π΅Π»ΡŠΡ‚, Π΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π΄Π° сС подрСдят ΠΊΠ²Π°Π΄Ρ€Π°Ρ‚ΠΈΡ‚Π΅ с числа ΠΏΠΎ Ρ€Π΅Π΄, ΠΎΡ‚ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎΡ‚ΠΎ към ΠΏΠΎ-голямото. Π’ΠΎΠ²Π° Π½Π΅ Π΅ лСсно, Π½ΠΎ Π΅ интСрСсно.

Π’ днСшния ΡƒΡ€ΠΎΠΊ ΠΏΠΎΠΊΠ°Π·Π²Π°ΠΌΠ΅ ΠΊΠ°ΠΊ Π΄Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΈΠΌ Β«ΠŸΡΡ‚Π½Π°ΡˆΠΊΠ°Β» Π½Π° Java 8 с Eclipse. Π—Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π½Π° UI Ρ‰Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ Swing API.

НапомнямС: Π·Π° всички Ρ‡ΠΈΡ‚Π°Ρ‚Π΅Π»ΠΈ Π½Π° Β«Π₯Π°Π±Ρ€Π°Β» β€” ΠΎΡ‚ΡΡ‚ΡŠΠΏΠΊΠ° ΠΎΡ‚ 10 000 Ρ€ΡƒΠ±Π»ΠΈ ΠΏΡ€ΠΈ записванС Π½Π° всСки курс Skillbox с ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ Β«Π₯Π°Π±Ρ€Β».

Skillbox ΠΏΡ€Π΅ΠΏΠΎΡ€ΡŠΡ‡Π²Π°: ΠžΠ±Ρ€Π°Π·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅Π½ ΠΎΠ½Π»Π°ΠΉΠ½ курс Β«ΠŸΡ€ΠΎΡ„Π΅ΡΠΈΡ Java-Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΒ».

Π”ΠΈΠ·Π°ΠΉΠ½ Π½Π° ΠΈΠ³Ρ€Π°Ρ‚Π°

На Ρ‚ΠΎΠ·ΠΈ Π΅Ρ‚Π°ΠΏ Π΅ Π½ΡƒΠΆΠ½ΠΎ Π΄Π° ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠΌ свойствата:

  • Size β€” Ρ€Π°Π·ΠΌΠ΅Ρ€ Π½Π° ΠΈΠ³Ρ€Π°Π»Π½ΠΎΡ‚ΠΎ ΠΏΠΎΠ»Π΅;
  • nbTiles β€” Π±Ρ€ΠΎΠΉ Π½Π° ΠΏΠ΅Ρ‚Π½Π°ΡˆΠΊΠΈΡ‚Π΅ Π½Π° ΠΏΠΎΠ»Π΅Ρ‚ΠΎ. nbTiles = size*size β€” 1;
  • Tiles β€” ΠΏΠ΅Ρ‚Π½Π°ΡˆΠΊa, прСдставляваща Π΅Π΄Π½ΠΎΠΌΠ΅Ρ€Π΅Π½ масив ΠΎΡ‚ Ρ†Π΅Π»ΠΈ числа. Всяка ΠΎΡ‚ ΠΏΠ΅Ρ‚Π½Π°ΡˆΠΊΠΈΡ‚Π΅ Ρ‰Π΅ ΠΏΠΎΠ»ΡƒΡ‡ΠΈ ΡƒΠ½ΠΈΠΊΠ°Π»Π½Π° стойност Π² Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π° [0, nbTiles]. Нулата ΠΎΠ±ΠΎΠ·Π½Π°Ρ‡Π°Π²Π° ΠΏΡ€Π°Π·Π΅Π½ ΠΊΠ²Π°Π΄Ρ€Π°Ρ‚;
  • blankPos β€” позиция Π½Π° празния ΠΊΠ²Π°Π΄Ρ€Π°Ρ‚.

Π›ΠΎΠ³ΠΈΠΊΠ° Π½Π° ΠΈΠ³Ρ€Π°Ρ‚Π°

НуТно Π΅ Π΄Π° ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄ Π·Π° Π½ΡƒΠ»ΠΈΡ€Π°Π½Π΅ (reset), ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½ Π·Π° ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€Π°Π½Π΅ Π½Π° Π½ΠΎΠ²Π° ΠΈΠ³Ρ€ΠΎΠ²Π° позиция. Π’Π°ΠΊΠ° Π·Π°Π΄Π°Π²Π°ΠΌΠ΅ стойност Π·Π° всСки Π΅Π»Π΅ΠΌΠ΅Π½Ρ‚ ΠΎΡ‚ масива Π½Π° ΠΏΠ΅Ρ‚Π½Π°ΡˆΠΊΠΈΡ‚Π΅. Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π° поставямС blankPos Π² послСдната позиция Π½Π° масива.

Π‘ΡŠΡ‰ΠΎ Ρ‚Π°ΠΊΠ° Π΅ Π½ΡƒΠΆΠ΅Π½ ΠΌΠ΅Ρ‚ΠΎΠ΄ shuffle Π·Π° Ρ€Π°Π·Π±ΡŠΡ€ΠΊΠ²Π°Π½Π΅ Π½Π° масива Π½Π° ΠΏΠ΅Ρ‚Π½Π°ΡˆΠΊΠΈΡ‚Π΅. НС Π²ΠΊΠ»ΡŽΡ‡Π²Π°ΠΌΠ΅ ΠΏΡ€Π°Π·Π½Π°Ρ‚Π° ΠΏΠ΅Ρ‚Π½Π°ΡˆΠΊΠ° Π² процСса Π½Π° Ρ€Π°Π·Π±ΡŠΡ€ΠΊΠ²Π°Π½Π΅, Π·Π° Π΄Π° я оставим Π½Π° ΠΏΡ€Π΅Π΄ΠΈΡˆΠ½Π°Ρ‚Π° ѝ позиция.

Въй ΠΊΠ°Ρ‚ΠΎ само ΠΏΠΎΠ»ΠΎΠ²ΠΈΠ½Π°Ρ‚Π° ΠΎΡ‚ Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΈΡ‚Π΅ стартови ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ Π½Π° пъзСла ΠΈΠΌΠ°Ρ‚ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅, Π΅ Π½ΡƒΠΆΠ½ΠΎ Π΄Π° ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ получСния Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚ ΠΎΡ‚ Ρ€Π°Π·Π±ΡŠΡ€ΠΊΠ²Π°Π½Π΅Ρ‚ΠΎ, Π·Π° Π΄Π° смС сигурни, Ρ‡Π΅ Ρ‚Π΅ΠΊΡƒΡ‰ΠΎΡ‚ΠΎ Ρ€Π°Π·ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΈΠ·ΠΎΠ±Ρ‰ΠΎ Π΅ Ρ€Π°Π·Ρ€Π΅ΡˆΠΈΠΌΠΎ. Π—Π° Ρ‚ΠΎΠ²Π° опрСдСлямС ΠΌΠ΅Ρ‚ΠΎΠ΄Π° isSolvable.

Ако ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Π° ΠΏΠ΅Ρ‚Π½Π°ΡˆΠΊΠ° Π΅ ΠΏΡ€Π΅Π΄ΡˆΠ΅ΡΡ‚Π²Π°Π½Π° ΠΎΡ‚ ΠΏΠ΅Ρ‚Π½Π°ΡˆΠΊΠ° с ΠΏΠΎ-висока стойност, Ρ‚ΠΎΠ²Π° сС счита Π·Π° инвСрсия. ΠšΠΎΠ³Π°Ρ‚ΠΎ ΠΏΡ€Π°Π·Π½Π°Ρ‚Π° ΠΏΠ΅Ρ‚Π½Π°ΡˆΠΊΠ° Π΅ Π½Π° своСто място, Π±Ρ€ΠΎΠΉΠΊΠ°Ρ‚Π° Π½Π° инвСрсиитС трябва Π΄Π° Π΅ Ρ‡Π΅Ρ‚Π½Π°, Π·Π° Π΄Π° бъдС ΠΏΡŠΠ·Π΅Π»ΡŠΡ‚ Ρ€Π°Π·Ρ€Π΅ΡˆΠΈΠΌ. Π’Π°ΠΊΠ° Ρ‡Π΅, Π±Ρ€ΠΎΠΈΠΌ инвСрсиитС ΠΈ Π²Ρ€ΡŠΡ‰Π°ΠΌΠ΅ true, Π°ΠΊΠΎ числото Π΅ Ρ‡Π΅Ρ‚Π½ΠΎ.

Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π° Π΅ Π²Π°ΠΆΠ½ΠΎ Π΄Π° ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° isSolved, Π·Π° Π΄Π° ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ Π΄Π°Π»ΠΈ Π½Π°ΡˆΠ΅Ρ‚ΠΎ Ρ€Π°Π·ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Game Of Fifteen Π΅ Ρ€Π΅ΡˆΠ΅Π½ΠΎ. ΠŸΡŠΡ€Π²ΠΎ Π³Π»Π΅Π΄Π°ΠΌΠ΅ къдС сС Π½Π°ΠΌΠΈΡ€Π° ΠΏΡ€Π°Π·Π½Π°Ρ‚Π° ΠΏΠ΅Ρ‚Π½Π°ΡˆΠΊΠ°. Ако Π΅ Π² Π½Π°Ρ‡Π°Π»Π½Π°Ρ‚Π° позиция, Ρ‚Π΅ΠΊΡƒΡ‰ΠΎΡ‚ΠΎ Ρ€Π°Π·ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π΅ Π½ΠΎΠ²ΠΎ, ΠΊΠΎΠ΅Ρ‚ΠΎ Π½Π΅ Π΅ Ρ€Π°Π·Ρ€Π΅ΡˆΠ°Π²Π°Π½ΠΎ ΠΏΡ€Π΅Π΄ΠΈ. Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π° обикалямС ΠΏΠ»ΠΎΡ‡ΠΊΠΈΡ‚Π΅ Π² ΠΎΠ±Ρ€Π°Ρ‚Π΅Π½ Ρ€Π΅Π΄ ΠΈ, Π°ΠΊΠΎ стойността Π½Π° ΠΏΠ΅Ρ‚Π½Π°ΡˆΠΊΠ°Ρ‚Π° сС Ρ€Π°Π·Π»ΠΈΡ‡Π°Π²Π° ΠΎΡ‚ ΡΡŠΠΎΡ‚Π²Π΅Ρ‚Π½ΠΈΡ индСкс +1, Π²Ρ€ΡŠΡ‰Π°ΠΌΠ΅ false. Π’ ΠΏΡ€ΠΎΡ‚ΠΈΠ²Π΅Π½ случай, Π² края Π½Π° ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Π΅ Π²Ρ€Π΅ΠΌΠ΅ Π΄Π° Π²ΡŠΡ€Π½Π΅ΠΌ true, Ρ‚ΡŠΠΉ ΠΊΠ°Ρ‚ΠΎ ΠΏΡŠΠ·Π΅Π»ΡŠΡ‚ Π²Π΅Ρ‡Π΅ Π΅ Ρ€Π΅ΡˆΠ΅Π½.

ΠžΡ‰Π΅ Π΅Π΄ΠΈΠ½ ΠΌΠ΅Ρ‚ΠΎΠ΄, ΠΊΠΎΠΉΡ‚ΠΎ трябва Π΄Π° ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈ, Π΅ newGame. Π’ΠΎΠΉ Π΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌ Π·Π° създаванС Π½Π° Π½ΠΎΠ²Π° инстанция Π½Π° ΠΈΠ³Ρ€Π°Ρ‚Π°. Π—Π° Ρ†Π΅Π»Ρ‚Π° Π½ΠΈΠ΅ Π½ΡƒΠ»ΠΈΡ€Π°ΠΌΠ΅ ΠΈΠ³Ρ€ΠΎΠ²ΠΎΡ‚ΠΎ ΠΏΠΎΠ»Π΅, слСд ΠΊΠΎΠ΅Ρ‚ΠΎ Π³ΠΎ Ρ€Π°Π·Π±ΡŠΡ€ΠΊΠ²Π°ΠΌΠ΅ ΠΈ ΠΏΡ€ΠΎΠ΄ΡŠΠ»ΠΆΠ°Π²Π°ΠΌΠ΅, Π΄ΠΎΠΊΠ°Ρ‚ΠΎ ΠΈΠ³Ρ€ΠΎΠ²Π°Ρ‚Π° позиция Π½Π΅ станС Ρ€Π°Π·Ρ€Π΅ΡˆΠΈΠΌΠ°.

Π•Ρ‚ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π·Π° ΠΊΠΎΠ΄ с ΠΊΠ»ΡŽΡ‡ΠΎΠ²Π°Ρ‚Π° Π»ΠΎΠ³ΠΈΠΊΠ° Π½Π° ΠΏΠ΅Ρ‚Π°Π½ΠΊΠΈΡ‚Π΅:

private void newGame() {
  do {
    reset(); // Π½ΡƒΠ»ΠΈΡ€Π°Π½Π΅ Π² Π½Π°Ρ‡Π°Π»Π½ΠΎ ΡΡŠΡΡ‚ΠΎΡΠ½ΠΈΠ΅
    shuffle(); // Ρ€Π°Π·Π±ΡŠΡ€ΠΊΠ²Π°Π½Π΅
  } while(!isSolvable()); // ΠΏΡ€Π°Π²ΠΈΠΌ Π³ΠΎ, Π΄ΠΎΠΊΠ°Ρ‚ΠΎ ΠΌΡ€Π΅ΠΆΠ°Ρ‚Π° станС Ρ€Π°Π·Ρ€Π΅ΡˆΠΈΠΌΠ°
 
  gameOver = false;
}
 
private void reset() {
  for (int i = 0; i  1) {
    int r = RANDOM.nextInt(n--);
    int tmp = tiles[r];
    tiles[r] = tiles[n];
    tiles[n] = tmp;
  }
}
 
// Π‘Π°ΠΌΠΎ ΠΏΠΎΠ»ΠΎΠ²ΠΈΠ½Π°Ρ‚Π° ΠΎΡ‚ ΠΏΠ΅Ρ€ΠΌΡƒΡ‚Π°Ρ†ΠΈΠΈΡ‚Π΅ Π½Π° пъзСла са Ρ€Π°Π·Ρ€Π΅ΡˆΠΈΠΌΠΈ/
// Всяка ΠΏΡŠΡ‚ΠΊΠ°, която Π΅ ΠΏΡ€Π΅Π΄ΡˆΠ΅ΡΡ‚Π²Π°Π½Π° ΠΎΡ‚ ΠΏΡŠΡ‚ΠΊΠ° с ΠΏΠΎ-висока стойност, сС Π±Ρ€ΠΎΠΈ
// ΠΊΠ°Ρ‚ΠΎ инвСрсия. Π’ нашия случай, с ΠΏΡ€Π°Π·Π½Π°Ρ‚Π° ΠΏΠ»ΠΎΡ‡ΠΊΠ° Π² Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½Π°Ρ‚Π° позиция,
// броят Π½Π° инвСрсиитС трябва Π΄Π° Π΅ Ρ‡Π΅Ρ‚Π΅Π½, Π·Π° Π΄Π° Π΅ ΠΏΡŠΠ·Π΅Π»ΡŠΡ‚ Ρ€Π°Π·Ρ€Π΅ΡˆΠΈΠΌ
private boolean isSolvable() {
  int countInversions = 0;
 
  for (int i = 0; i < nbTiles; i++) {
    for (int j = 0; j  tiles[i])
        countInversions++;
    }
  }
 
  return countInversions % 2 == 0;
}
 
private boolean isSolved() {
  if (tiles[tiles.length - 1] != 0) // Π°ΠΊΠΎ ΠΏΡ€Π°Π·Π½Π°Ρ‚Π° ΠΏΠ»ΠΎΡ‡ΠΊΠ° Π½Π΅ Π΅ Π² Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½Π°Ρ‚Π° позиция ==> Π½Π΅ Π΅ Ρ€Π΅ΡˆΠ΅Π½
    return false;
 
  for (int i = nbTiles - 1; i >= 0; i--) {
    if (tiles[i] != i + 1)
      return false;
  }
 
  return true;
}

Накрая трябва Π΄Π° ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈΡ€Π°ΠΌΠ΅ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ Π½Π° ΠΏΠ΅Ρ‚Π°Π½ΠΊΠΈΡ‚Π΅ Π² масива. Π’ΠΎΠ·ΠΈ ΠΊΠΎΠ΄ Ρ‰Π΅ бъдС ΠΈΠ·Π²ΠΈΠΊΠ°Π½ ΠΏΠΎ-късно Ρ‡Ρ€Π΅Π· ΠΎΠ±Ρ€Π°Ρ‚Π΅Π½ ΠΏΠΎΠ²ΠΈΠΊ (callback), Π·Π° Π΄Π° Ρ€Π΅Π°Π³ΠΈΡ€Π° Π½Π° Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ Π½Π° курсора. ΠΠ°ΡˆΠ°Ρ‚Π° ΠΈΠ³Ρ€Π° Ρ‰Π΅ ΠΏΠΎΠ΄Π΄ΡŠΡ€ΠΆΠ° ΠΌΠ½ΠΎΠ³ΠΎΠ±Ρ€ΠΎΠΉΠ½ΠΈ двиТСния Π½Π° ΠΏΠ»ΠΎΡ‡ΠΊΠΈ Π΅Π΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ. Π‘Π»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»Π½ΠΎ, слСд ΠΊΠ°Ρ‚ΠΎ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€Π°ΠΌΠ΅ натиснатата позиция Π½Π° Π΅ΠΊΡ€Π°Π½Π° Π² ΠΏΠ΅Ρ‚Π°Π½ΠΊΠ°, ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ позицията Π½Π° ΠΏΡ€Π°Π·Π½Π°Ρ‚Π° ΠΏΠ΅Ρ‚Π°Π½ΠΊΠ° ΠΈ Ρ‚ΡŠΡ€ΡΠΈΠΌ посоката Π½Π° Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅, Π·Π° Π΄Π° ΠΏΠΎΠ΄Π΄ΡŠΡ€ΠΆΠ°ΠΌΠ΅ мноТСство Π½Π΅ΠΉΠ½ΠΈ двиТСния Π΅Π΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ.

Π•Ρ‚ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π·Π° ΠΊΠΎΠ΄:

// 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;

Π Π°Π·Ρ€Π°Π±ΠΎΡ‚Π²Π°ΠΌΠ΅ интСрфСйс Π½Π° Swing API

Π‘Π΅Π³Π° Π΅ Π²Ρ€Π΅ΠΌΠ΅ Π΄Π° сС Π·Π°Π½ΠΈΠΌΠ°Π΅ΠΌ с интСрфСйса. ΠŸΡŠΡ€Π²ΠΎ Π²Π·Π΅ΠΌΠ°ΠΌΠ΅ класа Jpanel. Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π° рисувамС ΠΏΠ΅Ρ‚Π°Π½ΠΊΠΈΡ‚Π΅ Π½Π° ΠΏΠΎΠ»Π΅Ρ‚ΠΎ - Π·Π° Π΄Π° изчислим Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΈΡ‚Π΅ Π½Π° всяка, Ρ‰Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ Π΄Π°Π½Π½ΠΈΡ‚Π΅, Π·Π°Π΄Π°Π΄Π΅Π½ΠΈ Π² ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚ΡŠΡ€Π° Π½Π° конструктора Π½Π° ΠΈΠ³Ρ€Π°Ρ‚Π°:

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

Margin ΡΡŠΡ‰ΠΎ Π΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚ΡŠΡ€, Π·Π°Π΄Π°Π΄Π΅Π½ Π² конструктора Π½Π° ΠΈΠ³Ρ€Π°Ρ‚Π°.

Π‘Π΅Π³Π° трябва Π΄Π° ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° drawGrid Π·Π° рисуванС Π½Π° ΠΌΡ€Π΅ΠΆΠ°Ρ‚Π° ΠΈ ΠΏΠ΅Ρ‚Π°Π½ΠΊΠΈΡ‚Π΅ Π½Π° Π΅ΠΊΡ€Π°Π½Π°. АнализирамС масива Π½Π° ΠΏΠ΅Ρ‚Π°Π½ΠΊΠΈΡ‚Π΅ ΠΈ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€Π°ΠΌΠ΅ ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ΠΈΡ‚Π΅ Π² ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ΠΈ Π½Π° потрСбитСлския интСрфСйс. Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π° Ρ‰Π΅ нарисувамС всяка ΠΏΠ΅Ρ‚Π°Π½ΠΊΠ° със ΡΡŠΠΎΡ‚Π²Π΅Ρ‚Π½ΠΈΡ Π½ΠΎΠΌΠ΅Ρ€ Π² Ρ†Π΅Π½Ρ‚ΡŠΡ€Π°:

private void drawGrid(Graphics2D g) {
  for (int i = 0; i < tiles.length; i++) {
    // ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€Π°ΠΌΠ΅ 1D ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ΠΈ Π² 2D ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ΠΈ Π½Π° Π±Π°Π·Π°Ρ‚Π° Π½Π° Ρ€Π°Π·ΠΌΠ΅Ρ€Π° Π½Π° 2D масива
    int r = i / size;
    int c = i % size;
    // ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€Π°ΠΌΠ΅ Π² ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ΠΈ Π½Π° интСрфСйса
    int x = margin + c * tileSize;
    int y = margin + r * tileSize;
 
    // ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π·Π° спСциалСн случай с ΠΏΡ€Π°Π·Π½Π° ΠΏΠ»ΠΎΡ‡ΠΊΠ°
    if(tiles[i] == 0) {
      if (gameOver) {
        g.setColor(FOREGROUND_COLOR);
        drawCenteredString(g, "u2713", x, y);
      }
 
      continue;
    }
 
    // Π·Π° Π΄Ρ€ΡƒΠ³ΠΈΡ‚Π΅ ΠΏΠ»ΠΎΡ‡ΠΊΠΈ
    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);
  }
}

Накрая, Ρ‰Π΅ ΠΏΡ€Π΅ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° paintComponent, ΠΊΠΎΠΉΡ‚ΠΎ ΠΏΡ€ΠΎΠΈΠ·Π»ΠΈΠ·Π° ΠΎΡ‚ класа JPane. Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° drawGrid ΠΈ слСд Π½Π΅Π³ΠΎ β€” ΠΌΠ΅Ρ‚ΠΎΠ΄Π° drawStartMessage, Π·Π° Π΄Π° ΠΏΠΎΠΊΠ°ΠΆΠ΅ΠΌ ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅, ΠΊΠΎΠ΅Ρ‚ΠΎ ΠΏΡ€Π΅Π΄Π»Π°Π³Π° ΠΊΠ»ΠΈΠΊΠ²Π°Π½Π΅ Π·Π° стартиранС Π½Π° ΠΈΠ³Ρ€Π°Ρ‚Π°:

private void drawStartMessage(Graphics2D g) {
  if (gameOver) {
    g.setFont(getFont().deriveFont(Font.BOLD, 18));
    g.setColor(FOREGROUND_COLOR);
    String s = "ΠšΠ»ΠΈΠΊΠ½Π΅Ρ‚Π΅, Π·Π° Π΄Π° стартиратС Π½ΠΎΠ²Π° ΠΈΠ³Ρ€Π°";
    g.drawString(s, (getWidth() - g.getFontMetrics().stringWidth(s)) / 2,
        getHeight() - margin);
  }
}
 
private void drawCenteredString(Graphics2D g, String s, int x, int y) {
  // Ρ†Π΅Π½Ρ‚Ρ€ΠΈΡ€Π°ΠΌΠ΅ Π½ΠΈΠ· s Π·Π° Π΄Π°Π΄Π΅Π½Π°Ρ‚Π° ΠΏΠ»ΠΎΡ‡ΠΊΠ° (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);
}

Π Π΅Π°Π³ΠΈΡ€Π°ΠΌΠ΅ Π½Π° дСйствията Π½Π° потрСбитСля Π² интСрфСйса

Π—Π° Π΄Π° ΠΈΠ³Ρ€Π°Ρ‚Π° ΠΏΡ€ΠΎΡ‚ΠΈΡ‡Π° Π½ΠΎΡ€ΠΌΠ°Π»Π½ΠΎ, Π΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π΄Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π²Π°ΠΌΠ΅ дСйствията Π½Π° потрСбитСля Π² интСрфСйса. Π—Π° Ρ‚Π°Π·ΠΈ Ρ†Π΅Π» добавямС имплСмСнтация Π½Π° MouseListener Π² Jpanel ΠΈ ΠΊΠΎΠ΄ Π·Π° Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ Π½Π° ΠΏΠ»ΠΎΡ‡ΠΊΠΈΡ‚Π΅, ΠΊΠΎΠΉΡ‚ΠΎ Π²Π΅Ρ‡Π΅ Π΅ ΠΏΠΎΠΊΠ°Π·Π°Π½ ΠΏΠΎ-Π³ΠΎΡ€Π΅:

addMouseListener(new MouseAdapter() {
  @Override
  public void mousePressed(MouseEvent e) {
    // ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° сС, Π·Π° Π΄Π° ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈ Π½Π° ΠΏΠΎΡ‚Ρ€Π΅Π±ΠΈΡ‚Π΅Π»ΠΈΡ‚Π΅ Π΄Π° взаимодСйстват с ΠΌΡ€Π΅ΠΆΠ°Ρ‚Π° Ρ‡Ρ€Π΅Π· ΠΊΠ»ΠΈΠΊΠ²Π°Π½Π΅
    // Π²Ρ€Π΅ΠΌΠ΅ Π΅ Π΄Π° Ρ€Π΅Π°Π»ΠΈΠ·ΠΈΡ€Π°ΠΌΠ΅ взаимодСйствиС с ΠΏΠΎΡ‚Ρ€Π΅Π±ΠΈΡ‚Π΅Π»ΠΈΡ‚Π΅ Π·Π° Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ Π½Π° ΠΏΠ»ΠΎΡ‡ΠΊΠΈΡ‚Π΅ Π·Π° Ρ€Π΅ΡˆΠ°Π²Π°Π½Π΅ Π½Π° ΠΈΠ³Ρ€Π°Ρ‚Π°!
    if (gameOver) {
      newGame();
    } else {
      // ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ позицията Π½Π° ΠΊΠ»ΠΈΠΊΠ°
      int ex = e.getX() - margin;
      int ey = e.getY() - margin;
 
      // ΠΊΠ»ΠΈΠΊ Π² ΠΌΡ€Π΅ΠΆΠ°Ρ‚Π°?
      if (ex  gridSize  || ey  gridSize)
        return;
 
      // ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ позицията Π² ΠΌΡ€Π΅ΠΆΠ°Ρ‚Π°
      int c1 = ex / tileSize;
      int r1 = ey / tileSize;
 
      // ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ позицията Π½Π° ΠΏΡ€Π°Π·Π½Π°Ρ‚Π° ΠΊΠ»Π΅Ρ‚ΠΊΠ°
      int c2 = blankPos % size;
      int r2 = blankPos / size;
 
      // ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€Π°ΠΌΠ΅ Π² 1D ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ΠΈ
      int clickPos = r1 * size + c1;
 
      int dir = 0;
 
      // Ρ‚ΡŠΡ€ΡΠΈΠΌ посока Π·Π° ΠΌΠ½ΠΎΠ³ΠΎΠΊΡ€Π°Ρ‚Π½ΠΈ двиТСния Π½Π° ΠΏΠ»ΠΎΡ‡ΠΊΠΈ навСднъТ
      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) {
        // Π΄Π²ΠΈΠΆΠΈΠΌ ΠΏΠ»ΠΎΡ‡ΠΊΠΈΡ‚Π΅ Π² посоката
        do {
          int newBlankPos = blankPos + dir;
          tiles[blankPos] = tiles[newBlankPos];
          blankPos = newBlankPos;
        } while(blankPos != clickPos);
 
        tiles[blankPos] = 0;
      }
 
      // провСрявамС Π΄Π°Π»ΠΈ ΠΈΠ³Ρ€Π°Ρ‚Π° Π΅ Ρ€Π΅ΡˆΠ΅Π½Π°
      gameOver = isSolved();
    }
 
    // прСрисувамС ΠΏΠ°Π½Π΅Π»Π°
    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;
 
// Π©Π΅ създадСм ΠΈΠ³Ρ€Π° Π½Π° 15 с Java 8 ΠΈ Swing
// Ако ΠΈΠΌΠ°Ρ‚Π΅ Π²ΡŠΠΏΡ€ΠΎΡΠΈ, Π½Π΅ сС ΠΊΠΎΠ»Π΅Π±Π°ΠΉΡ‚Π΅ Π΄Π° ΠΏΡ€ΠΎΡ‡Π΅Ρ‚Π΅Ρ‚Π΅ ΠΊΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΡ‚Π΅ ;)
public class GameOfFifteen extends JPanel { // Π½Π°ΡˆΠ°Ρ‚Π° ΠΌΡ€Π΅ΠΆΠ° Ρ‰Π΅ бъдС нарисувана Π² спСциализиран ΠΏΠ°Π½Π΅Π»
 
  // Π Π°Π·ΠΌΠ΅Ρ€ Π½Π° инстанцията Π½Π° ΠΈΠ³Ρ€Π°Ρ‚Π° Голяма осСм
  private int size;
  // Π‘Ρ€ΠΎΠΉ ΠΏΠ»ΠΎΡ‡ΠΊΠΈ
  private int nbTiles;
  // UI Π Π°Π·ΠΌΠ΅Ρ€ Π½Π° ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Π°Ρ‚Π°
  private int dimension;
  // ОсновСн цвят
  private static final Color FOREGROUND_COLOR = new Color(239, 83, 80); // ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»Π΅Π½ цвят
  // Π‘Π»ΡƒΡ‡Π°Π΅Π½ ΠΎΠ±Π΅ΠΊΡ‚ Π·Π° Ρ€Π°Π·Π±ΡŠΡ€ΠΊΠ²Π°Π½Π΅ Π½Π° ΠΏΠ»ΠΎΡ‡ΠΊΠΈΡ‚Π΅
  private static final Random RANDOM = new Random();
  // Π‘ΡŠΡ…Ρ€Π°Π½ΡΠ²Π°Π½Π΅ Π½Π° ΠΏΠ»ΠΎΡ‡ΠΊΠΈΡ‚Π΅ Π² 1D масив ΠΎΡ‚ Ρ†Π΅Π»ΠΈ числа
  private int[] tiles;
  // Π Π°Π·ΠΌΠ΅Ρ€ Π½Π° ΠΏΠ»ΠΎΡ‡ΠΊΠ°Ρ‚Π° Π² UI
  private int tileSize;
  // ΠŸΠΎΠ·ΠΈΡ†ΠΈΡ Π½Π° ΠΏΡ€Π°Π·Π½Π°Ρ‚Π° ΠΏΠ»ΠΎΡ‡ΠΊΠ°
  private int blankPos;
  // Margin Π·Π° ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Π°Ρ‚Π° Π½Π° Ρ€Π°ΠΌΠΊΠ°Ρ‚Π°
  private int margin;
  // Π Π°Π·ΠΌΠ΅Ρ€ Π½Π° UI ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Π°Ρ‚Π°
  private int gridSize;
  private boolean gameOver; // true Π°ΠΊΠΎ ΠΈΠ³Ρ€Π°Ρ‚Π° Π΅ Π·Π°Π²ΡŠΡ€ΡˆΠΈΠ»Π°, false Π² ΠΏΡ€ΠΎΡ‚ΠΈΠ²Π΅Π½ случай
 
  public GameOfFifteen(int size, int dim, int mar) {
    this.size = size;
    dimension = dim;
    margin = mar;
    
    // ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€Π°Π½Π΅ Π½Π° ΠΏΠ»ΠΎΡ‡ΠΊΠΈΡ‚Π΅
    nbTiles = size * size - 1; // -1, Π·Π°Ρ‰ΠΎΡ‚ΠΎ Π½Π΅ Π±Ρ€ΠΎΠΈΠΌ ΠΏΡ€Π°Π·Π½Π°Ρ‚Π° ΠΏΠ»ΠΎΡ‡ΠΊΠ°
    tiles = new int[size * 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) {
        // ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° сС Π·Π° позволяванС Π½Π° ΠΏΠΎΡ‚Ρ€Π΅Π±ΠΈΡ‚Π΅Π»ΠΈΡ‚Π΅ Π΄Π° взаимодСйстват с ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Π°Ρ‚Π° Ρ‡Ρ€Π΅Π· ΠΊΠ»ΠΈΠΊΠ²Π°Π½Π΅
        // Π²Ρ€Π΅ΠΌΠ΅ Π΅ Π΄Π° Ρ€Π΅Π°Π»ΠΈΠ·ΠΈΡ€Π°ΠΌΠ΅ взаимодСйствиС с ΠΏΠΎΡ‚Ρ€Π΅Π±ΠΈΡ‚Π΅Π»ΠΈΡ‚Π΅ Π·Π° ΠΏΡ€ΠΈΠ΄Π²ΠΈΠΆΠ²Π°Π½Π΅ Π½Π° ΠΏΠ»ΠΎΡ‡ΠΊΠΈΡ‚Π΅, Π·Π° Π΄Π° Ρ€Π΅ΡˆΠΈΠΌ ΠΈΠ³Ρ€Π°Ρ‚Π° !
        if (gameOver) {
          newGame();
        } else {
          // ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°Π½Π΅ Π½Π° позицията Π½Π° ΠΊΠ»ΠΈΠΊΠ°
          int ex = e.getX() - margin;
          int ey = e.getY() - margin;
          
          // ΠΊΠ»ΠΈΠΊΠ½Π΅Ρ‚Π΅ Π² ΠΌΡ€Π΅ΠΆΠ°Ρ‚Π° ?
          if (ex  gridSize  || ey  gridSize)
            return;
          
          // ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°Π½Π΅ Π½Π° позицията Π² ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Π°Ρ‚Π°
          int c1 = ex / tileSize;
          int r1 = ey / tileSize;
          
          // ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°Π½Π΅ Π½Π° позицията Π½Π° ΠΏΡ€Π°Π·Π½Π°Ρ‚Π° ΠΊΠ»Π΅Ρ‚ΠΊΠ°
          int c2 = blankPos % size;
          int r2 = blankPos / size;
          
          // ΠΏΡ€Π΅Π²Ρ€ΡŠΡ‰Π°ΠΌΠ΅ Π² 1D ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ΠΈ
          int clickPos = r1 * size + c1;
          
          int dir = 0;
          
          // Ρ‚ΡŠΡ€ΡΠΈΠΌ посока Π·Π° мноТСство двиТСния Π½Π° ΠΏΠ»ΠΎΡ‡ΠΊΠΈ навСднъТ
          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) {
            // Π΄Π²ΠΈΠΆΠΈΠΌ ΠΏΠ»ΠΎΡ‡ΠΊΠΈΡ‚Π΅ Π² посоката
            do {
              int newBlankPos = blankPos + dir;
              tiles[blankPos] = tiles[newBlankPos];
              blankPos = newBlankPos;
            } while(blankPos != clickPos);
            
            tiles[blankPos] = 0;
          }
          
          // провСрявамС Π΄Π°Π»ΠΈ ΠΈΠ³Ρ€Π°Ρ‚Π° Π΅ Ρ€Π΅ΡˆΠ΅Π½Π°
          gameOver = isSolved();
        }
        
        // освСТавамС панСла
        repaint();
      }
    });
    
    newGame();
  }
 
  private void newGame() {
    do {
      reset(); // Π½ΡƒΠ»ΠΈΡ€Π°Π½Π΅ Π² Π½Π°Ρ‡Π°Π»Π½ΠΎ ΡΡŠΡΡ‚ΠΎΡΠ½ΠΈΠ΅
      shuffle(); // Ρ€Π°Π·Π±ΡŠΡ€ΠΊΠ²Π°Π½Π΅
    } while(!isSolvable()); // Π΄Π° Π³ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ, Π΄ΠΎΠΊΠ°Ρ‚ΠΎ ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Π°Ρ‚Π° Π½Π΅ станС Ρ€Π΅ΡˆΠΈΠΌΠ°
    
    gameOver = false;
  }
 
  private void reset() {
    for (int i = 0; i  1) {
      int r = RANDOM.nextInt(n--);
      int tmp = tiles[r];
      tiles[r] = tiles[n];
      tiles[n] = tmp;
    }
  }
 
  // Π‘Π°ΠΌΠΎ ΠΏΠΎΠ»ΠΎΠ²ΠΈΠ½Π°Ρ‚Π° ΠΎΡ‚ пСрСстановкитС Π½Π° пъзСла са Ρ€Π΅ΡˆΠΈΠΌΠΈ.
  // Всякога, ΠΊΠΎΠ³Π°Ρ‚ΠΎ ΠΏΠ»ΠΎΡ‡ΠΊΠ° Π΅ ΠΏΡ€Π΅Π΄ΡˆΠ΅ΡΡ‚Π²Π°Π½Π° ΠΎΡ‚ Π΄Ρ€ΡƒΠ³Π° с ΠΏΠΎ-висока стойност, Ρ‚ΠΎΠ²Π° сС Π±Ρ€ΠΎΠΈ
  // ΠΊΠ°Ρ‚ΠΎ инвСрсия. Π’ нашия случай, с ΠΏΡ€Π°Π·Π½Π°Ρ‚Π° ΠΏΠ»ΠΎΡ‡ΠΊΠ° Π² Ρ€Π΅ΡˆΠ΅Π½Π°Ρ‚Π° позиция,
  // броят Π½Π° инвСрсиитС трябва Π΄Π° бъдС Ρ‡Π΅Ρ‚Π΅Π½ Π·Π° пъзСла Π΄Π° бъдС Ρ€Π΅ΡˆΠΈΠΌ
  private boolean isSolvable() {
    int countInversions = 0;
    
    for (int i = 0; i < nbTiles; i++) {
      for (int j = 0; j  tiles[i])
          countInversions++;
      }
    }
    
    return countInversions % 2 == 0;
  }
 
  private boolean isSolved() {
    if (tiles[tiles.length - 1] != 0) // Π°ΠΊΠΎ ΠΏΡ€Π°Π·Π½Π°Ρ‚Π° ΠΏΠ»ΠΎΡ‡ΠΊΠ° Π½Π΅ Π΅ Π² Ρ€Π΅ΡˆΠ΅Π½Π°Ρ‚Π° позиция ==> Π½Π΅ Π΅ Ρ€Π΅ΡˆΠ΅Π½Π°
      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  {
      JFrame frame = new JFrame();
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setTitle("Π˜Π³Ρ€Π° Π½Π° ΠŸΠ΅Ρ‚Π½Π°Π΄Π΅ΡΠ΅Ρ‚");
      frame.setResizable(false);
      frame.add(new GameOfFifteen(4, 550, 30), BorderLayout.CENTER);
      frame.pack();
      // Ρ†Π΅Π½Ρ‚Ρ€ΠΈΡ€Π°ΠΉΡ‚Π΅ Π½Π° Π΅ΠΊΡ€Π°Π½Π°
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
    });
  }
 
 
}

Накрая, ΠΈΠ³Ρ€Π°Π΅ΠΌ!

Π’Ρ€Π΅ΠΌΠ΅ Π΅ Π΄Π° стартирамС ΠΈΠ³Ρ€Π°Ρ‚Π° ΠΈ Π΄Π° я ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ Π² дСйствиС. ΠŸΠΎΠ»Π΅Ρ‚ΠΎ трябва Π΄Π° ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° ΠΏΠΎ слСдния Π½Π°Ρ‡ΠΈΠ½:

Β«ΠŸΡΡ‚Π½Π°ΡˆΠΊΠ°Β» Π½Π° Java β€” ΠΊΠ°ΠΊ Π΄Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΈΠΌ ΠΏΡŠΠ»Π½ΠΎΡ†Π΅Π½Π½Π° ΠΈΠ³Ρ€Π°

ΠžΠΏΠΈΡ‚Π²Π°ΠΌΠ΅ сС Π΄Π° Ρ€Π΅ΡˆΠΈΠΌ Π³ΠΎΠ»ΠΎΠ²ΠΎΠ»ΠΎΠΌΠΊΠ°. Ако всичко Π΅ ΠΌΠΈΠ½Π°Π»ΠΎ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ, ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ Ρ‚ΠΎΠ²Π°:

Β«ΠŸΡΡ‚Π½Π°ΡˆΠΊΠ°Β» Π½Π° Java β€” ΠΊΠ°ΠΊ Π΄Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΈΠΌ ΠΏΡŠΠ»Π½ΠΎΡ†Π΅Π½Π½Π° ΠΈΠ³Ρ€Π°

И Ρ‚ΠΎΠ²Π° Π΅ всичко. ΠžΡ‡Π°ΠΊΠ²Π°Ρ…Ρ‚Π΅ Π»ΠΈ ΠΏΠΎΠ²Π΅Ρ‡Π΅? πŸ™‚

Skillbox ΠΏΡ€Π΅ΠΏΠΎΡ€ΡŠΡ‡Π²Π°:

Π˜Π·Ρ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com

ΠšΡƒΠΏΠ΅Ρ‚Π΅ Π½Π°Π΄Π΅ΠΆΠ΄Π΅Π½ хостинг Π·Π° сайтовС с Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ DDoS, VPS VDS ΡΡŠΡ€Π²ΡŠΡ€ΠΈ πŸ”₯ ΠšΡƒΠΏΠ΅Ρ‚Π΅ Π½Π°Π΄Π΅ΠΆΠ΄Π΅Π½ хостинг Π·Π° сайтовС с Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ DDoS, VPS VDS ΡΡŠΡ€Π²ΡŠΡ€ΠΈ | ProHoster