* Игра на Пексесо в Java – упражнение за събития и обхождане на LinkedList
Публикувано на 17 май 2023 в раздел УКИ.
Ще създадем опростен вариант на играта Пексесо. На игралното поле са разположени четен брой карти с еднакви гърбове (при нашия вариант ще бъдат 20 на брой). Всяка една карта има точно една карта-двойник (двете имат една и съща картинка на лицевата страна). Играчът обръща две карти, след което:
- ако картинките им не са еднакви, той ги връща обратно с гърба нагоре;
- ако картинките им са еднакви, ги оставя с лицето нагоре.
Целта на играта е с максимално малко ходове да се обърнат всички карти с лицето нагоре.
За да реализираме картите ще ни трябва набор от картинки. Понеже ще имаме общо 20 карти, ще ни трябват 10 картинки за лицеа (понеже 2 по 2 се повтарят) и 1 картинка за гръб. Може да изтеглите примерни от тук (свалени са като безплатни картинки от iconfinder).
Размерът на игралното поле зависи от размера на картинките. Понеже нашите са с размер 48 на 48 пиксела (дължина и ширина), ще реализираме играта, като ги поставим една до друга в пет реда и четири стълба. Оставайки по един пиксел отляво и един пиксел отдясно на всяка картинка, това ще направи общо 5×50=250 пиксела по хоризонтала и 4×50=200 пиксела по вертикала за цялото игрално поле. Точно с такива размери ще е и основният JFrame. Създайте го, а в него разположете разпънат от край до край JPanel с име gamePanel. Картинките от архива добавете в директория images. Изгледът на програмата е показан на следващата картинка.
Пристъпваме към програмния код. Трябва да създадем 20 на брой карти и да ги разположим по игралното поле. Този път няма да го правим ръчно, използвайки компонентите, които ни предоставя средата NetBeans, а програмно.
Нека започнем с въпроса „какво е карта“. Това е сложен обект - той се състои от картинка за гръб и картинка за лице. Освен това би могъл да извършва действие - да се обръща наобратно. Ще реализираме това като се възползваме от компактния запис на клас Record. Вътре в създадения JFrame създайте следния вложен клас:
// Всяка карта ще се състои от две картинки - предна и задна
record Card(JLabel front, JLabel back) {
Card{
// Ако някой натисне с бутон на мишката върху гърба на картата
// тя се разменя с лицето, а лицето става гръб. Т.е. обръща се
back.addMouseListener(new java.awt.event.MouseAdapter() {
@Override
public void mouseClicked(java.awt.event.MouseEvent evt) {
Icon tmp = back.getIcon();
back.setIcon(front.getIcon());
front.setIcon(tmp);
}
});
// Същото правим за лицето на картата - при натискане се обръща
front.addMouseListener(new java.awt.event.MouseAdapter() {
@Override
public void mouseClicked(java.awt.event.MouseEvent evt) {
Icon tmp = back.getIcon();
back.setIcon(front.getIcon());
front.setIcon(tmp);
}
});
}
// Да променим местоположението на една карта означава, че местим
// както лицето, така и гърба ѝ
void setLocation(int x, int y) {
front.setLocation(x, y);
back.setLocation(x, y);
}
}
Ще съхраняваме тестето от карти в свързан списък. Създайте следната член-променлива:
LinkedList<Card> cards = new LinkedList<>();
Сега остава да създадем самите карти и да ги разположим по игралното поле. Създайте събитие за основния JFrame от тип WindowOpened и в него добавете следния код:
private void formWindowOpened(java.awt.event.WindowEvent evt) {
// метод, който ще напишем по-късно.
// Той ще създаде картите и ще ги запише в свързания списък cards (тестето)
generateCards();
// разбъркваме вече създадените карти
Collections.shuffle(cards);
// ще подреждаме картите по игралното поле с гърбовете им
// в пет реда и четири стълба, започвайки от клетка с индекси (0, 0)
int row = 0;
int col = 0;
for (Card c : cards) {
// поставяме картата на съответната позиция
c.setLocation(col * 50 + 1, row * 50 + 1);
// и я добавяме към панела, за да се визуализира
gamePanel.add(c.back);
// преминаваме към следващата карта, но внимаваме да не излезнем извън екрана
row++;
if (row > 3) {
row = 0;
col++;
}
}
// след като сложим всички карти се уверяваме, че всичко е визуализирано коректно
gamePanel.repaint();
}
Методът generateCards() ще има следния код:
void generateCards(){
// ще създаваме картите по двойки, затова завъртаме цикъла 10 пъти (за 20 карти общо)
for (int i = 1; i <= 10; i++) {
// създаваме гърба на едната карта
JLabel card1Back = new JLabel();
card1Back.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Pexeso/images/back.png")));
card1Back.setSize(card1Back.getPreferredSize());
// създаваме и лицето ѝ
JLabel card1Front = new JLabel();
card1Front.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Pexeso/images/" + i + ".png")));
card1Front.setSize(card1Front.getPreferredSize());
// създаваме самата карта
Card newCard1 = new Card(card1Front, card1Back);
// повтаряме абсолютно същото за другата карта
JLabel card2Back = new JLabel();
card2Back.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Pexeso/images/back.png")));
card2Back.setSize(card2Back.getPreferredSize());
JLabel card2Front = new JLabel();
card2Front.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Pexeso/images/" + i + ".png")));
card2Front.setSize(card2Front.getPreferredSize());
Card newCard2 = new Card(card2Front, card2Back);
// добавяме двойката карти в тестето с карти
cards.add(newCard1);
cards.add(newCard2);
}
Играта е готова.

Както виждате, при нея няма автоматизирано оценяване дали картинките на картите съвпадат или не - това е оставено за играча и той е отговорен да ги връща обратно сам при положение, че те последната двойка карти са различни. Едно бъдещо усъвършенстване може да включи и функционалност за автоматизиране. За такава реализация ще е нужно по-прецизно създаване на самото тесте - ще трябва сами да реализирате метод equals, така че да може две карти с едно и също лице наистина да се оценяват като еднакви (при горната реализация не е така). Такъв метод може да е следния (сравнява иконките на лицата на картите по името на файла им):
@Override
public boolean equals(Object o) {
return o instanceof Card p
&& this.front.getIcon().toString().equals(p.front.getIcon().toString());
}

Добави коментар