рдЬрд╛рд╡рд╛ рдореЗрдВ "рдЯреИрдЧ" - рдПрдХ рдкреВрд░реНрдг рдЧреЗрдо рдХреИрд╕реЗ рд╡рд┐рдХрд╕рд┐рдд рдХрд░реЗрдВ

рдЬрд╛рд╡рд╛ рдореЗрдВ "рдЯреИрдЧ" - рдПрдХ рдкреВрд░реНрдг рдЧреЗрдо рдХреИрд╕реЗ рд╡рд┐рдХрд╕рд┐рдд рдХрд░реЗрдВ

"рдкрдВрджреНрд░рд╣" рдпрд╛ "рдкрдВрджреНрд░рд╣" рдПрдХ рд╕рд░рд▓ рддрд░реНрдХ рдЦреЗрд▓ рдХрд╛ рдПрдХ рдЙрддреНрдХреГрд╖реНрдЯ рдЙрджрд╛рд╣рд░рдг рд╣реИ рдЬреЛ рдкреВрд░реА рджреБрдирд┐рдпрд╛ рдореЗрдВ рд▓реЛрдХрдкреНрд░рд┐рдп рд╣реИред рдкрд╣реЗрд▓реА рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рд╡рд░реНрдЧреЛрдВ рдХреЛ рдЫреЛрдЯреЗ рд╕реЗ рдмрдбрд╝реЗ рддрдХ рдХреЗ рдХреНрд░рдо рдореЗрдВ рд╕рдВрдЦреНрдпрд╛рдУрдВ рдХреЗ рд╕рд╛рде рд╡реНрдпрд╡рд╕реНрдерд┐рдд рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдпрд╣ рдЖрд╕рд╛рди рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рджрд┐рд▓рдЪрд╕реНрдк рд╣реИ.

рдЖрдЬ рдХреЗ рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рдореЗрдВ рд╣рдо рдЖрдкрдХреЛ рджрд┐рдЦрд╛рдПрдВрдЧреЗ рдХрд┐ рдПрдХреНрд▓рд┐рдкреНрд╕ рдХреЗ рд╕рд╛рде рдЬрд╛рд╡рд╛ 8 рдореЗрдВ рдлрд┐рдлреНрдЯреАрди рдХреИрд╕реЗ рд╡рд┐рдХрд╕рд┐рдд рдХрд░реЗрдВред рдпреВрдЖрдИ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдо рд╕реНрд╡рд┐рдВрдЧ рдПрдкреАрдЖрдИ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред

рдЕрдиреБрд╕реНрдорд╛рд░рдХ: "рд╣реИрдмрд░" рдХреЗ рд╕рднреА рдкрд╛рдардХреЛрдВ рдХреЗ рд▓рд┐рдП - "рд╣реИрдмрд░" рдкреНрд░рдЪрд╛рд░ рдХреЛрдб рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдХрд┐рд╕реА рднреА рд╕реНрдХрд┐рд▓рдмреЙрдХреНрд╕ рдкрд╛рдареНрдпрдХреНрд░рдо рдореЗрдВ рдирд╛рдорд╛рдВрдХрди рдХрд░рддреЗ рд╕рдордп 10 рд░реВрдмрд▓ рдХреА рдЫреВрдЯред

рд╕реНрдХрд┐рд▓рдмреЙрдХреНрд╕ рдЕрдиреБрд╢рдВрд╕рд╛ рдХрд░рддрд╛ рд╣реИ: рд╢реИрдХреНрд╖рд┐рдХ рдСрдирд▓рд╛рдЗрди рдкрд╛рдареНрдпрдХреНрд░рдо "рдкреЗрд╢рд╛ рдЬрд╛рд╡рд╛ рдбреЗрд╡рд▓рдкрд░".

рдЧреЗрдо рдбрд┐рдЬрд╛рдЗрди

рдЗрд╕ рд╕реНрддрд░ рдкрд░ рдЖрдкрдХреЛ рдЧреБрдгреЛрдВ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ:

  • рдЖрдХрд╛рд░ - рдЦреЗрд▓ рдХреЗ рдореИрджрд╛рди рдХрд╛ рдЖрдХрд╛рд░;
  • nbTiles - рдлрд╝реАрд▓реНрдб рдореЗрдВ рдЯреИрдЧ рдХреА рд╕рдВрдЦреНрдпрд╛ред nbTiles = рдЖрдХрд╛рд░*рдЖрдХрд╛рд░ - 1;
  • рдЯрд╛рдЗрд▓реНрд╕ рдПрдХ рдЯреИрдЧ рд╣реИ рдЬреЛ рдкреВрд░реНрдгрд╛рдВрдХреЛрдВ рдХреА рдПрдХ-рдЖрдпрд╛рдореА рд╕рд░рдгреА рд╣реИред рдкреНрд░рддреНрдпреЗрдХ рдЯреИрдЧ рдХреЛ [0, nbTiles] рд╢реНрд░реЗрдгреА рдореЗрдВ рдПрдХ рдЕрджреНрд╡рд┐рддреАрдп рдорд╛рди рдкреНрд░рд╛рдкреНрдд рд╣реЛрдЧрд╛ред рд╢реВрдиреНрдп рдПрдХ рдЦрд╛рд▓реА рд╡рд░реНрдЧ рдХреЛ рдЗрдВрдЧрд┐рдд рдХрд░рддрд╛ рд╣реИ;
  • рдмреНрд▓реИрдВрдХрдкреЛрдЬрд╝ - рдЦрд╛рд▓реА рд╡рд░реНрдЧ рдХреА рд╕реНрдерд┐рддрд┐ред

рдЦреЗрд▓ рддрд░реНрдХ

рд╣рдореЗрдВ рдПрдХ рдирдИ рдЧреЗрдо рд╕реНрдерд┐рддрд┐ рдЖрд░рдВрдн рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХреА рдЬрд╛рдиреЗ рд╡рд╛рд▓реА рд░реАрд╕реЗрдЯ рд╡рд┐рдзрд┐ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдЗрд╕ рддрд░рд╣ рд╣рдо рдЯреИрдЧ рд╕рд░рдгреА рдХреЗ рдкреНрд░рддреНрдпреЗрдХ рддрддреНрд╡ рдХреЗ рд▓рд┐рдП рдПрдХ рдорд╛рди рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рддреЗ рд╣реИрдВред рдареАрдХ рд╣реИ, рдлрд┐рд░ рд╣рдо рд░рд┐рдХреНрддрдкреЛрдЬрд╝ рдХреЛ рд╕рд░рдгреА рдХреА рдЕрдВрддрд┐рдо рд╕реНрдерд┐рддрд┐ рдореЗрдВ рд░рдЦрддреЗ рд╣реИрдВред

рдЯреИрдЧ рдХреА рд╢реНрд░реГрдВрдЦрд▓рд╛ рдореЗрдВ рдлреЗрд░рдмрджрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдореЗрдВ рдПрдХ рдлреЗрд░рдмрджрд▓ рд╡рд┐рдзрд┐ рдХреА рднреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рд╣рдо рдЦрд╛рд▓реА рдЯреИрдЧ рдХреЛ рдЙрд╕реА рд╕реНрдерд┐рддрд┐ рдореЗрдВ рдЫреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рд╢рдлрд╝рд▓рд┐рдВрдЧ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рд╢рд╛рдорд┐рд▓ рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВред

рдЪреВрдВрдХрд┐ рдкрд╣реЗрд▓реА рдХреА рдХреЗрд╡рд▓ рдЖрдзреА рд╕рдВрднрд╛рд╡рд┐рдд рд╢реБрд░реБрдЖрддреА рд╕реНрдерд┐рддрд┐рдпреЛрдВ рдореЗрдВ рд╣реА рд╕рдорд╛рдзрд╛рди рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЖрдкрдХреЛ рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░рд┐рдгрд╛рдореА рдлреЗрд░рдмрджрд▓ рдкрд░рд┐рдгрд╛рдо рдХреА рдЬрд╛рдВрдЪ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдХрд┐ рд╡рд░реНрддрдорд╛рди рд▓реЗрдЖрдЙрдЯ рднреА рд╣рд▓ рдХрд░рдиреЗ рдпреЛрдЧреНрдп рд╣реИред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо isSolvable рд╡рд┐рдзрд┐ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддреЗ рд╣реИрдВред

рдпрджрд┐ рдХрд┐рд╕реА рд╡рд┐рд╢реЗрд╖ рдЯреИрдЧ рдХреЗ рдкрд╣рд▓реЗ рдЙрдЪреНрдЪ рдорд╛рди рд╡рд╛рд▓рд╛ рдЯреИрдЧ рд▓рдЧрд╛ рд╣реЛ рддреЛ рдЗрд╕реЗ рд╡реНрдпреБрддреНрдХреНрд░рдордг рдорд╛рдирд╛ рдЬрд╛рддрд╛ рд╣реИред рдЬрдм рдЦрд╛рд▓реА рд╕реНрдерд╛рди рдЕрдкрдиреА рдЬрдЧрд╣ рдкрд░ рд╣реЛ, рддреЛ рдкрд╣реЗрд▓реА рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╡реНрдпреБрддреНрдХреНрд░рдореЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╕рдо рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдПред рдЗрд╕рд▓рд┐рдП рд╣рдо рд╡реНрдпреБрддреНрдХреНрд░рдореЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рдЧрд┐рдирддреЗ рд╣реИрдВ рдФрд░ рдпрджрд┐ рд╕рдВрдЦреНрдпрд╛ рд╕рдо рд╣реИ рддреЛ рд╕рддреНрдп рд▓реМрдЯрд╛рддреЗ рд╣реИрдВред

рдпрд╣ рдЬрд╛рдВрдЪрдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рдХреНрдпрд╛ рд╣рдорд╛рд░рд╛ рдЧреЗрдо рдСрдл рдлрд┐рдлреНрдЯреАрди рд▓реЗрдЖрдЙрдЯ рд╣рд▓ рд╣реЛ рдЧрдпрд╛ рд╣реИ, isSolven рдкрджреНрдзрддрд┐ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдирд╛ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИред рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ рд╣рдо рдпрд╣ рджреЗрдЦрддреЗ рд╣реИрдВ рдХрд┐ рдЦрд╛рд▓реА рдЬрдЧрд╣ рдХрд╣рд╛рдВ рд╣реИред рдпрджрд┐ рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд╕реНрдерд┐рддрд┐ рдореЗрдВ рд╣реИ, рддреЛ рд╡рд░реНрддрдорд╛рди рд╕рдВрд░реЗрдЦрдг рдирдпрд╛ рд╣реИ, рдкрд╣рд▓реЗ рд╕реЗ рддрдп рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдлрд┐рд░ рд╣рдо рдЯрд╛рдЗрд▓реНрд╕ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЙрд▓реНрдЯреЗ рдХреНрд░рдо рдореЗрдВ рдкреБрдирд░рд╛рд╡реГрддрд┐ рдХрд░рддреЗ рд╣реИрдВ, рдФрд░ рдпрджрд┐ рдЯреИрдЧ рдХрд╛ рдорд╛рди рд╕рдВрдмрдВрдзрд┐рдд рд╕реВрдЪрдХрд╛рдВрдХ +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;

рд╣рдо рд╕реНрд╡рд┐рдВрдЧ рдПрдкреАрдЖрдИ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдпреВрдЖрдИ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рддреЗ рд╣реИрдВ

рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдкрд░ рдХрд╛рдо рд╢реБрд░реВ рдХрд░рдиреЗ рдХрд╛ рд╕рдордп рдЖ рдЧрдпрд╛ рд╣реИред рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ рд╣рдо 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);
}

рдпреВрдЖрдИ рдореЗрдВ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдЧрддрд┐рд╡рд┐рдзрд┐рдпреЛрдВ рдкрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХрд░рдирд╛

рдЧреЗрдо рдХреЛ рдЕрдкрдиреЗ рдкрд╛рдареНрдпрдХреНрд░рдо рдореЗрдВ рдЪрд▓рд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рдпреВрдЖрдИ рдореЗрдВ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, 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 рдХреНрд▓рд╛рд╕ рдХреЗ рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рдореЗрдВ рд░рдЦрддреЗ рд╣реИрдВред рдЕрдВрдд рдореЗрдВ, рд╣рдо рдПрдХ рдирдпрд╛ рдЧреЗрдо рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдиреНрдпреВрдЧреЗрдо рд╡рд┐рдзрд┐ рдХреЛ рдХреЙрд▓ рдХрд░рддреЗ рд╣реИрдВред

рдкреВрд░рд╛ рдЧреЗрдо рдХреЛрдб

рдЧреЗрдо рдХреЛ рдХреНрд░рд┐рдпрд╛рдиреНрд╡рд┐рдд рд╣реЛрддреЗ рджреЗрдЦрдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рдЕрдВрддрд┐рдо рдЪрд░рдг рд╕рднреА рдХреЛрдб рддрддреНрд╡реЛрдВ рдХреЛ рдПрдХ рд╕рд╛рде рд░рдЦрдирд╛ рд╣реИред рдпрд╣рд╛рдБ рдХреНрдпрд╛ рд╣реЛрддрд╛ рд╣реИ:

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

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдЬреЛрдбрд╝реЗрдВ