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:
- Repozitoriydan fork qilish: https://github.com/vasylmalik/project-servlet.git
- O'zingizning loyihangiz versiyasini kompyuteringizga yuklab oling.
- 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:
- “pom.xml” faylini oching. “dependencies” blokida 2 ta bog'liqlik mavjud.
javax.servlet-apiservletlar 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.- “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.- “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.- Shablon haqidagi tushuntirishlar tugadi, endi vazifani bajarishga kirishish mumkin. Avvalo, 3x3 jadval chizamiz. Buning uchun „index.jsp” ga quyidagi kodni qo'shing:
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:<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><link href="static/main.css" rel="stylesheet">Uslub faylining mazmuni sizning ixtiyoringizda qoladi. Men quyidagini ishlatdim:
Ishga tushirgandan so'ng, natija quyidagicha ko'rinadi: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; }![]()
- 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.
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:<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>
Xato shundaki, biz hali “logic” manziliga javob bera oladigan servlet yaratmadik. - “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:
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.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 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:
U unutmaslik uchun, server ishga tushirilgandan keyin brauzerda ochiladigan boshlang'ich sahifani “/start” ga almashtiramiz: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); } }
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: ![]()
- 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).
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.@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; } - 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:![]()
- <head> bo‘limida quyidagini qo‘shamiz:
- 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; } } - 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.
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./** * 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; } - “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; } } - 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:setvac:ifdirektivalaridan jadvaldan keyin foydalanamiz:
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:<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 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.
G'olib bo'lganidan keyin “Start again” tugmasi paydo bo'ladi. Uni bosganingizdan so'ng – maydon to'liq tozalanadi va o'yin boshidan boshlanadi.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” faylida “head” qismida “jquery” skriptini ulaymiz. Ushbu kutubxonadan foydalangan holda biz serverga so'rov yuboramiz.
- 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:
Durang natijada mos xabar paydo bo'ladi va qayta boshlash taklifi chiqadi:<c:if test="${draw}"> <h1>IT'S A DRAW</h1> <br> <button onclick="restart()">Start again</button> </c:if>
- “LogicServlet”da sessiyaga yana bir “draw” parametrini qo‘shamiz, “data” maydonini yangilaymiz va “index.jsp”ga qayta yo‘naltirishni yuboramiz:
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;
}

Xato shundaki, biz hali “logic” manziliga javob bera oladigan servlet yaratmadik.
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: 

Kod:
GO TO FULL VERSION