JavaRush /Kurslar /All lectures for UZ purposes /JSP va servletlar mavzusida loyiha

JSP va servletlar mavzusida loyiha

All lectures for UZ purposes
Daraja, Dars
Mavjud

Bugun JSP va servletlardan foydalangan holda «X-O» o‘yinini yozamiz.

Bu loyiha avvalgilardan biroz farq qiladi. Unda nafaqat vazifalar, balki ularni qanday bajarish kerakligi haqida tushuntirishlar ham bo‘ladi. Ya'ni bu loyiha «QANDAY ...» turkumidan bo‘ladi.

Ko'rsatmalar:

  1. Repozitoriydan fork qilish: https://github.com/vasylmalik/project-servlet.git
  2. O'zingizning loyihangiz versiyasini kompyuteringizga yuklab oling.
  3. IDEAda ilovani ishga tushirishni sozlang:
    • Alt + Shift + F9 -> Edit Configurations… -> Alt + insert -> tom (qidiruv satrida) -> Local.
    • Shundan so'ng “CONFIGURE” tugmasini bosib, Tomcat fayllarini qaerga yuklab olish va ochish kerakligini ko'rsating.
    • “Deployment” bo'limida: Alt + insert -> Artifact… -> tic-tac-toe:war exploded -> OK.
    • “Application context” maydonida faqat “/” (slash) qoldiring.
    • “APPLY” tugmasini bosing.
    • Sozlash oynasini yoping.
    • Sozlangan konfiguratsiyaning birinchi sinov ishga tushirishini bajaring. Agar hammasini to'g'ri bajargan bo'lsangiz – brauzeringiz ochiladi va unda quyidagi natija bo'ladi:
  4. “pom.xml” faylini oching. “dependencies” blokida 2 ta bog'liqlik mavjud.
    • javax.servlet-api servletlar spetsifikatsiyasini ta'minlaydi. Scope “provided” ishlab chiqish jarayonida kerak bo'ladi, lekin runtime’da kerak emas (Tomcat papkasida bu bog'liqlik allaqachon mavjud).
    • jstl – shablon yaratishda foydalaniladi.
  5. “webapp” papkasida 3 ta fayl bor:
    • index.jsp – bu bizning shablonimiz (HTML sahifa analogi). Unda belgilash va skriptlar bo'ladi. Aynan nomi “index” bo'lgan fayl boshlang'ich sahifa sifatida taqdim etiladi, agar biror konfiguratsiya bo'lmasa, bu biz 3- punktda ko'rdik.
    • /static/main.css – uslublar uchun fayl. Ilgari loyihada bo'lgani kabi, hammasi sizning ixtiyoringizda, xohlagan rangingiz bilan bo'yang.
    • /static/jquery-3.6.0.min.js – frontend bog'liqligi, uni serverimiz statik sifatida tarqatib beradi.
  6. “com.tictactoe” paketida barcha Java-kod mavjud bo'ladi. Hozir u yerda 2 ta sinf mavjud:
    • Sign – enum, bu “x/nol/bo‘shliq” uchun javobgardir.
    • Field – bu bizning maydonimiz. Ushbu sinfda “field” xarita mavjud. Ma'lumotlar saqlash tamoyillari quyidagicha bo'ladi: x-nol maydoni hujayralar raqamlangan bo'ladi. Birinchi qator: 0, 1 va 2. Ikkinchi qator: 3, 4 va 5. Va hokazo. Bundan tashqari, 3 ta metod mavjud. “getEmptyFieldIndex” birinchi bo‘sh hujayrani qidiradi (ha, bizning raqibimiz unchalik aqlli bo‘lmaydi). “checkWin” o‘yin tugagan-tugamaganligini tekshiradi. Agar 3 ta x chizig'i bo'lsa - x qaytaradi, agar 3 ta nol bo'lsa - nol. Aks holda - bo‘shliq. “getFieldData” xaritaning “field” qiymatlarini indekslar o'sishi tartibida saralangan ro'yxat sifatida qaytaradi.
  7. Shablon haqidagi tushuntirishlar tugadi, endi vazifani bajarishga kirishish mumkin. Avvalo, 3x3 jadval chizamiz. Buning uchun „index.jsp” ga quyidagi kodni qo'shing:
    
    <table> 
    	<tr> 
    		<td>0</td> 
    		<td>1</td> 
    		<td>2</td> 
    	</tr> 
    	<tr> 
    		<td>3</td> 
    		<td>4</td> 
    		<td>5</td> 
    	</tr> 
    	<tr> 
    		<td>6</td> 
    		<td>7</td> 
    		<td>8</td> 
    	</tr> 
    </table> 
    				
    Jadvaldagi raqamlarni keyin olib tashlaymiz va ularni x, nolik yoki bo'sh maydon bilan almashtiramiz. Bundan tashqari, “head” tegida uslub faylini ulashing. Buning uchun quyidagi qatorni qo‘shing: <link href="static/main.css" rel="stylesheet"> Uslub faylining mazmuni sizning ixtiyoringizda qoladi. Men quyidagini ishlatdim:
    
    td { 
        border: 3px solid black; 
        padding: 10px; 
        border-collapse: separate; 
        margin: 10px; 
        width: 100px; 
        height: 100px; 
        font-size: 50px; 
        text-align: center; 
        empty-cells: show; 
    } 
    		
    Ishga tushirgandan so'ng, natija quyidagicha ko'rinadi:
  8. Endi quyidagi funksionallikni qo'shamiz: hujayrani bosganingizda, serverga so'rov yuboriladi va parametr sifatida bosilgan hujayra indeksi uzatiladi. Bu vazifani ikkiga bo'lish mumkin: frontdan so'rov yuborish, serverda so'rovni qabul qilish. Keling, turli-tumanlik uchun frontdan boshlaymiz. Har bir “td” tegi uchun “onclick” parametri qo'shamiz. Qiymatda joriy sahifani ko'rsatilgan URL manziliga o'zgartirishni ko'rsatamiz. Logikaga javob beradigan servlet “/logic” URL manziliga ega bo'ladi va “click” nomi bilan parametr qabul qiladi. Shu tarzda foydalanuvchi bosgan hujayra indeksini uzatamiz.
    
    <table> 
        <tr> 
            <td onclick="window.location='/logic?click=0'">0</td> 
            <td onclick="window.location='/logic?click=1'">1</td> 
            <td onclick="window.location='/logic?click=2'">2</td> 
        </tr> 
        <tr> 
            <td onclick="window.location='/logic?click=3'">3</td> 
            <td onclick="window.location='/logic?click=4'">4</td> 
            <td onclick="window.location='/logic?click=5'">5</td> 
        </tr> 
        <tr> 
            <td onclick="window.location='/logic?click=6'">6</td> 
            <td onclick="window.location='/logic?click=7'">7</td> 
            <td onclick="window.location='/logic?click=8'">8</td> 
        </tr> 
    </table> 			
    		
    Hammasi to'g'ri bajarilganligini brauzerdagi ishlab chiquvchi paneli orqali tekshirishingiz mumkin. Masalan, Chrome’da u F12 tugmasi yordamida ochiladi. 4- indeksli hujayraga bosganingizda quyidagi ko'rinish paydo bo'ladi: Xato shundaki, biz hali “logic” manziliga javob bera oladigan servlet yaratmadik.
  9. “com.tictactoe” paketida “LogicServlet” sinfini yarating va “javax.servlet.http.HttpServlet” sinfidan meros oling. Sinfda “doGet” metodini qayta belgilang. Keling, bosilgan hujayra indeksini olish uchun metod qo'shamiz. Shuningdek, mapping (bu servlet so'rovni ushlab oladigan manzil) qo'shish kerak bo'ladi. Buni annotatsiya orqali amalga oshirishni taklif qilaman (lekin qiyinchiliklarni yaxshi ko'rsangiz – web.xml orqali ham mumkin). Servletdagi umumiy kod:
    
    package com.tictactoe; 
    
    import javax.servlet.ServletException; 
    import javax.servlet.annotation.WebServlet; 
    import javax.servlet.http.HttpServlet; 
    import javax.servlet.http.HttpServletRequest; 
    import javax.servlet.http.HttpServletResponse; 
    import java.io.IOException; 
    
    
    @WebServlet(name = "LogicServlet", value = "/logic") 
    public class LogicServlet extends HttpServlet { 
        @Override 
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
            int index = getSelectedIndex(req); 
            resp.sendRedirect("/index.jsp"); 
        } 
    
     
        private int getSelectedIndex(HttpServletRequest request) { 
            String click = request.getParameter("click"); 
            boolean isNumeric = click.chars().allMatch(Character::isDigit); 
            return isNumeric ? Integer.parseInt(click) : 0; 
        } 
    
    } 
    		
    Endi, har qanday hujayrani bosish bilan biz serverda ushbu hujayra indeksini olamiz (serverni debugda ishga tushirib ko'ring va bunga ishonch hosil qilishingiz mumkin). Va o‘sha sahifaga qaytish bo‘ladi, qaysi sahifadan bosishni amalga oshirdik.
  10. Endi biz bosamiz, lekin bu hali ham o‘yin emas. O‘yinda mantiq bo‘lishi uchun o‘yin holatini (qayerda x, qayerda nol joylashgan) so‘rovlar orasida saqlash kerak. Eng oddiy usul bu ma'lumotlarni sessiyada saqlashdir. Ushbu yondashuv bilan sessiya serverda saqlanadi, va mijoz “JSESSIONID” nomli cookie ichida sessiya identifikatorini oladi. Lekin sessiyani har safar emas, faqat o'yin boshida yaratish kerak. Buning uchun biz yana bir servlet yaratamiz, “InitServlet” deb nomlaymiz. Unda “doGet” metodini qayta belgilaymiz, unda yangi sessiya yaratib, o‘yin maydonini yaratamiz, mazkur o‘yin maydoni va Sign turidagi ro‘yxatni sessiya atributlariga qo‘yamiz va “forward” ni index.jsp sahifasiga yuboramiz. Kod:
    
    package com.tictactoe; 
     
    import javax.servlet.ServletException; 
    import javax.servlet.annotation.WebServlet; 
    import javax.servlet.http.HttpServlet; 
    import javax.servlet.http.HttpServletRequest; 
    import javax.servlet.http.HttpServletResponse; 
    import javax.servlet.http.HttpSession; 
    import java.io.IOException; 
    import java.util.List; 
    import java.util.Map; 
     
    @WebServlet(name = "InitServlet", value = "/start") 
    public class InitServlet extends HttpServlet { 
        @Override 
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
            // Yangi sessiya yaratish 
            HttpSession currentSession = req.getSession(true); 
     
            // O‘yin maydonini yaratish 
            Field field = new Field(); 
            Map<Integer, Sign> fieldData = field.getField(); 
     
            // Maydon qiymatlari ro‘yxatini olish 
            List<Sign> data = field.getFieldData(); 
     
            // Sessiyaga maydon parametrlarini qo‘shish (so‘rovlar orasidagi holatni saqlash uchun kerak bo‘ladi) 
            currentSession.setAttribute("field", field); 
            // va maydon qiymatlarini indekslar bo‘yicha tartiblangan (x va noliklarni chizish uchun kerak bo‘ladi) 
            currentSession.setAttribute("data", data); 
     
            // So‘rovni server orqali index.jsp sahifasiga yo‘naltirish 
            getServletContext().getRequestDispatcher("/index.jsp").forward(req, resp); 
        } 
    } 
    
    U unutmaslik uchun, server ishga tushirilgandan keyin brauzerda ochiladigan boshlang'ich sahifani “/start” ga almashtiramiz: Server qayta ishga tushirilgandan so‘ng va maydonning har qanday hujayrasini bosganingizda, brauzerning ishlab chiquvchi menyusida “Request Headers” bo‘limida sessiya identifikatori bilan cookie mavjud bo‘ladi:
  11. Bizda mijozdan (brauzerdan) tushadigan so'rovlar orasidagi holatni saqlash uchun saqlash vositasi paydo bo'lganda, o'yin mantiqini yoza boshlash mumkin. Bizda bu “LogicServlet” da joylashgan. Biz “doGet” metodida ishlashimiz kerak. Metodga quyidagi xatti-harakatni qo'shamiz:
    • Sessiyadan Field turidagi “field” ob'ektini olamiz (uni “extractField” metodiga keltirib chiqaramiz).
    • Foydalanuvchi bosgan maydonchaga xni qo'ying (hech qanday tekshiruvlarsiz).
    
    @Override 
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
        // Joriy sessiyani olamiz 
        HttpSession currentSession = req.getSession(); 
     
        // Sessiyadan o‘yin maydonining ob’ektini olamiz 
        Field field = extractField(currentSession); 
     
        // Bosilgan hujayra indeksini olamiz 
        int index = getSelectedIndex(req); 
     
        // Foydalanuvchi bosgan hujayraga x qo‘ying 
        field.getField().put(index, Sign.CROSS); 
     
        // Belgi ro‘yxatini hisoblaymiz 
        List<Sign> data = field.getFieldData(); 
     
        // Sessiyadagi maydon ob’ektini va belgi ro‘yxatini yangilaymiz 
        currentSession.setAttribute("data", data); 
        currentSession.setAttribute("field", field); 
     
        resp.sendRedirect("/index.jsp"); 
    } 
    
    private Field extractField(HttpSession currentSession) { 
        Object fieldAttribute = currentSession.getAttribute("field"); 
        if (Field.class != fieldAttribute.getClass()) { 
            currentSession.invalidate(); 
            throw new RuntimeException("Session is broken, try one more time"); 
        } 
        return (Field) fieldAttribute; 
    } 
    
    Hozircha hech qanday o‘zgarish bo‘lmadi, lekin serverni debugging rejimida ishga tushirsangiz va redirect uzatiladigan qatorida breakpoint qo‘ysangiz, “data” ob’ektining “ichki qismlarini” ko‘rishingiz mumkin. Haqiqatan ham ushbu ob’ekt under “CROSS” indeksida belgi bor.
  12. Endi esa xni front-endda ko'rsatish vaqti keldi. Buning uchun “index.jsp” fayl bilan ishlaymiz va “JSTL” texnologiyasidan foydalanamiz.
    • <head> bo‘limida quyidagini qo‘shamiz: <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    • Jadvaldagi har bir <td> blokining indeksini formula orqali hisoblash imkonini beruvchi konstruktsiyaga almashtiramiz. Masalan, nol indeks uchun: <td onclick="window.location='/logic?click=0'">${data.get(0).getSign()}</td> Endi hujayrani bosganingizda u yerda x paydo bo‘ladi:
  13. Biz o'z xodimizni qildik, endi “nolik”ning navbati. Va bu yerda bir-ikkita tekshiruvlarni qo'shamiz, shunda belgilar allaqachon band bo'lgan hujayralarga qo'yilmaydi.
    • Bosilgan hujayra bo‘shligini tekshirishingiz kerak. Aks holda, hech narsa qilmaymiz va foydalanuvchini sessiya parametrlari o'zgarmagan holda o'sha sahifaga yuboramiz.
    • Maydon hujayralari soni toq bo‘lganligi sababli, xni qo‘yish mumkin bo‘lgan payt bo‘lishi mumkin, lekin nolga joy yo‘q. Shuning uchun, xni qo'yganimizdan so'ng, bo'sh hujayra indeksini olishga harakat qilamiz (Field sinfining getEmptyFieldIndex metodi). Agar indeks musbat bo'lsa, u yerga nolikni qo'yamiz. Kod:
      
      package com.tictactoe; 
       
      import javax.servlet.RequestDispatcher; 
      import javax.servlet.ServletException; 
      import javax.servlet.annotation.WebServlet; 
      import javax.servlet.http.HttpServlet; 
      import javax.servlet.http.HttpServletRequest; 
      import javax.servlet.http.HttpServletResponse; 
      import javax.servlet.http.HttpSession; 
      import java.io.IOException; 
      import java.util.List; 
       
      @WebServlet(name = "LogicServlet", value = "/logic") 
      public class LogicServlet extends HttpServlet { 
          @Override 
          protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
              // Joriy sessiyani olamiz 
              HttpSession currentSession = req.getSession(); 
       
              // Sessiyadan o‘yin maydonining ob’ektini olamiz 
              Field field = extractField(currentSession); 
       
              // Bosilgan hujayra indeksini olamiz 
              int index = getSelectedIndex(req); 
              Sign currentSign = field.getField().get(index); 
       
              // Bosilgan hujayra bo'shligini tekshiramiz. 
              // Aks holda, hech narsa qilmaymiz va foydalanuvchini o'sha sahifaga yuboramiz, 
              // sessiya parametrlari o'zgarmagan holda 
              if (Sign.EMPTY != currentSign) { 
                  RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/index.jsp"); 
                  dispatcher.forward(req, resp); 
                  return; 
              } 
       
              // Foydalanuvchi bosgan hujayraga x qo‘ying 
              field.getField().put(index, Sign.CROSS); 
       
              // Bo'sh maydon hujayrasini olish
              int emptyFieldIndex = field.getEmptyFieldIndex(); 
       
              if (emptyFieldIndex >= 0) { 
                  field.getField().put(emptyFieldIndex, Sign.NOUGHT); 
              } 
       
              // Belgi ro‘yxatini hisoblaymiz 
              List<Sign> data = field.getFieldData(); 
       
              // Sessiyadagi maydon ob’ektini va belgi ro‘yxatini yangilaymiz 
              currentSession.setAttribute("data", data); 
              currentSession.setAttribute("field", field); 
       
              resp.sendRedirect("/index.jsp"); 
          } 
       
          private int getSelectedIndex(HttpServletRequest request) { 
              String click = request.getParameter("click"); 
              boolean isNumeric = click.chars().allMatch(Character::isDigit); 
              return isNumeric ? Integer.parseInt(click) : 0; 
          } 
       
          private Field extractField(HttpSession currentSession) { 
              Object fieldAttribute = currentSession.getAttribute("field"); 
              if (Field.class != fieldAttribute.getClass()) { 
                  currentSession.invalidate(); 
                  throw new RuntimeException("Session is broken, try one more time"); 
              } 
              return (Field) fieldAttribute; 
          } 
      } 
      
  14. Bu bosqichda biz xni qo‘yishimiz mumkin, AI nol bilan javob beradi. Lekin, o'yinni qachon to'xtatish kerakligini tekshirmayapmiz. Bu uchta holatda bo'lishi mumkin:
    • navbatdagi x xarakati natijasida uchta x chiziqlari paydo bo‘ldi;
    • navbatdagi javob no'l harakati natijasida uchta nol chizig'i hosil bo'ldi;
    • navbatdagi x xarakatidan so‘ng bo‘sh hujayralar tugadi.
    Uchta x/nolik ketma-ket tushmasligi uchun tekshiruvchi metod qo'shamiz:
    
    /** 
     * Uchta x/nolik ketma-ket tushmasligini tekshiruvchi metod. 
     * True/false qaytaradi 
     */ 
    private boolean checkWin(HttpServletResponse response, HttpSession currentSession, Field field) throws IOException { 
        Sign winner = field.checkWin(); 
        if (Sign.CROSS == winner || Sign.NOUGHT == winner) { 
            // Kimdir g‘olib bo‘lganini ko‘rsatadigan flag qo‘shamiz 
            currentSession.setAttribute("winner", winner); 
     
            // Belgi ro‘yxatini hisoblaymiz 
            List<Sign> data = field.getFieldData(); 
     
            // Sessiyadagi bu ro‘yxatni yangilaymiz 
            currentSession.setAttribute("data", data); 
     
            // Redirect yuboramiz 
            response.sendRedirect("/index.jsp"); 
            return true; 
        } 
        return false; 
    } 
    
    Agar g‘olib aniqlansa, bu metodning o‘ziga xosligi shundaki, sessiyaga qo‘shimcha parametr qo‘shiladi, uning yordamida keyingi punktlarda “index.jsp” da aks ettirilishini o‘zgartiramiz.
  15. “checkWin” metodiga “doGet” metodini ikki marta qo‘shamiz. Birinchi marta xni o‘rnatgandan so‘ng, ikkinchi marta nolikni o‘rnatgandan so‘ng.
    
    // Foydalanuvchi tomonidan oxirgi marta bosilganidan keyin x gʻolib boʻlganligini tekshiramiz 
    if (checkWin(resp, currentSession, field)) { 
        return; 
    } 
    
    
    if (emptyFieldIndex >= 0) { 
        field.getField().put(emptyFieldIndex, Sign.NOUGHT); 
        // Oxirgi nolik qo'shilgandan keyin nolik g'olib bo'lganligini tekshiramiz 
        if (checkWin(resp, currentSession, field)) { 
            return; 
        } 
    } 
    
  16. Xatti-harakatlar deyarli o'zgarmadi (lekin bir belgi g'olib bo'lgan holatda noliklar qo'shilmaydi). “index.jsp” dagi “winner” parametridan foydalanamiz va g'olibni ko'rsatamiz. c:set va c:if direktivalaridan jadvaldan keyin foydalanamiz:
    
    <hr> 
    <c:set var="CROSSES" value="<%=Sign.CROSS%>"/> 
    <c:set var="NOUGHTS" value="<%=Sign.NOUGHT%>"/> 
     
    <c:if test="${winner == CROSSES}"> 
        <h1>CROSSES WIN!</h1> 
    </c:if> 
    <c:if test="${winner == NOUGHTS}"> 
        <h1>NOUGHTS WIN!</h1> 
    </c:if> 
    
    Agar xlar g‘olib bo‘lsa, “CROSSES WIN!” xabari chiqariladi, agar noliklar g‘olib bo‘lsa – “NOUGHTS WIN!”. Natijada siz ikkita yozuvdan birini olishingiz mumkin:
  17. Agar g'olib bo'lsa, revanj olish imkoniyatiga ega bo'lishingiz kerak. Buning uchun serverga so'rov yuboradigan tugma kerak bo'ladi. Server esa joriy sessiyani bekor qiladi va so‘rovni “/start”ga yana yo‘naltiradi.
    • “index.jsp” faylida “head” qismida “jquery” skriptini ulaymiz. Ushbu kutubxonadan foydalangan holda biz serverga so'rov yuboramiz.
      
      <script src="<c:url value="/static/jquery-3.6.0.min.js"/>"></script> 
      
    • “index.jsp” faylida “script” bo‘limida serverga POST so‘rov yuboradigan sinxron funksiya qo‘shamiz. Serverdan javob kelganda – joriy sahifani qayta yuklaydi.
      
      <script> 
          function restart() { 
              $.ajax({ 
                  url: '/restart', 
                  type: 'POST', 
                  contentType: 'application/json;charset=UTF-8', 
                  async: false, 
                  success: function () { 
                      location.reload(); 
                  } 
              }); 
          } 
      </script> 
      
    • “c:if” bloklari ichida, bosilganda yangi yozilgan functionni chaqiradigan tugma qo'shamiz:
      
      <c:if test="${winner == CROSSES}"> 
          <h1>CROSSES WIN!</h1> 
          <button onclick="restart()">Start again</button> 
      </c:if> 
      <c:if test="${winner == NOUGHTS}"> 
          <h1>NOUGHTS WIN!</h1> 
          <button onclick="restart()">Start again</button> 
      </c:if> 
      
    • “/restart” URL manzilini qo'llab-quvvatlaydigan yangi servlet yaratamiz.
      
      package com.tictactoe; 
       
      import javax.servlet.annotation.WebServlet; 
      import javax.servlet.http.HttpServlet; 
      import javax.servlet.http.HttpServletRequest; 
      import javax.servlet.http.HttpServletResponse; 
      import java.io.IOException; 
       
      @WebServlet(name = "RestartServlet", value = "/restart") 
      public class RestartServlet extends HttpServlet { 
          @Override 
          protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { 
              req.getSession().invalidate(); 
              resp.sendRedirect("/start"); 
          } 
      } 
      
      G'olib bo'lganidan keyin “Start again” tugmasi paydo bo'ladi. Uni bosganingizdan so'ng – maydon to'liq tozalanadi va o'yin boshidan boshlanadi.
  18. Oxirgi holatni ko'rib chiqamiz. Agar foydalanuvchi x qo‘ysa, g‘alaba bo‘lmasa va nolik uchun joy qolmasa nima bo‘ladi? Ushbu holatda durang, hozir biz uni ham qaytaramiz:
    • “LogicServlet”da sessiyaga yana bir “draw” parametrini qo‘shamiz, “data” maydonini yangilaymiz va “index.jsp”ga qayta yo‘naltirishni yuboramiz:
      
      // Agar bunday hujayra mavjud bo'lsa 
      if (emptyFieldIndex >= 0) { 
          … 
      } 
      // Agar bo'sh hujayra yo'q va hech kim g'olib bo'lmasa - bu durang 
      else { 
          // Sessiyaga durang bo‘lganligini bildiruvchi flagni qo‘shamiz 
          currentSession.setAttribute("draw", true); 
       
          // Belgilar ro'yxatini hisoblaymiz 
          List<Sign> data = field.getFieldData(); 
       
          // Ushbu ro‘yxatni sessiyaga yangilaymiz 
          currentSession.setAttribute("data", data); 
       
          // Redirect yuboramiz 
          response.sendRedirect("/index.jsp"); 
          return; 
      } 
      
    • “index.jsp” faylida ushbu parameterni qayta ishlaymiz:
      
      <c:if test="${draw}"> 
          <h1>IT'S A DRAW</h1> 
          <br> 
          <button onclick="restart()">Start again</button> 
      </c:if> 
      
      Durang natijada mos xabar paydo bo'ladi va qayta boshlash taklifi chiqadi:

Bu orqali o‘yinni yaratish tugallandi.

Biz ishlagan sinflar va fayllarning kodlari

InitServlet


package com.tictactoe; 
 
import javax.servlet.ServletException; 
import javax.servlet.annotation.WebServlet; 
import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import javax.servlet.http.HttpSession; 
import java.io.IOException; 
import java.util.List; 
import java.util.Map; 
 
@WebServlet(name = "InitServlet", value = "/start") 
public class InitServlet extends HttpServlet { 
    @Override 
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
        // Yangi sessiya yaratish 
        HttpSession currentSession = req.getSession(true); 
 
        // O‘yin maydonini yaratish 
        Field field = new Field(); 
        Map<Integer, Sign> fieldData = field.getField(); 
 
        // Maydon qiymatlari ro‘yxatini olish 
        List<Sign> data = field.getFieldData(); 
 
        // Sessiyaga maydon parametrlarini qo‘shish (so‘rovlar orasidagi holatni saqlash uchun) 
        currentSession.setAttribute("field", field); 
        // va maydon qiymatlarini indekslar bo‘yicha tartiblangan (x va noliklarni chizish uchun) 
        currentSession.setAttribute("data", data); 
 
        // So‘rovni server orqali index.jsp sahifasiga yo‘naltirish 
        getServletContext().getRequestDispatcher("/index.jsp").forward(req, resp); 
    } 
} 

LogicServlet


package com.tictactoe; 
 
import javax.servlet.RequestDispatcher; 
import javax.servlet.ServletException; 
import javax.servlet.annotation.WebServlet; 
import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import javax.servlet.http.HttpSession; 
import java.io.IOException; 
import java.util.List; 
 
@WebServlet(name = "LogicServlet", value = "/logic") 
public class LogicServlet extends HttpServlet { 
    @Override 
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
        // Joriy sessiyani olamiz 
        HttpSession currentSession = req.getSession(); 
 
        // Sessiyadan o‘yin maydonining ob’ektini olamiz 
        Field field = extractField(currentSession); 
 
        // Bosilgan hujayra indeksini olamiz 
        int index = getSelectedIndex(req); 
        Sign currentSign = field.getField().get(index); 
 
        // Bosilgan hujayra bo'shligini tekshiramiz. 
        // Aks holda, hech narsa qilmaymiz va foydalanuvchini o'sha sahifaga yuboramiz, 
        // sessiya parametrlari o'zgarmagan holda 
        if (Sign.EMPTY != currentSign) { 
            RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/index.jsp"); 
            dispatcher.forward(req, resp); 
            return; 
        } 
 
        // Foydalanuvchi bosgan hujayraga x qo‘ying 
        field.getField().put(index, Sign.CROSS); 
 
        // Foydalanuvchi tomonidan oxirgi marta bosilganidan keyin x gʻolib boʻlganligini tekshiramiz 
        if (checkWin(resp, currentSession, field)) { 
            return; 
        } 
 
        // Bo'sh maydon hujayrasini olish
        int emptyFieldIndex = field.getEmptyFieldIndex(); 
 
        if (emptyFieldIndex >= 0) { 
            field.getField().put(emptyFieldIndex, Sign.NOUGHT); 
            // Oxirgi nolik qo'shilgandan keyin nolik g'olib bo'lganligini tekshiramiz 
            if (checkWin(resp, currentSession, field)) { 
                return; 
            } 
        } 
        // Agar bo'sh hujayra yo'q va hech kim g'olib bo'lmasa - bu durang 
        else { 
            // Sessiyaga durang bo‘lganligini bildiruvchi flagni qo‘shamiz 
            currentSession.setAttribute("draw", true); 
 
            // Belgilar ro'yxatini hisoblaymiz 
            List<Sign> data = field.getFieldData(); 
 
            // Ushbu ro‘yxatni sessiyaga yangilaymiz 
            currentSession.setAttribute("data", data); 
 
            // Redirect yuboramiz 
            resp.sendRedirect("/index.jsp"); 
            return; 
        } 
 
        // Belgilar ro'yxatini hisoblaymiz 
        List<Sign> data = field.getFieldData(); 
 
        // Sessiyadagi maydon ob’ektini va belgi ro‘yxatini yangilaymiz 
        currentSession.setAttribute("data", data); 
        currentSession.setAttribute("field", field); 
 
        resp.sendRedirect("/index.jsp"); 
    } 
 
    /** 
     * Uchta x/nolik ketma-ket tushmasligini tekshiruvchi metod. 
     * True/false qaytaradi 
     */ 
    private boolean checkWin(HttpServletResponse response, HttpSession currentSession, Field field) throws IOException { 
        Sign winner = field.checkWin(); 
        if (Sign.CROSS == winner || Sign.NOUGHT == winner) { 
            // Kimdir g‘olib bo‘lganini ko‘rsatadigan flag qo‘shamiz 
            currentSession.setAttribute("winner", winner); 
 
            // Belgi ro‘yxatini hisoblaymiz 
            List<Sign> data = field.getFieldData(); 
 
            // Sessiyadagi bu ro‘yxatni yangilaymiz 
            currentSession.setAttribute("data", data); 
 
            // Redirect yuboramiz 
            response.sendRedirect("/index.jsp"); 
            return true; 
        } 
        return false; 
    } 
 
    private int getSelectedIndex(HttpServletRequest request) { 
        String click = request.getParameter("click"); 
        boolean isNumeric = click.chars().allMatch(Character::isDigit); 
        return isNumeric ? Integer.parseInt(click) : 0; 
    } 
 
    private Field extractField(HttpSession currentSession) { 
        Object fieldAttribute = currentSession.getAttribute("field"); 
        if (Field.class != fieldAttribute.getClass()) { 
            currentSession.invalidate(); 
            throw new RuntimeException("Session is broken, try one more time"); 
        } 
        return (Field) fieldAttribute; 
    } 
} 

RestartServlet


package com.tictactoe; 
 
import javax.servlet.annotation.WebServlet; 
import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import java.io.IOException; 
 
@WebServlet(name = "RestartServlet", value = "/restart") 
public class RestartServlet extends HttpServlet { 
    @Override 
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { 
        req.getSession().invalidate(); 
        resp.sendRedirect("/start"); 
    } 
} 

index.jsp


<%@ page import="com.tictactoe.Sign" %> 
<%@ page contentType="text/html;charset=UTF-8" language="java" %> 
 
<!DOCTYPE html> 
<html> 
<head> 
    <link href="static/main.css" rel="stylesheet"> 
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
    <script src="<c:url value="/static/jquery-3.6.0.min.js"/>"></script> 
    <title>Tic-Tac-Toe</title> 
</head> 
<body> 
<h1>Tic-Tac-Toe</h1> 
 
<table> 
    <tr> 
        <td onclick="window.location='/logic?click=0'">${data.get(0).getSign()}</td> 
        <td onclick="window.location='/logic?click=1'">${data.get(1).getSign()}</td> 
        <td onclick="window.location='/logic?click=2'">${data.get(2).getSign()}</td> 
    </tr> 
    <tr> 
        <td onclick="window.location='/logic?click=3'">${data.get(3).getSign()}</td> 
        <td onclick="window.location='/logic?click=4'">${data.get(4).getSign()}</td> 
        <td onclick="window.location='/logic?click=5'">${data.get(5).getSign()}</td> 
    </tr> 
    <tr> 
        <td onclick="window.location='/logic?click=6'">${data.get(6).getSign()}</td> 
        <td onclick="window.location='/logic?click=7'">${data.get(7).getSign()}</td> 
        <td onclick="window.location='/logic?click=8'">${data.get(8).getSign()}</td> 
    </tr> 
</table> 
 
<hr> 
<c:set var="CROSSES" value="<%=Sign.CROSS%>"/> 
<c:set var="NOUGHTS" value="<%=Sign.NOUGHT%>"/> 
 
<c:if test="${winner == CROSSES}"> 
    <h1>CROSSES WIN!</h1> 
    <button onclick="restart()">Start again</button> 
</c:if> 
<c:if test="${winner == NOUGHTS}"> 
    <h1>NOUGHTS WIN!</h1> 
    <button onclick="restart()">Start again</button> 
</c:if> 
<c:if test="${draw}"> 
    <h1>IT'S A DRAW</h1> 
    <button onclick="restart()">Start again</button> 
</c:if> 
 
<script> 
    function restart() { 
        $.ajax({ 
            url: '/restart', 
            type: 'POST', 
            contentType: 'application/json;charset=UTF-8', 
            async: false, 
            success: function () { 
                location.reload(); 
            } 
        }); 
    } 
</script> 
 
</body> 
</html> 

main.css


td { 
    border: 3px solid black; 
    padding: 10px; 
    border-collapse: separate; 
    margin: 10px; 
    width: 100px; 
    height: 100px; 
    font-size: 50px; 
    text-align: center; 
    empty-cells: show;
   }
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION