"Tag" ann an Java - mar a leasaicheas tu geama làn-chuimseach
"Còig-deug" no "Còig-deug" na dheagh eisimpleir de gheama loidsig sìmplidh a tha mòr-chòrdte air feadh an t-saoghail. Gus an tòimhseachan fhuasgladh, feumaidh tu na ceàrnagan a chuir air dòigh le àireamhan ann an òrdugh, bhon fheadhainn as lugha chun as motha. Chan eil e furasta, ach tha e inntinneach.
Anns an oideachadh an-diugh tha sinn a 'sealltainn mar a leasaicheas tu Fifteen ann an Java 8 le Eclipse. Gus an UI a leasachadh cleachdaidh sinn an Swing API.
Tha sinn a ’cur nar cuimhne:airson a h-uile leughadair de "Habr" - lasachadh de 10 rubles nuair a chlàraicheas tu ann an cùrsa sam bith Skillbox a 'cleachdadh a' chòd adhartachaidh "Habr".
Aig an ìre seo feumaidh tu na feartan a mhìneachadh:
Meud - meud an raon-cluiche;
nbTiles - àireamh nan tagaichean san raon. nbTiles = meud * meud - 1;
Is e taga a th’ ann an Tiles a tha na raon aon-thaobhach de shlànaichean. Gheibh gach aon de na tagaichean luach sònraichte anns an raon [0, nbTiles]. Tha neoni a’ comharrachadh ceàrnag falamh;
blankPos - suidheachadh na ceàrnaig fhalamh.
Logic geamannan
Feumaidh sinn dòigh ath-shuidheachadh a mhìneachadh a chleachdar gus suidheachadh geama ùr a thòiseachadh. San dòigh seo shuidhich sinn luach airson gach eileamaid den raon tagaichean. Uill, an uairsin bidh sinn a’ cur Pos bàn san t-suidheachadh mu dheireadh den raon.
Tha feum againn cuideachd air dòigh shuffle airson an raon tagaichean a ghluasad. Cha bhith sinn a’ toirt a-steach an taga falamh sa phròiseas shuffling gus a fàgail san aon suidheachadh.
Leis nach eil fuasgladh aig ach leth de na h-àiteachan tòiseachaidh a dh’ fhaodadh a bhith aig an tòimhseachan, feumaidh tu sgrùdadh a dhèanamh air an toradh shuffle a thig às gus dèanamh cinnteach gu bheil an cruth gnàthach eadhon so-fhuasgladh. Gus seo a dhèanamh, bidh sinn a’ mìneachadh an dòigh isSolvable.
Ma tha tag le luach nas àirde air thoiseach air tag sònraichte, thathas den bheachd gur e tionndadh a th’ ann. Nuair a tha an t-àite falamh na àite, feumaidh an àireamh de thionndadh a bhith eadhon airson an tòimhseachan a bhith furasta a rèiteachadh. Mar sin bidh sinn a’ cunntadh an àireamh de thionndadh agus a’ tilleadh fìor ma tha an àireamh cothromach.
Tha e an uairsin cudromach an dòigh isSolved a mhìneachadh gus faighinn a-mach a bheil an cruth Game Of Fifteen againn air fhuasgladh. An toiseach bheir sinn sùil air far a bheil an t-àite falamh. Ma tha e anns a 'chiad suidheachadh, tha an co-thaobhadh làithreach ùr, nach deach a cho-dhùnadh roimhe. Bidh sinn an uairsin ag ath-aithris tro na leacan ann an òrdugh cùil, agus ma tha luach an taga eadar-dhealaichte bhon chlàr-amais co-fhreagarrach +1, tillidh sinn meallta. Rud eile, aig deireadh an dòigh tha an t-àm ann tilleadh gu fìor leis gu bheil an tòimhseachan air fhuasgladh mu thràth.
Is e dòigh eile a dh’ fheumar a mhìneachadh newGame. Tha e riatanach eisimpleir ùr den gheama a chruthachadh. Gus seo a dhèanamh, bidh sinn ag ath-shuidheachadh an raon cluiche, an uairsin ga ghluasad agus a’ leantainn gus am bi e comasach an suidheachadh cluiche fhuasgladh.
Seo eisimpleir de chòd le prìomh loidsig tag:
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;
}
Mu dheireadh, feumaidh tu gluasad nan tagaichean san raon a phrògramadh. Thèid an còd seo a ghairm nas fhaide air adhart tro ghairm air ais gus freagairt a thoirt do ghluasad cùrsair. Bheir an geama againn taic do ghrunn ghluasadan leac aig an aon àm. Mar sin, às deidh dhuinn an suidheachadh brùthaidh air an sgrion a thionndadh gu taga, gheibh sinn suidheachadh an taga falamh agus coimhead airson treòrachadh gluasaid gus taic a thoirt do ghrunn de na gluasadan aige aig an aon àm.
Seo eisimpleir de chòd:
// 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;
Bidh sinn a’ leasachadh UI a’ cleachdadh Swing API
Tha an t-àm ann a bhith ag obair air an eadar-aghaidh. An toiseach gabhaidh sinn an clas Jpanel. An uairsin bidh sinn a ’tarraing tagaichean air an raon - gus meudan gach fear obrachadh a-mach, cleachdaidh sinn an dàta a tha air a shònrachadh ann am paramadair neach-togail geama:
Tha iomall cuideachd na paramadair suidhichte anns an neach-togail geama.
A-nis feumaidh sinn an dòigh drawGrid a mhìneachadh gus a ’ghriod agus na spotan air an sgrion a tharraing. Bidh sinn a’ dèanamh anailis air an raon de thagaichean agus ag atharrachadh na co-chomharran gu co-chomharran eadar-aghaidh luchd-cleachdaidh. An uairsin tarraing gach spot leis an àireamh fhreagarrach sa mheadhan:
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);
}
}
Mu dheireadh, leig dhuinn a dhol thairis air an dòigh peant Component, a tha a 'tighinn bhon chlas JPane. Bidh sinn an uairsin a’ cleachdadh an dòigh drawGrid, agus an uairsin an dòigh drawStartMessage gus teachdaireachd a thaisbeanadh ag iarraidh oirnn cliogadh gus an geama a thòiseachadh:
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);
}
Freagairt ri gnìomhan luchd-cleachdaidh san UI
Gus an urrainn don gheama a chùrsa a ruith, feumar gnìomhan luchd-cleachdaidh a phròiseasadh san UI. Gus seo a dhèanamh, cuir an gnìomh MouseListener air Jpanel agus an còd airson gluasad spotan, a chithear gu h-àrd mar-thà:
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();
}
});
Bidh sinn a’ cur a’ chòd ann an neach-togail a’ chlas GameOfFifteen. Aig an fhìor dheireadh, is e an dòigh GameGame ùr a chanas sinn ri geama ùr a thòiseachadh.
Còd geamannan air an làn
Is e an ceum mu dheireadh mus faic thu an geama ann an gnìomh na h-eileamaidean còd gu lèir a chuir ri chèile. Seo na thachras:
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);
});
}
}
Mu dheireadh, cluichidh sinn!
Tha an t-àm ann an geama a chuir air bhog agus deuchainn a dhèanamh air ann an gnìomh. Bu chòir don raon coimhead mar seo:
Feuchaidh sinn ri fuasgladh fhaighinn air an tòimhseachan. Ma chaidh a h-uile càil gu math, gheibh sinn seo: