เจœเจพเจตเจพ เจตเจฟเฉฑเจš "เจŸเฉˆเจ—" - เจ‡เฉฑเจ• เจชเฉ‚เจฐเฉ€ เจ–เฉ‡เจก เจจเฉ‚เฉฐ เจ•เจฟเจตเฉ‡เจ‚ เจตเจฟเจ•เจธเจฟเจค เจ•เจฐเจจเจพ เจนเฉˆ

เจœเจพเจตเจพ เจตเจฟเฉฑเจš "เจŸเฉˆเจ—" - เจ‡เฉฑเจ• เจชเฉ‚เจฐเฉ€ เจ–เฉ‡เจก เจจเฉ‚เฉฐ เจ•เจฟเจตเฉ‡เจ‚ เจตเจฟเจ•เจธเจฟเจค เจ•เจฐเจจเจพ เจนเฉˆ

"เจชเฉฐเจฆเจฐเจพเจ‚" เจœเจพเจ‚ "เจชเฉฐเจฆเจฐเจพเจ‚" เจ‡เฉฑเจ• เจธเจงเจพเจฐเจจ เจคเจฐเจ• เจฆเฉ€ เจ–เฉ‡เจก เจฆเจพ เจ‡เฉฑเจ• เจธเจผเจพเจจเจฆเจพเจฐ เจ‰เจฆเจพเจนเจฐเจจ เจนเฉˆ เจœเฉ‹ เจชเฉ‚เจฐเฉ€ เจฆเฉเจจเฉ€เจ† เจตเจฟเฉฑเจš เจชเฉเจฐเจธเจฟเฉฑเจง เจนเฉˆเฅค เจฌเฉเจเจพเจฐเจค เจจเฉ‚เฉฐ เจธเฉเจฒเจเจพเจ‰เจฃ เจฒเจˆ, เจคเฉเจนเจพเจจเฉ‚เฉฐ เจธเจญ เจคเฉ‹เจ‚ เจ›เฉ‹เจŸเฉ‡ เจคเฉ‹เจ‚ เจตเฉฑเจกเฉ‡ เจคเฉฑเจ•, เจจเฉฐเจฌเจฐเจพเจ‚ เจฆเฉ‡ เจจเจพเจฒ เจตเจฐเจ—เจพเจ‚ เจจเฉ‚เฉฐ เจตเจฟเจตเจธเจฅเจฟเจค เจ•เจฐเจจ เจฆเฉ€ เจฒเฉ‹เฉœ เจนเฉˆเฅค เจ‡เจน เจ†เจธเจพเจจ เจจเจนเฉ€เจ‚ เจนเฉˆ, เจชเจฐ เจ‡เจน เจฆเจฟเจฒเจšเจธเจช เจนเฉˆเฅค

เจ…เฉฑเจœ เจฆเฉ‡ เจŸเจฟเจŠเจŸเฉ‹เจฐเจฟเจ…เจฒ เจตเจฟเฉฑเจš เจ…เจธเฉ€เจ‚ เจคเฉเจนเจพเจจเฉ‚เฉฐ เจฆเจฟเจ–เจพเจตเจพเจ‚เจ—เฉ‡ เจ•เจฟ เจ•เจฟเจตเฉ‡เจ‚ Eclipse เจฆเฉ‡ เจจเจพเจฒ Java 8 เจตเจฟเฉฑเจš Fifteen เจจเฉ‚เฉฐ เจตเจฟเจ•เจธเจฟเจค เจ•เจฐเจจเจพ เจนเฉˆเฅค UI เจจเฉ‚เฉฐ เจตเจฟเจ•เจธเจค เจ•เจฐเจจ เจฒเจˆ เจ…เจธเฉ€เจ‚ เจธเจตเจฟเฉฐเจ— API เจฆเฉ€ เจตเจฐเจคเฉ‹เจ‚ เจ•เจฐเจพเจ‚เจ—เฉ‡เฅค

เจ…เจธเฉ€เจ‚ เจฏเจพเจฆ เจฆเจฟเจตเจพเจ‰เจ‚เจฆเฉ‡ เจนเจพเจ‚: "Habr" เจฆเฉ‡ เจธเจพเจฐเฉ‡ เจชเจพเจ เจ•เจพเจ‚ เจฒเจˆ - "Habr" เจชเฉเจฐเจšเจพเจฐ เจ•เฉ‹เจก เจฆเฉ€ เจตเจฐเจคเฉ‹เจ‚ เจ•เจฐเจฆเฉ‡ เจนเฉ‹เจ เจ•เจฟเจธเฉ‡ เจตเฉ€ เจธเจ•เจฟเฉฑเจฒเจฌเจพเจ•เจธ เจ•เฉ‹เจฐเจธ เจตเจฟเฉฑเจš เจฆเจพเจ–เจฒเจพ เจฒเฉˆเจฃ เจตเฉ‡เจฒเฉ‡ 10 เจฐเฉ‚เจฌเจฒ เจฆเฉ€ เจ›เฉ‹เจŸเฅค

เจธเจ•เจฟเฉฑเจฒเจฌเจพเจ•เจธ เจธเจฟเจซเจผเจพเจฐเจฟเจธเจผ เจ•เจฐเจฆเจพ เจนเฉˆ: เจตเจฟเจฆเจฟเจ…เจ• เจ”เจจเจฒเจพเจˆเจจ เจ•เฉ‹เจฐเจธ "เจชเฉเจฐเฉ‹เจซเฉˆเจธเจผเจจ เจœเจพเจตเจพ เจกเจฟเจตเฉˆเจฒเจชเจฐ".

เจ–เฉ‡เจก เจกเจฟเจœเจผเจพเจˆเจจ

เจ‡เจธ เจชเฉœเจพเจ… 'เจคเฉ‡ เจคเฉเจนเจพเจจเฉ‚เฉฐ เจตเจฟเจธเจผเฉ‡เจธเจผเจคเจพเจตเจพเจ‚ เจจเฉ‚เฉฐ เจชเจฐเจฟเจญเจพเจธเจผเจฟเจค เจ•เจฐเจจ เจฆเฉ€ เจฒเฉ‹เฉœ เจนเฉˆ:

  • เจ†เจ•เจพเจฐ - เจ–เฉ‡เจกเจฃ เจฆเฉ‡ เจฎเฉˆเจฆเจพเจจ เจฆเจพ เจ†เจ•เจพเจฐ;
  • nbTiles โ€” เจ–เฉ‡เจคเจฐ เจตเจฟเฉฑเจš เจŸเฉˆเจ— เจฆเฉ€ เจ—เจฟเจฃเจคเฉ€. nbTiles = เจ†เจ•เจพเจฐ* เจ†เจ•เจพเจฐ - 1;
  • เจŸเจพเจ‡เจฒเจธ เจ‡เฉฑเจ• เจŸเฉˆเจ— เจนเฉˆ เจœเฉ‹ เจชเฉ‚เจฐเจจ เจ…เฉฐเจ•เจพเจ‚ เจฆเฉ€ เจ‡เฉฑเจ•-เจ…เจฏเจพเจฎเฉ€ เจเจฐเฉ‡ เจนเฉˆเฅค เจนเจฐเฉ‡เจ• เจŸเฉˆเจ— เจจเฉ‚เฉฐ เจธเฉ€เจฎเจพ [0, nbTiles] เจตเจฟเฉฑเจš เจ‡เฉฑเจ• เจตเจฟเจฒเฉฑเจ–เจฃ เจฎเฉเฉฑเจฒ เจชเฉเจฐเจพเจชเจค เจนเฉ‹เจตเฉ‡เจ—เจพเฅค เจœเจผเฉ€เจฐเฉ‹ เจ‡เฉฑเจ• เจ–เจพเจฒเฉ€ เจตเจฐเจ— เจฆเจฐเจธเจพเจ‰เจ‚เจฆเจพ เจนเฉˆ;
  • 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;

เจฎเจพเจฐเจœเจฟเจจ เจ—เฉ‡เจฎ เจ•เฉฐเจธเจŸเจฐเจ•เจŸเจฐ เจตเจฟเฉฑเจš เจ‡เฉฑเจ• เจชเฉˆเจฐเจพเจฎเฉ€เจŸเจฐ เจธเฉˆเฉฑเจŸ เจตเฉ€ เจนเฉˆเฅค

เจนเฉเจฃ เจธเจพเจจเฉ‚เฉฐ เจธเจ•เจฐเฉ€เจจ 'เจคเฉ‡ เจ—เจฐเจฟเฉฑเจก เจ…เจคเฉ‡ เจธเจชเฉŒเจŸเจธ เจจเฉ‚เฉฐ เจ–เจฟเฉฑเจšเจฃ เจฒเจˆ เจกเจฐเจพเจ…เจ—เฉเจฐเจฟเจก เจตเจฟเจงเฉ€ เจจเฉ‚เฉฐ เจชเจฐเจฟเจญเจพเจธเจผเจฟเจค เจ•เจฐเจจ เจฆเฉ€ เจฒเฉ‹เฉœ เจนเฉˆเฅค เจ…เจธเฉ€เจ‚ เจŸเฉˆเจ—เจธ เจฆเฉ€ เจเจฐเฉ‡ เจฆเจพ เจตเจฟเจธเจผเจฒเฉ‡เจธเจผเจฃ เจ•เจฐเจฆเฉ‡ เจนเจพเจ‚ เจ…เจคเฉ‡ เจ•เฉ‹เจ†เจฐเจกเฉ€เจจเฉ‡เจŸเจธ เจจเฉ‚เฉฐ เจฏเฉ‚เจœเจผเจฐ เจ‡เฉฐเจŸเจฐเจซเฉ‡เจธ เจ•เฉ‹เจ†เจฐเจกเฉ€เจจเฉ‡เจŸเจธ เจตเจฟเฉฑเจš เจฌเจฆเจฒเจฆเฉ‡ เจนเจพเจ‚เฅค เจซเจฟเจฐ เจ•เฉ‡เจ‚เจฆเจฐ เจตเจฟเฉฑเจš เจ…เจจเฉเจธเจพเจฐเฉ€ เจธเฉฐเจ–เจฟเจ† เจฆเฉ‡ เจจเจพเจฒ เจนเจฐเฉ‡เจ• เจธเจฅเจพเจจ เจจเฉ‚เฉฐ เจ–เจฟเฉฑเจšเฉ‹:

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 เจ•เจฒเจพเจธ เจฆเฉ‡ เจ•เฉฐเจธเจŸเจฐเจ•เจŸเจฐ เจตเจฟเฉฑเจš เจฐเฉฑเจ–เจฆเฉ‡ เจนเจพเจ‚เฅค เจฌเจฟเจฒเจ•เฉเจฒ เจ…เฉฐเจค เจตเจฟเฉฑเจš, เจ…เจธเฉ€เจ‚ เจ‡เฉฑเจ• เจจเจตเฉ€เจ‚ เจ—เฉ‡เจฎ เจธเจผเฉเจฐเฉ‚ เจ•เจฐเจจ เจฒเจˆ เจจเจตเฉ€เจ‚ เจ—เฉ‡เจฎ เจตเจฟเจงเฉ€ เจจเฉ‚เฉฐ เจ•เจพเจฒ เจ•เจฐเจฆเฉ‡ เจนเจพเจ‚เฅค

เจชเฉ‚เจฐเจพ เจ—เฉ‡เจฎ เจ•เฉ‹เจก

เจ—เฉ‡เจฎ เจจเฉ‚เฉฐ เจเจ•เจธเจผเจจ เจตเจฟเฉฑเจš เจฆเฉ‡เจ–เจฃ เจคเฉ‹เจ‚ เจชเจนเจฟเจฒเจพเจ‚ เจ†เจ–เจฐเฉ€ เจ•เจฆเจฎ เจนเฉˆ เจธเจพเจฐเฉ‡ เจ•เฉ‹เจก เจเจฒเฉ€เจฎเฉˆเจ‚เจŸเจธ เจจเฉ‚เฉฐ เจ‡เจ•เฉฑเจ เฉ‡ เจฐเฉฑเจ–เจฃเจพเฅค เจ‡เฉฑเจฅเฉ‡ เจ•เฉ€ เจนเฉเฉฐเจฆเจพ เจนเฉˆ:

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

เจ‡เฉฑเจ• เจŸเจฟเฉฑเจชเจฃเฉ€ เจœเฉ‹เฉœเฉ‹