C, PHP, VB, .NET

Дневникът на Филип Петров


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

 



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

Адресът на електронната поща няма да се публикува


*