* Игра на Пексесо в 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()); }
Добави коментар