
"āĻĒāύā§āϰā§" āĻŦāĻž "āĻĒāύā§āϰā§" āĻāĻāĻāĻŋ āϏāĻžāϧāĻžāϰāĻŖ āϞāĻāĻŋāĻ āĻā§āĻŽā§āϰ āĻāĻāĻāĻŋ āĻāĻŽā§āĻāĻžāϰ āĻāĻĻāĻžāĻšāϰāĻŖ āϝāĻž āϏāĻžāϰāĻž āĻŦāĻŋāĻļā§āĻŦā§ āĻāύāĻĒā§āϰāĻŋāϝāĻŧāĨ¤ āϧāĻžāĻāϧāĻžāĻāĻŋ āϏāĻŽāĻžāϧāĻžāύ āĻāϰāĻžāϰ āĻāύā§āϝ, āĻāĻĒāύāĻžāĻā§ āĻā§āĻ āĻĨā§āĻā§ āĻŦāĻĄāĻŧ āĻĒāϰā§āϝāύā§āϤ āĻā§āϰāĻŽāĻžāύā§āϏāĻžāϰ⧠āϏāĻāĻā§āϝāĻž āϏāĻš āĻŦāϰā§āĻāĻā§āώā§āϤā§āϰāĻā§āϞāĻŋ āϏāĻžāĻāĻžāϤ⧠āĻšāĻŦā§āĨ¤ āĻāĻāĻž āϏāĻšāĻ āύāϝāĻŧ, āĻāĻŋāύā§āϤ⧠āĻāĻāĻž āĻāĻāϰā§āώāĻŖā§āϝāĻŧ.
āĻāĻāĻā§āϰ āĻāĻŋāĻāĻā§āϰāĻŋāϝāĻŧāĻžāϞ⧠āĻāĻŽāϰāĻž āĻĻā§āĻāĻžāĻŦ āĻāĻŋāĻāĻžāĻŦā§ Eclipse āĻāϰ āĻŽāĻžāϧā§āϝāĻŽā§ Java 8-āĻ Fifteen āĻĄā§āĻā§āϞāĻĒ āĻāϰāĻž āϝāĻžāϝāĻŧāĨ¤ UI āĻĄā§āĻā§āϞāĻĒ āĻāϰāϤ⧠āĻāĻŽāϰāĻž Swing API āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻāϰāĻŦāĨ¤
āĻāĻŽāϰāĻž āĻŽāύ⧠āĻāϰāĻŋāϝāĻŧā§ āĻĻāĻŋāĻā§āĻāĻŋ: "Habr"-āĻāϰ āϏāĻāϞ āĻĒāĻžāĻ āĻāĻĻā§āϰ āĻāύā§āϝ - "Habr" āĻĒā§āϰāĻāĻžāϰāĻŽā§āϞāĻ āĻā§āĻĄ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻāϰ⧠āϝā§āĻā§āύ⧠Skillbox āĻā§āϰā§āϏ⧠āύāĻĨāĻŋāĻā§āĻā§āϤ āĻāϰāĻžāϰ āϏāĻŽāϝāĻŧ 10 āϰā§āĻŦā§āϞ āĻāĻžāĻĄāĻŧāĨ¤
Skillbox āϏā§āĻĒāĻžāϰāĻŋāĻļ āĻāϰā§: āĻļāĻŋāĻā§āώāĻžāĻāϤ āĻ āύāϞāĻžāĻāύ āĻā§āϰā§āϏ .
āĻā§āϞāĻžāϰ āύāĻāĻļāĻž
āĻāĻ āĻĒāϰā§āϝāĻžāϝāĻŧā§ āĻāĻĒāύāĻžāĻā§ āĻŦā§āĻļāĻŋāώā§āĻā§āϝāĻā§āϞāĻŋ āϏāĻāĻā§āĻāĻžāϝāĻŧāĻŋāϤ āĻāϰāϤ⧠āĻšāĻŦā§:
- āĻāĻāĻžāϰ - āĻā§āϞāĻžāϰ āĻŽāĻžāĻ ā§āϰ āĻāĻāĻžāϰ;
- nbTiles â āĻā§āώā§āϤā§āϰā§āϰ āĻā§āϝāĻžāĻā§āϰ āϏāĻāĻā§āϝāĻžāĨ¤ nbTiles = āĻāĻāĻžāϰ * āĻāĻāĻžāϰ - 1;
- āĻāĻžāĻāϞāϏ āĻšāϞ āĻāĻāĻāĻŋ āĻā§āϝāĻžāĻ āϝāĻž āĻĒā§āϰā§āĻŖāϏāĻāĻā§āϝāĻžāϰ āĻāĻ-āĻŽāĻžāϤā§āϰāĻŋāĻ āĻ ā§āϝāĻžāϰā§āĨ¤ āĻĒā§āϰāϤāĻŋāĻāĻŋ āĻā§āϝāĻžāĻ āĻĒāϰāĻŋāϏāϰ⧠āĻāĻāĻāĻŋ āĻ āύāύā§āϝ āĻŽāĻžāύ āĻĒāĻžāĻŦā§ [0, nbTiles]āĨ¤ āĻļā§āύā§āϝ āĻāĻāĻāĻŋ āĻāĻžāϞāĻŋ āĻŦāϰā§āĻ āύāĻŋāϰā§āĻĻā§āĻļ āĻāϰā§;
- blankPos â āĻāĻžāϞāĻŋ āĻŦāϰā§āĻāĻā§āώā§āϤā§āϰā§āϰ āĻ āĻŦāϏā§āĻĨāĻžāύāĨ¤
āĻā§āϞāĻž āϝā§āĻā§āϤāĻŋ
āĻāĻŽāĻžāĻĻā§āϰ āĻāĻāĻāĻŋ āύāϤā§āύ āĻā§āĻŽ āĻĒāĻāĻŋāĻļāύ āĻļā§āϰ⧠āĻāϰāĻžāϰ āĻāύā§āϝ āĻŦā§āϝāĻŦāĻšā§āϤ āĻāĻāĻāĻŋ āϰāĻŋāϏā§āĻ āĻĒāĻĻā§āϧāϤāĻŋ āϏāĻāĻā§āĻāĻžāϝāĻŧāĻŋāϤ āĻāϰāϤ⧠āĻšāĻŦā§āĨ¤ āĻāĻāĻāĻžāĻŦā§ āĻāĻŽāϰāĻž āĻā§āϝāĻžāĻ āĻ ā§āϝāĻžāϰā§āϰ āĻĒā§āϰāϤāĻŋāĻāĻŋ āĻāĻĒāĻžāĻĻāĻžāύā§āϰ āĻāύā§āϝ āĻāĻāĻāĻŋ āĻŽāĻžāύ āϏā§āĻ āĻāϰāĻŋāĨ¤ āĻāĻā§āĻāĻž, āϤāĻžāĻšāϞ⧠āĻāĻŽāϰāĻž āĻ ā§āϝāĻžāϰā§āϰ āĻļā§āώ āĻ āĻŦāϏā§āĻĨāĻžāύ⧠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;āĻŽāĻžāϰā§āĻāĻŋāύ āĻšāϞ āĻā§āĻŽ āĻāύāϏā§āĻā§āϰāĻžāĻā§āĻāϰ⧠āĻāĻāĻāĻŋ āĻĒā§āϝāĻžāϰāĻžāĻŽāĻŋāĻāĻžāϰ āϏā§āĻāĨ¤
āĻāĻāύ āĻāĻŽāĻžāĻĻā§āϰ āϏā§āĻā§āϰāĻŋāύ⧠āĻā§āϰāĻŋāĻĄ āĻāĻŦāĻ āĻĻāĻžāĻ āĻāĻāĻāĻžāϰ āĻāύā§āϝ 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);
}
}āĻ āĻŦāĻļā§āώā§, āĻĒā§āĻāύā§āĻ āĻāĻŽā§āĻĒā§āύā§āύā§āĻ āĻĒāĻĻā§āϧāϤāĻŋāĻā§ āĻāĻāĻžāϰāϰāĻžāĻāĻĄ āĻāϰāĻž āϝāĻžāĻ, āϝāĻž 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);
});
}
}āĻ āĻŦāĻļā§āώā§, āĻā§āϞāĻž āϝāĻžāĻ!
āĻāĻāĻŋ āĻā§āĻŽāĻāĻŋ āĻāĻžāϞ⧠āĻāϰāĻžāϰ āĻāĻŦāĻ āĻāĻāĻŋāĻā§ āĻāĻžāϰā§āϝāĻāϰāĻāĻžāĻŦā§ āĻĒāϰā§āĻā§āώāĻž āĻāϰāĻžāϰ āϏāĻŽāϝāĻŧāĨ¤ āĻā§āώā§āϤā§āϰāĻāĻŋ āĻāĻāϰāĻāĻŽ āĻšāĻāϝāĻŧāĻž āĻāĻāĻŋāϤ:

āĻāϏā§āύ āϧāĻžāĻāϧāĻž āϏāĻŽāĻžāϧāĻžāύ āĻāϰāĻžāϰ āĻā§āώā§āĻāĻž āĻāϰāĻŋāĨ¤ āϏāĻŦāĻāĻŋāĻā§ āĻ āĻŋāĻāĻ āĻžāĻ āĻĨāĻžāĻāϞā§, āĻāĻŽāϰāĻž āĻāĻāĻŋ āĻĒā§āϤ⧠āĻĒāĻžāϰāĻŋ:

āĻāĻāĻžāύā§āĻ āĻļā§āώ. āĻāĻĒāύāĻŋ āĻāĻŋ āĻāϰ⧠āĻāĻļāĻž āĻāϰā§āĻāĻŋāϞā§āύ? đ
Skillbox āϏā§āĻĒāĻžāϰāĻŋāĻļ āĻāϰā§:
- āĻŦā§āϝāĻŦāĻšāĻžāϰāĻŋāĻ āĻā§āϰā§āϏ .
- āĻ āύāϞāĻžāĻāύ āĻā§āϰā§āϏā§āϰ āĻāĻŦā§āĻĻāύ āĻāϰāĻž āĻšāϝāĻŧā§āĻā§ .
- āĻĻā§āĻ āĻŦāĻāϰā§āϰ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻŋāĻ āĻā§āϰā§āϏ .
āĻāϤā§āϏ: www.habr.com
