Java์˜ "ํƒœ๊ทธ" - ๋ณธ๊ฒฉ์ ์ธ ๊ฒŒ์ž„์„ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐฉ๋ฒ•

Java์˜ "ํƒœ๊ทธ" - ๋ณธ๊ฒฉ์ ์ธ ๊ฒŒ์ž„์„ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐฉ๋ฒ•

"์—ด๋‹ค์„ฏ" ๋˜๋Š” "์—ด๋‹ค์„ฏ" ์ „ ์„ธ๊ณ„์ ์œผ๋กœ ์ธ๊ธฐ๋ฅผ ๋Œ๊ณ  ์žˆ๋Š” ๊ฐ„๋‹จํ•œ ๋…ผ๋ฆฌ ๊ฒŒ์ž„์˜ ํ›Œ๋ฅญํ•œ ์˜ˆ์ž…๋‹ˆ๋‹ค. ํผ์ฆ์„ ํ’€๋ ค๋ฉด ์ˆซ์ž๊ฐ€ ์žˆ๋Š” ์‚ฌ๊ฐํ˜•์„ ๊ฐ€์žฅ ์ž‘์€ ๊ฒƒ๋ถ€ํ„ฐ ๊ฐ€์žฅ ํฐ ๊ฒƒ๊นŒ์ง€ ์ˆœ์„œ๋Œ€๋กœ ๋ฐฐ์—ดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‰ฝ์ง€๋Š” ์•Š์ง€๋งŒ ํฅ๋ฏธ๋กญ์Šต๋‹ˆ๋‹ค.

์˜ค๋Š˜์˜ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” Eclipse๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Java 8์—์„œ Fifteen์„ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. UI๋ฅผ ๊ฐœ๋ฐœํ•˜๊ธฐ ์œ„ํ•ด Swing API๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์•Œ๋ฆผ: "Habr"์˜ ๋ชจ๋“  ๋…์ž๋ฅผ ์œ„ํ•œ - "Habr" ํ”„๋กœ๋ชจ์…˜ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Skillbox ๊ณผ์ •์— ๋“ฑ๋กํ•  ๋•Œ 10 ๋ฃจ๋ธ” ํ• ์ธ.

Skillbox๋Š” ๋‹ค์Œ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ต์œก์šฉ ์˜จ๋ผ์ธ ๊ณผ์ • "์ง์—… ์ž๋ฐ” ๊ฐœ๋ฐœ์ž".

๊ฒŒ์ž„ ๋””์ž์ธ

์ด ๋‹จ๊ณ„์—์„œ๋Š” ์†์„ฑ์„ ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ํฌ๊ธฐ - ๊ฒฝ๊ธฐ์žฅ์˜ ํฌ๊ธฐ์ž…๋‹ˆ๋‹ค.
  • nbTiles โ€” ํ•„๋“œ์˜ ํƒœ๊ทธ ์ˆ˜์ž…๋‹ˆ๋‹ค. nbTiles = ํฌ๊ธฐ*ํฌ๊ธฐ - 1;
  • ํƒ€์ผ์€ ์ •์ˆ˜์˜ 0์ฐจ์› ๋ฐฐ์—ด์ธ ํƒœ๊ทธ์ž…๋‹ˆ๋‹ค. ๊ฐ ํƒœ๊ทธ๋Š” [XNUMX, nbTiles] ๋ฒ”์œ„์˜ ๊ณ ์œ ํ•œ ๊ฐ’์„ ๋ฐ›์Šต๋‹ˆ๋‹ค. XNUMX์€ ๋นˆ ์‚ฌ๊ฐํ˜•์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
  • BlankPos โ€” ๋นˆ ์‚ฌ๊ฐํ˜•์˜ ์œ„์น˜์ž…๋‹ˆ๋‹ค.

๊ฒŒ์ž„ ๋กœ์ง

์ƒˆ๋กœ์šด ๊ฒŒ์ž„ ์œ„์น˜๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ์žฌ์„ค์ • ๋ฐฉ๋ฒ•์„ ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์œผ๋กœ ํƒœ๊ทธ ๋ฐฐ์—ด์˜ ๊ฐ ์š”์†Œ์— ๋Œ€ํ•œ ๊ฐ’์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๊ณต๋ฐฑ์„ ๋ฐฐ์—ด์˜ ๋งˆ์ง€๋ง‰ ์œ„์น˜์— ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ํƒœ๊ทธ ๋ฐฐ์—ด์„ ์„ž๋Š” shuffle ๋ฉ”์†Œ๋“œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋™์ผํ•œ ์œ„์น˜์— ๋‘๊ธฐ ์œ„ํ•ด ์…”ํ”Œ๋ง ๊ณผ์ •์—์„œ ๋นˆ ํƒœ๊ทธ๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํผ์ฆ์˜ ๊ฐ€๋Šฅํ•œ ์‹œ์ž‘ ์œ„์น˜ ์ค‘ ์ ˆ๋ฐ˜์—๋งŒ ๋‹ต์ด ์žˆ์œผ๋ฏ€๋กœ ๊ฒฐ๊ณผ ์„ž๊ธฐ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜์—ฌ ํ˜„์žฌ ๋ ˆ์ด์•„์›ƒ์ด ํ’€ ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด isSolvable ๋ฉ”์†Œ๋“œ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

ํŠน์ • ํƒœ๊ทธ ์•ž์— ๋” ๋†’์€ ๊ฐ’์„ ๊ฐ€์ง„ ํƒœ๊ทธ๊ฐ€ ์˜ค๋ฉด ๋ฐ˜์ „์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค. ๋นˆ ์ž๋ฆฌ๊ฐ€ ์ œ์ž๋ฆฌ์— ์žˆ์„ ๋•Œ ํผ์ฆ์„ ํ’€ ์ˆ˜ ์žˆ์œผ๋ ค๋ฉด ๋ฐ˜์ „ ํšŸ์ˆ˜๊ฐ€ ๊ท ๋“ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฐ˜์ „ ํšŸ์ˆ˜๋ฅผ ์„ธ๊ณ  ์ˆซ์ž๊ฐ€ ์ง์ˆ˜์ด๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ Game Of 1 ๋ ˆ์ด์•„์›ƒ์ด ํ•ด๊ฒฐ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด isSolved ๋ฉ”์†Œ๋“œ๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๋จผ์ € ๋นˆ ์ž๋ฆฌ๊ฐ€ ์–ด๋””์— ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ดˆ๊ธฐ ์œ„์น˜์— ์žˆ๋Š” ๊ฒฝ์šฐ ํ˜„์žฌ ์ •๋ ฌ์€ ์ด์ „์— ๊ฒฐ์ •๋˜์ง€ ์•Š์€ ์ƒˆ๋กœ์šด ์ •๋ ฌ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ํƒ€์ผ์„ ์—ญ์ˆœ์œผ๋กœ ๋ฐ˜๋ณตํ•˜๊ณ  ํƒœ๊ทธ ๊ฐ’์ด ํ•ด๋‹น ์ธ๋ฑ์Šค +XNUMX๊ณผ ๋‹ค๋ฅด๋ฉด 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;
}

๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฐฐ์—ด์—์„œ ํƒœ๊ทธ์˜ ์ด๋™์„ ํ”„๋กœ๊ทธ๋ž˜๋ฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๋Š” ์ปค์„œ ์ด๋™์— ์‘๋‹ตํ•˜๊ธฐ ์œ„ํ•ด ๋‚˜์ค‘์— ์ฝœ๋ฐฑ์„ ํ†ตํ•ด ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ ๊ฒŒ์ž„์€ ๋™์‹œ์— ์—ฌ๋Ÿฌ ํƒ€์ผ ์ด๋™์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ™”๋ฉด์—์„œ ๋ˆ„๋ฅธ ์œ„์น˜๋ฅผ ํƒœ๊ทธ๋กœ ๋ณ€ํ™˜ํ•œ ํ›„ ๋นˆ ํƒœ๊ทธ์˜ ์œ„์น˜๋ฅผ โ€‹โ€‹๊ฐ€์ ธ์˜ค๊ณ  ๋™์‹œ์— ์—ฌ๋Ÿฌ ๋™์ž‘์„ ์ง€์›ํ•˜๋Š” ์ด๋™ ๋ฐฉํ–ฅ์„ ์ฐพ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ์˜ˆ์ œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

// 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๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ 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 ํด๋ž˜์Šค์—์„œ ํŒŒ์ƒ๋˜๋Š” PaintComponent ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ์ •์˜ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ 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์—์„œ ์‚ฌ์šฉ์ž ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด 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);
    });
  }
 
 
}

๋งˆ์ง€๋ง‰์œผ๋กœ ๋†€์ž!

์ด์ œ ๊ฒŒ์ž„์„ ์‹œ์ž‘ํ•˜๊ณ  ์‹ค์ œ๋กœ ํ…Œ์ŠคํŠธํ•ด ๋ณผ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค. ํ•„๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Java์˜ "ํƒœ๊ทธ" - ๋ณธ๊ฒฉ์ ์ธ ๊ฒŒ์ž„์„ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐฉ๋ฒ•

ํผ์ฆ์„ ํ’€์–ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฒƒ์ด ์ž˜ ์ง„ํ–‰๋˜์—ˆ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Java์˜ "ํƒœ๊ทธ" - ๋ณธ๊ฒฉ์ ์ธ ๊ฒŒ์ž„์„ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐฉ๋ฒ•

๊ทธ๊ฒŒ ๋‹ค์•ผ. ๋” ๋งŽ์€ ๊ฒƒ์„ ๊ธฐ๋Œ€ํ•˜์…จ๋‚˜์š”? ๐Ÿ™‚

Skillbox๋Š” ๋‹ค์Œ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€