Vaja 05 - Igra 2048

This commit is contained in:
Gašper Dobrovoljc 2024-04-11 12:18:13 +02:00
parent 68e43abf11
commit 9a87b97eb9
No known key found for this signature in database
GPG Key ID: 0E7E037018CFA5A5
4 changed files with 351 additions and 0 deletions

View File

@ -118,6 +118,7 @@ public class DN05 {
return null;
}
int[][] image = new int[height][width];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {

View File

@ -0,0 +1,44 @@
package vaja05.igra;
import javax.swing.*;
import java.awt.*;
/**
* Glavni razred igre, vključuje metodo main (z njim poženemo program).
* Ustvari novo okno, v katerem je igralno polje (privzeto mreža 4 x 4),
* in okno prikaže.
*/
public class Igra2048 extends JFrame {
public Igra2048() {
ustvariGUI();
}
// ustvarjanje uporabniškega vmesnika
private void ustvariGUI() {
// ustvarimo objekt razreda IgralnoPolje (panel, kjer poteka igra)
IgralnoPolje polje = new IgralnoPolje();
// ustvarimo novo okno z naslovom "2048"
setTitle("2048");
// če okno zapremo (klik na x), se program konča
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// onemogoči spreminjanje velikosti okna
setResizable(false);
// v okno postavimo objekt polje, in sicer v središče (uporabimo robno razvrščanje)
setLayout(new BorderLayout());
getContentPane().add(polje, BorderLayout.CENTER);
// poskrbimo za preračun ustrezne velikosti okna glede na njegove komponente
pack();
}
public static void main(String[] args) {
// v ustrezni niti ustvarimo novo okno in ga prikažemo
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Igra2048().setVisible(true);
}
});
}
}

View File

@ -0,0 +1,181 @@
package vaja05.igra;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
/**
* Razred predstavlja igralno polje, tj. mrežo kvadratkov,
* na katere postavimo ploščice s številkami. Poskrbi za
* grafični prikaz uporabniškega vmesnika ter za interakcijož
* z igralcem.
* Igralec lahko pritisne smerne tipke za premikanje ploščic
* po igralnem polju. Tipka ESC zaključi igro, tipka ENTER pa
* začne novo igro.
* Za logiko igre poskrbi razred Logika, katerega metode kličemo
* pri grafičnem prikazu igre (ob odzivu na pritisnjene tipke in
* ob izrisu spremenjenega igralnega polja).
* Ko igralec doseže ciljno ploščico (privzeto 2048), se pod točkami
* izpiše, da je zmagal, in igra se nadaljuje.
*/
public class IgralnoPolje extends JPanel {
private final static int STEVILO_POLJ = 4; // velikost mreže, privzeto je 4 x 4
private final static int VELIKOST_GLAVE = 100; // višina dela za sporočila, kjer se izpisujejo točke (v pikslih)
private final static int VELIKOST_POLJA = 80; // velikost enega polja (v pikslih)
private final static int ROB = 10; // širina roba in razmik med dvemi polji (v pikslih)
private final static int LOK = 10; // zaokroženost kvadratka
private final static int SIRINA = STEVILO_POLJ * (VELIKOST_POLJA + ROB) + ROB; // širina igralne površine (v pikslih)
private final static int VISINA = STEVILO_POLJ * (VELIKOST_POLJA + ROB) + ROB + VELIKOST_GLAVE; // višina igralne površine (v pikslih)
private final static Color BARVA_GLAVE = new Color(0xFA, 0xF8, 0xEF);
private final static Color BARVA_OZADJA = new Color(0xBB, 0xAD, 0xA0);
private final static Color BARVA_KONEC = new Color(0xBFFAA22E, true);
private final static int[] VELIKOST_PISAVE = {20, 45, 45, 35, 30};
private final String SPOROCILO = "KONEC IGRE!"; // sporočilo ob koncu igre
private final String SPOROCILO2 = "Pritisni <Enter> za novo igro."; // sporočilo ob koncu igre, druga vrstica
private final String IZPIS_TOCKE = "Dosežene točke:"; // sporočilo pri izpisu točk
private final String ZMAGA = "BRAVO! Doseženo število 2048 :-)"; // sporočilo ob zmagi (sestavljena številka 2048)
// vrni barvo številke v kvadratku
private Color vrniBarvoPisave(int vrednost) {
return vrednost < 16 ? new Color(0x776e65) : new Color(0xf9f6f2);
}
// vrni barvo kvadratka za posamezno številko
private Color vrniBarvoKvadratka(int vrednost) {
switch (vrednost) {
case 2:
return new Color(0xeee4da);
case 4:
return new Color(0xede0c8);
case 8:
return new Color(0xf2b179);
case 16:
return new Color(0xf59563);
case 32:
return new Color(0xf67c5f);
case 64:
return new Color(0xf65e3b);
case 128:
return new Color(0xedcf72);
case 256:
return new Color(0xedcc61);
case 512:
return new Color(0xedc850);
case 1024:
return new Color(0xedc53f);
case 2048:
return new Color(0xedc22e);
}
return new Color(0xcdc1b4);
}
public IgralnoPolje() {
// nastavimo privzeto velikost panela oz. naše igralne površine
setPreferredSize(new Dimension(SIRINA, VISINA));
// dodamo poslišalca za dogodke s tipkovnice (pritiske tipk)
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
// pogledamo, katero tipko je pritisnil uporabnik in ustrezno ukrepamo
// zanimajo nas puščice levo, desno, gor in dol ter ENTER (nova igra) in ESC (konec)
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT && !Logika.jeKonec()) {
// ustrezno reagiraj na tipko levo
//System.out.println("levo");
Logika.naslednjaPoteza(0);
}
if (key == KeyEvent.VK_RIGHT && !Logika.jeKonec()) {
// ustrezno reagiraj na tipko desno
//System.out.println("desno");
Logika.naslednjaPoteza(1);
}
if (key == KeyEvent.VK_UP && !Logika.jeKonec()) {
// ustrezno reagiraj na tipko gor
//System.out.println("gor");
Logika.naslednjaPoteza(2);
}
if (key == KeyEvent.VK_DOWN && !Logika.jeKonec()) {
// ustrezno reagiraj na tipko dol
//System.out.println("dol");
Logika.naslednjaPoteza(3);
}
if (key == KeyEvent.VK_ENTER && Logika.jeKonec()) {
// tipka ENTER
//System.out.println("ENTER: nova igra");
Logika.zacniNovoIgro(STEVILO_POLJ); // začni novo igro, če je igre že konec
}
if (key == KeyEvent.VK_ESCAPE) {
// tipka ESC
//System.out.printf("ESC: konec = %b\n", Logika.jeKonec());
Logika.koncajIgro();
}
repaint();
}
});
// barva v oknu igre
setBackground(BARVA_OZADJA);
// igralna površina je lahko v fokusu
setFocusable(true);
// začnemo novo igro
Logika.zacniNovoIgro(STEVILO_POLJ);
repaint();
}
@Override
public void paintComponent(Graphics g) {
// izriši igralno površino in točke
// če je igre konec, izpiši končno sporočilo na sredino igralne površine
super.paintComponent(g);
// nariši zgorni del - glavo, kjer se izpisujejo točke
g.setColor(BARVA_GLAVE);
g.fillRect(0, 0, SIRINA, VELIKOST_GLAVE);
// izpiši točke
String izpis = String.format("%s %6d", IZPIS_TOCKE, Logika.vrniTocke());
Font pisava = new Font("Arial", Font.BOLD, VELIKOST_PISAVE[0]);
g.setFont(pisava);
g.setColor(vrniBarvoPisave(0));
g.drawString(izpis, 2 * ROB, VELIKOST_GLAVE / 2 + ROB);
if (Logika.jeZmagal()) // če je igralec že dosegel 2048, izpiši sporočilo; igra se nadaljuje
g.drawString(ZMAGA, 2 * ROB, VELIKOST_GLAVE / 2 + 2 * ROB + VELIKOST_PISAVE[0]);
// nariši polje STEVILO_POLJ x STEVILO_POLJ (privzeto 4 x 4) z vpisanimi številkami
for (int i = 0; i < STEVILO_POLJ; i++) {
for (int j = 0; j < STEVILO_POLJ; j++) {
int ploscica = Logika.vrniPloscico(i, j);
g.setColor(vrniBarvoKvadratka(ploscica));
g.fillRoundRect(ROB + j * (VELIKOST_POLJA + ROB), VELIKOST_GLAVE + ROB + i * (VELIKOST_POLJA + ROB), VELIKOST_POLJA, VELIKOST_POLJA, LOK, LOK);
if (ploscica > 0) {
String stevilka = Integer.toString(ploscica);
int ind = stevilka.length();
pisava = new Font("Arial", Font.BOLD, VELIKOST_PISAVE[ind]);
int dolzina = this.getFontMetrics(pisava).stringWidth(stevilka);
g.setFont(pisava);
g.setColor(vrniBarvoPisave(ploscica));
g.drawString(stevilka, ROB + j * (VELIKOST_POLJA + ROB) + (VELIKOST_POLJA - dolzina) / 2, VELIKOST_GLAVE + ROB + i * (VELIKOST_POLJA + ROB) + VELIKOST_PISAVE[ind] + (VELIKOST_POLJA - VELIKOST_PISAVE[ind] - ROB) / 2);
}
}
}
// če je igra končana, izpiši sporočilo ob koncu igre na sredino igralne površine
if (Logika.jeKonec()) {
g.setColor(BARVA_KONEC);
g.fillRoundRect(ROB, VELIKOST_GLAVE + ROB, SIRINA - 2 * ROB, VISINA - VELIKOST_GLAVE - 2 * ROB, LOK, LOK);
pisava = new Font("Arial", Font.BOLD, VELIKOST_PISAVE[0]);
g.setFont(pisava);
g.setColor(vrniBarvoPisave(0));
int dolzina = this.getFontMetrics(pisava).stringWidth(SPOROCILO);
g.drawString(SPOROCILO, (this.getWidth() - dolzina) / 2, this.getHeight() / 2);
dolzina = this.getFontMetrics(pisava).stringWidth(SPOROCILO2);
g.drawString(SPOROCILO2, (this.getWidth() - dolzina) / 2, this.getHeight() / 2 + VELIKOST_PISAVE[0] + ROB);
}
}
}

125
src/vaja05/igra/Logika.java Normal file
View File

@ -0,0 +1,125 @@
package vaja05.igra;
import java.util.Random;
public class Logika {
static int[][] polja;
static int tocke;
static boolean konec;
public static void zacniNovoIgro(int velikost) {
polja = new int[velikost][velikost];
tocke = 0;
konec = false;
dodajPloscico();
}
public static void koncajIgro() {
if (konec) {
System.exit(0);
}
konec = true;
}
public static int vrniPloscico(int i, int j) {
return polja[i][j];
}
public static int vrniTocke() {
return tocke;
}
public static boolean jeZmagal() {
return false;
}
public static boolean jeKonec() {
return konec;
}
public static void naslednjaPoteza(int smer) {
switch (smer) {
case 0:
premikVLevo2();
}
dodajPloscico();
}
static void premikVLevo2() {
for (int[] vrstica : polja) {
int dest = 0;
for (int curr = 1; curr < vrstica.length; curr++) {
if (vrstica[dest] == 0) {
vrstica[dest] = vrstica[curr];
} else if (vrstica[dest] == vrstica[curr]) {
vrstica[dest] *= 2;
dest++;
} else {
vrstica[dest + 1] = vrstica[curr];
dest++;
}
vrstica[curr] = 0;
}
}
}
static void premikVLevo() {
for (int[] vrstica : polja) {
int prej = -1;
for (int i = 0; i < vrstica.length; i++) {
if (vrstica[i] == 0) continue;
if (prej != -1 && vrstica[i] == vrstica[prej]) {
vrstica[prej] *= 2;
vrstica[i] = 0;
} else {
prej = i;
}
}
prej = 0;
for (int i = 0; i < vrstica.length; i++) {
if (prej == -1 && vrstica[i] == 0) {
prej = i;
continue;
}
if (prej != -1) {
vrstica[prej] = vrstica[i];
vrstica[i] = 0;
prej = -1;
}
}
}
}
static void dodajPloscico() {
Random random = new Random();
boolean prosto = false;
for (int[] vrstica : polja) {
for (int polje : vrstica) {
if (polje == 0) {
prosto = true;
break;
}
}
}
if (!prosto) {
return;
}
int x, y;
do {
y = random.nextInt(polja.length);
x = random.nextInt(polja[0].length);
} while (polja[y][x] != 0);
if (random.nextInt(10) == 9) {
polja[y][x] = 4;
} else {
polja[y][x] = 2;
}
}
}