Bugün servletler ve JSP kullanarak bir Tic-Tac-Toe oyunu yazacağız.
Bu proje öncekilerden biraz farklı olacak. Sadece görevleri değil, aynı zamanda bunların nasıl yapılacağına dair açıklamaları da içerecektir. Yani "NASIL YAPILIR ..." serisinden bir proje olacak.
Talimat:
- Depodan çatal: https://github.com/CodeGymCC/project-servlet.git
- Projenizin sürümünü bilgisayarınıza indirin.
- IDEA'da uygulama başlatmayı ayarlayın:
- Alt + Shift + F9 -> Yapılandırmaları Düzenle… -> Alt + ekle -> tom (arama çubuğuna) -> Yerel.
- Bundan sonra, "YAPILANDIR" ı tıklamanız ve Tomcat ile arşivin nereye indirildiğini ve paketten çıkarıldığını belirtmeniz gerekir.
- "Dağıtım" sekmesinde: Alt + ekle -> Artifact… -> tic-tac-toe:war patladı -> Tamam.
- "Uygulama bağlamı" alanında: yalnızca "/" (eğik çizgi) bırakın.
- "UYGULA"ya basın.
- Ayarlar penceresini kapatın.
- Özelleştirilmiş yapılandırmanın ilk deneme çalıştırmasını yapın. Her şey doğru yapılırsa, varsayılan tarayıcınız açılır ve içinde şöyle olur:
- "pom.xml" dosyasını açın . “bağımlılıklar” bloğunda 2 bağımlılık vardır .
javax.servlet-api
servlet'lerin belirtilmesinden sorumludur. Geliştirme sırasında "sağlanan" kapsam gereklidir, ancak çalışma zamanında gerekli değildir (Tomcat zaten lib klasöründe bu bağımlılığa sahiptir).jstl
– bir şablon motoru olarak kabul edilebilir.- “webapp” klasöründe 3 dosya vardır :
index.jsp
- bu bizim şablonumuz (HTML sayfasına benzer). İşaretleme ve komut dosyaları içerecektir. 3. adımda gördüğümüz konfigürasyonlar yoksa ilk sayfa olarak verilen “index” adlı dosyadır ./static/main.css
- stiller için dosya. Bir önceki projede olduğu gibi burada da her şey size kalmış, dilediğiniz gibi boyayın./static/jquery-3.6.0.min.js
- sunucumuzun statik olarak dağıtacağı ön uç bağımlılığı.- "com.tictactoe" paketi tüm Java kodunu içerecektir. Şu anda 2 sınıf var:
Sign
- "çapraz / sıfır / boşluktan" sorumlu olan numaralandırma .Field
bizim alanımızdır. Bu sınıfın bir "alan" haritası vardır . Veri depolama prensibi şu şekilde olacaktır: tic-tac-toe alanının hücreleri sıfırdan numaralandırılır. İlk satırda 0, 1 ve 2. İkinci satırda: 3, 4 ve 5. Ve böyle devam eder. Ayrıca 3 yöntem var. “getEmptyFieldIndex” ilk boş hücreyi arar (evet, rakibimiz pek akıllı olmayacaktır). "checkWin" oyunun bitip bitmediğini kontrol eder. Üç çarpıdan oluşan bir satır varsa, bir çarpı döndürür; üç sıfırdan oluşan bir satır varsa, sıfır döndürür. Aksi halde boştur. "getFieldData" - "alan" haritasının değerlerini artan dizin sırasına göre sıralanmış bir liste olarak döndürür.- Şablonla ilgili açıklamalar bitti, artık göreve başlayabilirsiniz. 3'e 3'lük bir tablo çizerek başlayalım, bunun için “index.jsp” dosyasına aşağıdaki kodu ekleyin :
Daha sonra tablodaki sayıları kaldıracağız ve bunları artı, sıfır veya boş bir alanla değiştireceğiz. Ayrıca, "head" etiketinin içine stil dosyasını ekleyin. Bunu yapmak için bir satır ekleyin:<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">
Stil dosyasının içeriği size kalmış. Bunu kullandım:
Çalıştırdıktan sonra sonucum şöyle görünüyor: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; }
- Şimdi şu işlevi ekleyelim: Bir hücre tıklandığında sunucuya, tıklanan hücrenin dizinini parametre olarak geçireceğimiz bir istek gönderilecek. Bu görev iki bölüme ayrılabilir: önden bir istek gönderin, sunucuda bir isteği kabul edin. Bir değişiklik için önden başlayalım.
Her "d" etiketine bir "onclick" parametresi ekleyelim . Değerde, geçerli sayfanın belirtilen URL'ye değişimini belirtiriz. Mantıktan sorumlu olacak sunucu uygulamasının URL'si “/logic” olacaktır . Ve "tıkla" adlı bir parametre alacaktır . Böylece kullanıcının tıkladığı hücrenin indeksini geçeceğiz.
Her şeyin doğru yapılıp yapılmadığını tarayıcıdaki geliştirici panelinden kontrol edebilirsiniz. Örneğin Chrome'da F12 butonu ile açılır . İndeks 4'e sahip bir hücreye tıklanması sonucunda resim aşağıdaki gibi olacaktır: Henüz “logic” adresine sunucu gönderebilecek bir servlet oluşturamadığımız için hata alıyoruz .<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>
- "com.tictactoe" paketinde, "javax.servlet.http.HttpServlet " sınıfından türetilmesi gereken bir "LogicServlet" sınıfı oluşturun . Sınıfta "doGet" yöntemini geçersiz kılın .
Ve tıklanan hücrenin indeksini alacak bir metot ekleyelim. Ayrıca bir eşleme (bu servlet'in isteği yakalayacağı adres) eklemeniz gerekir. Bunu bir ek açıklama aracılığıyla yapmanızı öneririm (ancak zorlukları seviyorsanız web.xml'yi de kullanabilirsiniz). Genel sunucu uygulaması kodu:
Şimdi herhangi bir hücreye tıkladığımızda sunucuda bu hücrenin indeksini alacağız (sunucuyu debugda çalıştırarak emin olabilirsiniz). Ve tıklamanın yapıldığı aynı sayfaya bir yönlendirme olacaktır.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; } }
- Şimdi tıklayabiliriz, ancak bu henüz bir oyun değil. Oyunun mantık sahibi olması için istekler arasında oyunun durumunu (hataların nerede, sıfırların nerede olduğunu) kaydetmeniz gerekiyor. Bunu yapmanın en kolay yolu, bu verileri oturumda depolamaktır. Bu yaklaşımla, oturum sunucuda depolanacak ve istemci "JSESSIONID" adlı bir çerezde bir oturum kimliği alacaktır . Ancak oturumun her seferinde oluşturulması gerekmez, yalnızca oyunun başında oluşturulur. Bunun için "InitServlet" adını vereceğimiz başka bir servlet başlatalım . İçinde yeni bir oturum oluşturacağımız, bir oyun alanı oluşturacağımız, bu oyun alanını ve Sign türünde bir listeyi oturum özniteliklerine koyacağımız ve index.jsp'ye " ilet " göndereceğimiz "doGet" yöntemini geçersiz kılacağız. sayfa. kod:
Ve unutmamak için sunucuyu başlattıktan sonra tarayıcıda açılan başlangıç sayfasını “/start” olarak değiştirelim : Şimdi sunucuyu yeniden başlattıktan sonra tarayıcı geliştirici menüsündeki “Request Headers” bölümündeki alanın herhangi bir hücresine tıkladıktan sonra , oturum kimliğine sahip bir çerez olacaktır: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 { // Create a new session HttpSession currentSession = req.getSession(true); // Create a playing field Field field = new Field(); Map<Integer, Sign> fieldData = field.getField(); // Get a list of field values List<Sign> data = field.getFieldData(); // Adding field parameters to the session (needed to store state between requests) currentSession.setAttribute("field", field); // and field values sorted by index (required for drawing crosses and zeroes) currentSession.setAttribute("data", data); // Redirect request to index.jsp page via server getServletContext().getRequestDispatcher("/index.jsp").forward(req, resp); } }
- İstemciden (tarayıcıdan) gelen istekler arasında durumu saklayabileceğimiz bir havuza sahip olduğumuzda, oyun mantığı yazmaya başlayabiliriz. Sahip olduğumuz mantık “LogicServlet” içindedir . “doGet” metodu ile çalışmamız gerekiyor . Bu davranışı metoda ekleyelim:
- oturumdan Field tipindeki “field” nesnesini alacağız ( “extractField” metoduna çıkaracağız ).
- kullanıcının tıkladığı yere bir çarpı koyun (şimdiye kadar herhangi bir kontrol yapılmadı).
Davranış henüz değişmedi, ancak sunucuyu hata ayıklamada başlatır ve yönlendirmenin gönderildiği satırda bir kesme noktası ayarlarsanız, “ data” nesnesinin “iç kısımlarını” görebilirsiniz . Orada gerçekten de tıklanan dizinin altında “CROSS” görünüyor .@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Get the current session HttpSession currentSession = req.getSession(); // Get the playfield object from the session Field field = extractField(currentSession); // get the index of the cell that was clicked int index = getSelectedIndex(req); // put a cross in the cell that the user clicked on field.getField().put(index, Sign.CROSS); // Read the list of icons List<Sign> data = field.getFieldData(); // Update field object and icon list in session 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; }
- Şimdi ön uçta haçı göstermenin zamanı geldi. Bunun için “index.jsp” dosyası ve “JSTL” teknolojisi ile çalışacağız .
- <head> bölümüne şunu ekleyin:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- Her <td> bloğunun içindeki tabloda, dizini, değerleri hesaplamanıza izin veren bir yapıya değiştirin. Örneğin, sıfır indeksi için:
<td onclick="window.location='/logic?click=0'">${data.get(0).getSign()}</td>
Şimdi, bir hücreye tıkladığınızda, orada bir çarpı işareti görünecektir:
- <head> bölümüne şunu ekleyin:
- Hamlemizi yaptık, sıra “sıfıra” geldi. Ve buraya birkaç kontrol ekleyelim, böylece işaretler zaten dolu olan hücrelere yerleştirilmez.
- Tıklanan hücrenin boş olup olmadığını kontrol etmeniz gerekir. Aksi halde hiçbir şey yapmıyoruz ve oturum parametrelerini değiştirmeden kullanıcıyı aynı sayfaya gönderiyoruz.
- Alandaki hücre sayısı tek olduğundan, bir çarpı konmuş olabilir, ancak sıfıra yer yoktur. Bu nedenle çarpı işareti koyduktan sonra boş bir hücrenin indeksini almaya çalışıyoruz (Field sınıfının getEmptyFieldIndex metodu). İndeks negatif değilse, oraya bir sıfır koyun. 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 { // Get the current session HttpSession currentSession = req.getSession(); // Get the playfield object from the session Field field = extractField(currentSession); // get the index of the cell that was clicked int index = getSelectedIndex(req); Sign currentSign = field.getField().get(index); // Check if the clicked cell is empty. // Otherwise, we do nothing and send the user to the same page without changes // parameters in the session if (Sign.EMPTY != currentSign) { RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/index.jsp"); dispatcher.forward(req, resp); return; } // put a cross in the cell that the user clicked on field.getField().put(index, Sign.CROSS); // Get an empty field cell int emptyFieldIndex = field.getEmptyFieldIndex(); if (emptyFieldIndex >= 0) { field.getField().put(emptyFieldIndex, Sign.NOUGHT); } // Read the list of icons List<Sign> data = field.getFieldData(); // Update field object and icon list in session 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 aşamada artı işareti koyabilirsiniz, yapay zeka sıfırlarla yanıt verir. Ancak oyunun ne zaman durdurulacağının kontrolü yoktur. Bu üç durumda olabilir:
- haçın bir sonraki hareketinden sonra, üç çaprazdan oluşan bir çizgi oluştu;
- sıfır ile bir sonraki dönüş hareketinden sonra, üç sıfırdan oluşan bir çizgi oluşturuldu;
- haçın bir sonraki hareketinden sonra boş hücreler sona erdi.
Bu yöntemin özelliği, kazanan bulunursa, oturuma aşağıdaki paragraflarda "index.jsp" görüntüsünü değiştireceğimiz başka bir parametre eklememizdir ./** * The method checks if there are three X/O's in a row. * returns true/false */ private boolean checkWin(HttpServletResponse response, HttpSession currentSession, Field field) throws IOException { Sign winner = field.checkWin(); if (Sign.CROSS == winner || Sign.NOUGHT == winner) { // Add a flag to indicate that someone has won currentSession.setAttribute("winner", winner); // Read the list of icons List<Sign> data = field.getFieldData(); // Update this list in session currentSession.setAttribute("data", data); // helmet redirect response.sendRedirect("/index.jsp"); return true; } return false; }
- “DoGet” metoduna iki kez “checkWin ” metoduna bir çağrı ekleyelim . Haçı ayarladıktan sonra ilk kez, ikincisi - sıfırı ayarladıktan sonra.
// Check if the cross won after adding the user's last click if (checkWin(resp, currentSession, field)) { return; }
if (emptyFieldIndex >= 0) { field.getField().put(emptyFieldIndex, Sign.NOUGHT); // Check if the zero won after adding the last zero if (checkWin(resp, currentSession, field)) { return; } }
- Davranış açısından neredeyse hiçbir şey değişmedi (işaretlerden biri kazanırsa artık sıfırlar yerleştirilmemesi dışında. “index.jsp” içinde “winner” parametresini kullanalım ve kazananı gösterelim. Tablodan
c:set
sonra yönergeler kullanıyoruz:c:if
Ortalar kazanırsa, "HARİÇLER KAZANDI!" , sıfırlar "HİÇ KAZANDI!" . Sonuç olarak, iki yazıttan birini alabiliriz:<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>
- Bir kazanan varsa, intikam alabilmeniz gerekir. Bunu yapmak için, sunucuya istek gönderecek bir düğmeye ihtiyacınız var. Ve sunucu geçerli oturumu geçersiz kılacak ve isteği tekrar “/start” a yönlendirecektir .
- “index.jsp” içindeki “head” bölümüne “jquery” scriptini yazın . Bu kütüphaneyi kullanarak sunucuya bir istek göndereceğiz.
<script src="<c:url value="/static/jquery-3.6.0.min.js"/>"></script>
- "script" bölümündeki "index.jsp" içinde , sunucuya POST isteği gönderebilen bir işlev ekleyin. Fonksiyonu senkronize hale getireceğiz ve sunucudan bir cevap geldiğinde mevcut sayfayı yeniden yükleyecektir.
<script> function restart() { $.ajax({ url: '/restart', type: 'POST', contentType: 'application/json;charset=UTF-8', async: false, success: function () { location.reload(); } }); } </script>
- "c:if" bloklarının içine , tıklandığında az önce yazdığımız işlevi çağıran bir düğme ekleyin:
<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'sini sunacak yeni bir sunucu uygulaması oluşturalım .
Zaferden sonra, "Yeniden başla" düğmesi görünecektir . Üzerine tıkladıktan sonra alan tamamen temizlenecek ve oyun baştan başlayacaktır.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” içindeki “head” bölümüne “jquery” scriptini yazın . Bu kütüphaneyi kullanarak sunucuya bir istek göndereceğiz.
- Son durumu düşünmeye devam ediyor. Ya kullanıcı artı işareti koyarsa, zafer yoksa ve sıfıra yer yoksa? O zaman bu bir beraberlik ve bunu şimdi işleyeceğiz:
- "LogicServlet" oturumunda , başka bir "draw" parametresi ekleyin , "data" alanını güncelleyin ve "index.jsp" ye bir yönlendirme gönderin :
// If such a cell exists if (emptyFieldIndex >= 0) { … } // If there is no empty cell and no one wins, then it's a draw else { // Add a flag to the session that signals that a draw has occurred currentSession.setAttribute("draw", true); // Read the list of icons List<Sign> data = field.getFieldData(); // Update this list in session currentSession.setAttribute("data", data); // helmet redirect response.sendRedirect("/index.jsp"); return; }
- "index.jsp" içinde şu parametreyi işleyeceğiz:
Çekiliş sonucunda ilgili mesajı ve yeniden başlama teklifini alacağız:<c:if test="${draw}"> <h1>IT'S A DRAW</h1> <br> <button onclick="restart()">Start again</button> </c:if>
- "LogicServlet" oturumunda , başka bir "draw" parametresi ekleyin , "data" alanını güncelleyin ve "index.jsp" ye bir yönlendirme gönderin :
Bu, oyunun yazımını tamamlar.
Çalıştıkları sınıfların ve dosyaların kodu
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 {
// Create a new session
HttpSession currentSession = req.getSession(true);
// Create a playing field
Field field = new Field();
Map<Integer, Sign> fieldData = field.getField();
// Get a list of field values
List<Sign> data = field.getFieldData();
// Adding field parameters to the session (needed to store state between requests)
currentSession.setAttribute("field", field);
// and field values sorted by index (required for drawing crosses and zeroes)
currentSession.setAttribute("data", data);
// Redirect request to index.jsp page via server
getServletContext().getRequestDispatcher("/index.jsp").forward(req, resp);
}
}
Mantık Sunucu Uygulaması
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 {
// Get the current session
HttpSession currentSession = req.getSession();
// Get the playfield object from the session
Field field = extractField(currentSession);
// get the index of the cell that was clicked
int index = getSelectedIndex(req);
Sign currentSign = field.getField().get(index);
// Check if the clicked cell is empty.
// Otherwise, we do nothing and send the user to the same page without changes
// parameters in the session
if (Sign.EMPTY != currentSign) {
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/index.jsp");
dispatcher.forward(req, resp);
return;
}
// put a cross in the cell that the user clicked on
field.getField().put(index, Sign.CROSS);
// Check if the cross has won after adding the user's last click
if (checkWin(resp, currentSession, field)) {
return;
}
// Get an empty field cell
int emptyFieldIndex = field.getEmptyFieldIndex();
if (emptyFieldIndex >= 0) {
field.getField().put(emptyFieldIndex, Sign.NOUGHT);
// Check if the zero won after adding the last zero
if (checkWin(resp, currentSession, field)) {
return;
}
}
// If there is no empty cell and no one wins, then it's a draw
else {
// Add a flag to the session that signals that a draw has occurred
currentSession.setAttribute("draw", true);
// Read the list of icons
List<Sign> data = field.getFieldData();
// Update this list in session
currentSession.setAttribute("data", data);
// helmet redirect
resp.sendRedirect("/index.jsp");
return;
}
// Read the list of icons
List<Sign> data = field.getFieldData();
// Update field object and icon list in session
currentSession.setAttribute("data", data);
currentSession.setAttribute("field", field);
resp.sendRedirect("/index.jsp");
}
/**
* The method checks if there are three X/O's in a row.
* returns true/false
*/
private boolean checkWin(HttpServletResponse response, HttpSession currentSession, Field field) throws IOException {
Sign winner = field.checkWin();
if (Sign.CROSS == winner || Sign.NOUGHT == winner) {
// Add a flag to indicate that someone has won
currentSession.setAttribute("winner", winner);
// Read the list of icons
List<Sign> data = field.getFieldData();
// Update this list in session
currentSession.setAttribute("data", data);
// helmet redirect
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;
}
}
Yeniden BaşlatServlet
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>
ana.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;
}
GO TO FULL VERSION