
Β«ΠΡΡΠ½Π°Π΄ΡΠ°ΡΡΒ», ΠΈΠ»ΠΈ Β«ΠΡΡΠ½Π°ΡΠΊΠ°Β» β ΠΎΡΠ»ΠΈΡΠ½ΡΠΉ ΠΏΡΠΈΠΌΠ΅Ρ ΠΏΡΠΎΡΡΠΎΠΉ Π»ΠΎΠ³ΠΈΡΠ΅ΡΠΊΠΎΠΉ ΠΈΠ³ΡΡ, ΠΏΠΎΠΏΡΠ»ΡΡΠ½ΠΎΠΉ Π²ΠΎ Π²ΡΠ΅ΠΌ ΠΌΠΈΡΠ΅. ΠΠ»Ρ ΡΠΎΠ³ΠΎ ΡΡΠΎΠ±Ρ ΡΠ΅ΡΠΈΡΡ Π³ΠΎΠ»ΠΎΠ²ΠΎΠ»ΠΎΠΌΠΊΡ, Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠΎ ΡΠ°ΡΡΡΠ°Π²ΠΈΡΡ ΠΊΠ²Π°Π΄ΡΠ°ΡΠΈΠΊΠΈ Ρ ΡΠΈΡΡΠ°ΠΌΠΈ ΠΏΠΎ ΠΏΠΎΡΡΠ΄ΠΊΡ, ΠΎΡ ΠΌΠ΅Π½ΡΡΠ΅Π³ΠΎ ΠΊ Π±ΠΎΠ»ΡΡΠ΅ΠΌΡ. ΠΡΠΎ Π½Π΅ΠΏΡΠΎΡΡΠΎ, Π½ΠΎ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΎ.
Π ΡΠ΅Π³ΠΎΠ΄Π½ΡΡΠ½Π΅ΠΌ ΡΡΡΠΎΡΠΈΠ°Π»Π΅ ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°Π΅ΠΌ, ΠΊΠ°ΠΊ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠ°ΡΡ Β«ΠΡΡΠ½Π°ΡΠΊΡΒ» Π½Π° Java 8 Ρ Eclipse. ΠΠ»Ρ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ UI ΠΌΡ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Swing API.
ΠΠ°ΠΏΠΎΠΌΠΈΠ½Π°Π΅ΠΌ: Π΄Π»Ρ Π²ΡΠ΅Ρ ΡΠΈΡΠ°ΡΠ΅Π»Π΅ΠΉ Β«Π₯Π°Π±ΡΠ°Β» β ΡΠΊΠΈΠ΄ΠΊΠ° 10 000 ΡΡΠ±Π»Π΅ΠΉ ΠΏΡΠΈ Π·Π°ΠΏΠΈΡΠΈ Π½Π° Π»ΡΠ±ΠΎΠΉ ΠΊΡΡΡ Skillbox ΠΏΠΎ ΠΏΡΠΎΠΌΠΎΠΊΠΎΠ΄Ρ Β«Π₯Π°Π±ΡΒ».
Skillbox ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅Ρ: ΠΠ±ΡΠ°Π·ΠΎΠ²Π°ΡΠ΅Π»ΡΠ½ΡΠΉ ΠΎΠ½Π»Π°ΠΉΠ½-ΠΊΡΡΡ .
ΠΡΠΎΠ΅ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΠΈΠ³ΡΡ
ΠΠ° ΡΡΠΎΠΌ ΡΡΠ°ΠΏΠ΅ Π½ΡΠΆΠ½ΠΎ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ ΡΠ²ΠΎΠΉΡΡΠ²Π°:
- Size β ΡΠ°Π·ΠΌΠ΅Ρ ΠΈΠ³ΡΠΎΠ²ΠΎΠ³ΠΎ ΠΏΠΎΠ»Ρ;
- nbTiles β ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΏΡΡΠ½Π°ΡΠ΅ΠΊ Π² ΠΏΠΎΠ»Π΅. nbTiles = size*size β 1;
- Tiles β ΠΏΡΡΠ½Π°ΡΠΊΠ°, ΠΊΠΎΡΠΎΡΠ°Ρ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»ΡΠ΅Ρ ΡΠΎΠ±ΠΎΠΉ ΠΎΠ΄Π½ΠΎΠΌΠ΅ΡΠ½ΡΠΉ ΠΌΠ°ΡΡΠΈΠ² ΡΠ΅Π»ΡΡ ΡΠΈΡΠ΅Π». ΠΠ°ΠΆΠ΄Π°Ρ ΠΈΠ· ΠΏΡΡΠ½Π°ΡΠ΅ΠΊ ΠΏΠΎΠ»ΡΡΠΈΡ ΡΠ½ΠΈΠΊΠ°Π»ΡΠ½ΠΎΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ Π² Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π΅ [0, nbTiles]. ΠΠΎΠ»Ρ ΠΎΠ±ΠΎΠ·Π½Π°ΡΠ°Π΅Ρ ΠΏΡΡΡΠΎΠΉ ΠΊΠ²Π°Π΄ΡΠ°Ρ;
- blankPos β ΠΏΠΎΠ·ΠΈΡΠΈΡ ΠΏΡΡΡΠΎΠ³ΠΎ ΠΊΠ²Π°Π΄ΡΠ°ΡΠ°.
ΠΠΎΠ³ΠΈΠΊΠ° ΠΈΠ³ΡΡ
ΠΡΠΆΠ½ΠΎ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ ΠΌΠ΅ΡΠΎΠ΄ ΡΠ±ΡΠΎΡΠ° (reset), ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΠΉ Π΄Π»Ρ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΠΈ Π½ΠΎΠ²ΠΎΠΉ ΠΈΠ³ΡΠΎΠ²ΠΎΠΉ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ. Π’Π°ΠΊ ΠΌΡ ΡΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ° ΠΌΠ°ΡΡΠΈΠ²Π° ΠΏΡΡΠ½Π°ΡΠ΅ΠΊ. ΠΡ Π° ΠΏΠΎΡΠΎΠΌ ΠΏΠΎΠΌΠ΅ΡΠ°Π΅ΠΌ blankPos Π² ΠΏΠΎΡΠ»Π΅Π΄Π½ΡΡ ΠΏΠΎΠ·ΠΈΡΠΈΡ ΠΌΠ°ΡΡΠΈΠ²Π°.
Π’Π°ΠΊΠΆΠ΅ Π½ΡΠΆΠ΅Π½ ΠΌΠ΅ΡΠΎΠ΄ shuffle Π΄Π»Ρ ΠΏΠ΅ΡΠ΅ΡΠ°ΡΠΎΠ²ΠΊΠΈ ΠΌΠ°ΡΡΠΈΠ²Π° ΠΏΡΡΠ½Π°ΡΠ΅ΠΊ. ΠΡ Π½Π΅ Π²ΠΊΠ»ΡΡΠ°Π΅ΠΌ ΠΏΡΡΡΡΡ ΠΏΡΡΠ½Π°ΡΠΊΡ Π² ΠΏΡΠΎΡΠ΅ΡΡ ΠΏΠ΅ΡΠ΅ΡΠ°ΡΠΎΠ²ΠΊΠΈ, ΡΡΠΎΠ±Ρ ΠΎΡΡΠ°Π²ΠΈΡΡ Π΅Π΅ Π² ΠΏΡΠ΅ΠΆΠ½Π΅ΠΌ ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ.
ΠΠΎΡΠΊΠΎΠ»ΡΠΊΡ ΡΠΎΠ»ΡΠΊΠΎ ΠΏΠΎΠ»ΠΎΠ²ΠΈΠ½Π° Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΡΡ ΡΡΠ°ΡΡΠΎΠ²ΡΡ ΠΏΠΎΠ·ΠΈΡΠΈΠΉ Π³ΠΎΠ»ΠΎΠ²ΠΎΠ»ΠΎΠΌΠΊΠΈ ΠΈΠΌΠ΅Π΅Ρ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅, Π½ΡΠΆΠ½ΠΎ ΠΏΡΠΎΠ²Π΅ΡΠΈΡΡ ΠΏΠΎΠ»ΡΡΠΈΠ²ΡΠΈΠΉΡΡ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ ΠΏΠ΅ΡΠ΅ΠΌΠ΅ΡΠΈΠ²Π°Π½ΠΈΡ, ΡΡΠΎΠ±Ρ ΡΠ±Π΅Π΄ΠΈΡΡΡΡ Π² ΡΠΎΠΌ, ΡΡΠΎ ΡΠ΅ΠΊΡΡΠΈΠΉ ΡΠ°ΡΠΊΠ»Π°Π΄ Π²ΠΎΠΎΠ±ΡΠ΅ ΡΠ΅ΡΠ°Π΅ΠΌ. Π§ΡΠΎΠ±Ρ ΡΡΠΎ ΡΠ΄Π΅Π»Π°ΡΡ, ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ ΠΌΠ΅ΡΠΎΠ΄ isSolvable.
ΠΡΠ»ΠΈ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΎΠΉ ΠΏΡΡΠ½Π°ΡΠΊΠ΅ ΠΏΡΠ΅Π΄ΡΠ΅ΡΡΠ²ΡΠ΅Ρ ΠΏΡΡΠ½Π°ΡΠΊΠ° Ρ Π±ΠΎΠ»Π΅Π΅ Π²ΡΡΠΎΠΊΠΈΠΌ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ΠΌ, ΡΡΠΎ ΡΡΠΈΡΠ°Π΅ΡΡΡ ΠΈΠ½Π²Π΅ΡΡΠΈΠ΅ΠΉ. ΠΠΎΠ³Π΄Π° ΠΏΡΡΡΠ°Ρ ΠΏΡΡΠ½Π°ΡΠΊΠ° Π½Π°Ρ ΠΎΠ΄ΠΈΡΡΡ Π½Π° ΡΠ²ΠΎΠ΅ΠΌ ΠΌΠ΅ΡΡΠ΅, ΡΠΈΡΠ»ΠΎ ΠΈΠ½Π²Π΅ΡΡΠΈΠΉ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±ΡΡΡ ΡΠ΅ΡΠ½ΡΠΌ, ΡΡΠΎΠ±Ρ Π³ΠΎΠ»ΠΎΠ²ΠΎΠ»ΠΎΠΌΠΊΠ° Π±ΡΠ»Π° ΡΠ°Π·ΡΠ΅ΡΠΈΠΌΠΎΠΉ. ΠΡΠ°ΠΊ, ΠΌΡ ΠΏΠΎΠ΄ΡΡΠΈΡΡΠ²Π°Π΅ΠΌ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΈΠ½Π²Π΅ΡΡΠΈΠΉ ΠΈ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌ true, Π΅ΡΠ»ΠΈ ΡΠΈΡΠ»ΠΎ ΡΠ΅ΡΠ½ΠΎΠ΅.
ΠΠ°ΡΠ΅ΠΌ Π²Π°ΠΆΠ½ΠΎ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ ΠΌΠ΅ΡΠΎΠ΄ isSolved, ΡΡΠΎΠ±Ρ ΠΏΡΠΎΠ²Π΅ΡΠΈΡΡ, ΡΠ΅ΡΠ΅Π½ Π»ΠΈ Π½Π°Ρ ΡΠ°ΡΠΊΠ»Π°Π΄ Game Of Fifteen. Π‘Π½Π°ΡΠ°Π»Π° ΠΌΡ ΡΠΌΠΎΡΡΠΈΠΌ, Π³Π΄Π΅ Π½Π°Ρ ΠΎΠ΄ΠΈΡΡΡ ΠΏΡΡΡΠ°Ρ ΠΏΡΡΠ½Π°ΡΠΊΠ°. ΠΡΠ»ΠΈ Π² Π½Π°ΡΠ°Π»ΡΠ½ΠΎΠΉ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ, ΡΠΎ ΡΠ΅ΠΊΡΡΠΈΠΉ ΡΠ°ΡΠΊΠ»Π°Π΄ β Π½ΠΎΠ²ΡΠΉ, Π½Π΅ ΡΠ΅ΡΠ΅Π½Π½ΡΠΉ ΡΠ°Π½Π΅Π΅. ΠΠ°ΡΠ΅ΠΌ ΠΌΡ ΠΏΠ΅ΡΠ΅Π±ΠΈΡΠ°Π΅ΠΌ ΠΏΠ»ΠΈΡΠΊΠΈ Π² ΠΎΠ±ΡΠ°ΡΠ½ΠΎΠΌ ΠΏΠΎΡΡΠ΄ΠΊΠ΅, ΠΈ, Π΅ΡΠ»ΠΈ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΠΏΡΡΠ½Π°ΡΠΊΠΈ ΠΎΡΠ»ΠΈΡΠ°Π΅ΡΡΡ ΠΎΡ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΠ΅Π³ΠΎ ΠΈΠ½Π΄Π΅ΠΊΡΠ° +1, Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌ false. Π ΠΏΡΠΎΡΠΈΠ²Π½ΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ Π² ΠΊΠΎΠ½ΡΠ΅ ΠΌΠ΅ΡΠΎΠ΄Π° ΠΏΠΎΡΠ° Π²Π΅ΡΠ½ΡΡΡ true, ΠΏΠΎΡΠΎΠΌΡ ΡΡΠΎ Π³ΠΎΠ»ΠΎΠ²ΠΎΠ»ΠΎΠΌΠΊΠ° ΡΠΆΠ΅ ΡΠ΅ΡΠ΅Π½Π°.
ΠΡΠ΅ ΠΎΠ΄ΠΈΠ½ ΠΌΠ΅ΡΠΎΠ΄, ΠΊΠΎΡΠΎΡΡΠΉ Π½ΡΠΆΠ½ΠΎ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ, β 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;
}ΠΠ°ΠΊΠΎΠ½Π΅Ρ, Π½ΡΠΆΠ½ΠΎ Π·Π°ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠΈΡΠΎΠ²Π°ΡΡ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ ΠΏΡΡΠ½Π°ΡΠ΅ΠΊ Π² ΠΌΠ°ΡΡΠΈΠ²Π΅. ΠΡΠΎΡ ΠΊΠΎΠ΄ Π±ΡΠ΄Π΅Ρ Π²ΡΠ·ΡΠ²Π°ΡΡΡΡ ΠΏΠΎΠ·ΠΆΠ΅ ΠΏΠΎΡΡΠ΅Π΄ΡΡΠ²ΠΎΠΌ ΠΎΠ±ΡΠ°ΡΠ½ΠΎΠ³ΠΎ Π²ΡΠ·ΠΎΠ²Π° (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;Π Π°Π·ΡΠ°Π±Π°ΡΡΠ²Π°Π΅ΠΌ UI Π½Π° 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++) {
// 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);
}
}ΠΠ°ΠΊΠΎΠ½Π΅Ρ, ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΠΌ ΠΌΠ΅ΡΠΎΠ΄ paintComponent, ΡΠ²Π»ΡΡΡΠΈΠΉΡΡ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄Π½ΡΠΌ ΠΊΠ»Π°ΡΡΠ° JPane. ΠΠ°ΡΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ ΠΌΠ΅ΡΠΎΠ΄ drawGrid, Π° ΠΏΠΎΡΠ»Π΅ β ΠΌΠ΅ΡΠΎΠ΄ drawStartMessage Π΄Π»Ρ ΠΎΡΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ, ΠΏΡΠ΅Π΄Π»Π°Π³Π°ΡΡΠ΅Π³ΠΎ ΠΊΠ»ΠΈΠΊΠ½ΡΡΡ Π΄Π»Ρ Π·Π°ΠΏΡΡΠΊΠ° ΠΈΠ³ΡΡ:
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. ΠΠ»Ρ ΡΡΠΎΠ³ΠΎ Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ ΠΈΠΌΠΏΠ»Π΅ΠΌΠ΅Π½ΡΠ°ΡΠΈΡ MouseListener Π½Π° Jpanel ΠΈ ΠΊΠΎΠ΄ Π΄Π»Ρ ΠΏΠ΅ΡΠ΅ΠΌΠ΅ΡΠ΅Π½ΠΈΡ ΠΏΡΡΠ½Π°ΡΠ΅ΠΊ, ΡΠΆΠ΅ ΠΏΠΎΠΊΠ°Π·Π°Π½Π½ΡΠΉ Π²ΡΡΠ΅:
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);
});
}
}ΠΠ°ΠΊΠΎΠ½Π΅Ρ, ΠΈΠ³ΡΠ°Π΅ΠΌ!
Π‘Π°ΠΌΠΎΠ΅ Π²ΡΠ΅ΠΌΡ Π·Π°ΠΏΡΡΡΠΈΡΡ ΠΈΠ³ΡΡ ΠΈ ΠΏΡΠΎΠ²Π΅ΡΠΈΡΡ Π΅Π΅ Π² Π΄Π΅ΠΉΡΡΠ²ΠΈΠΈ. ΠΠΎΠ»Π΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π²ΡΠ³Π»ΡΠ΄Π΅ΡΡ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ:

ΠΡΠΎΠ±ΡΠ΅ΠΌ ΡΠ΅ΡΠΈΡΡ Π³ΠΎΠ»ΠΎΠ²ΠΎΠ»ΠΎΠΌΠΊΡ. ΠΡΠ»ΠΈ Π²ΡΠ΅ ΠΏΡΠΎΡΠ»ΠΎ ΡΡΠΏΠ΅ΡΠ½ΠΎ, ΠΏΠΎΠ»ΡΡΠ°Π΅ΠΌ Π²ΠΎΡ ΡΡΠΎ:

ΠΠΎΡ ΠΈ Π²ΡΡ. Π Π²Ρ ΠΎΠΆΠΈΠ΄Π°Π»ΠΈ Π±ΠΎΠ»ΡΡΠ΅Π³ΠΎ? π
Skillbox ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅Ρ:
- ΠΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΠΊΡΡΡ .
- ΠΡΠΈΠΊΠ»Π°Π΄Π½ΠΎΠΉ ΠΎΠ½Π»Π°ΠΉΠ½-ΠΊΡΡΡ .
- ΠΠ²ΡΡ Π»Π΅ΡΠ½ΠΈΠΉ ΠΏΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΠΊΡΡΡ .
ΠΡΡΠΎΡΠ½ΠΈΠΊ: habr.com
