Hari ini kami akan menulis game Tic-Tac-Toe menggunakan servlet dan JSP.
Proyek ini akan sedikit berbeda dari yang sebelumnya. Ini tidak hanya berisi tugas, tetapi juga penjelasan tentang cara melakukannya. Artinya, ini akan menjadi proyek dari seri "CARA ...".
Petunjuk:
- Garpu dari repositori: https://github.com/CodeGymCC/project-servlet.git
- Unduh versi proyek Anda ke komputer Anda.
- Siapkan peluncuran aplikasi di IDEA:
- Alt + Shift + F9 -> Edit Konfigurasi… -> Alt + insert -> tom (ke dalam bilah pencarian) -> Lokal.
- Setelah itu, Anda perlu mengklik "KONFIGURASI" dan menunjukkan di mana arsip dengan Tomcat diunduh dan dibongkar.
- Di tab "Penerapan": Alt + sisipkan -> Artefak… -> tic-tac-toe: perang meledak -> OK.
- Di bidang "Konteks aplikasi": biarkan hanya "/" (garis miring).
- Tekan "TERAPKAN".
- Tutup jendela pengaturan.
- Lakukan uji coba pertama dari konfigurasi yang disesuaikan. Jika semuanya dilakukan dengan benar, browser default Anda akan terbuka, di mana itu adalah:
- Buka file "pom.xml" . Ada 2 dependensi di blok "dependensi" .
javax.servlet-api
bertanggung jawab atas spesifikasi servlet. Lingkup "disediakan" diperlukan selama pengembangan, tetapi tidak diperlukan saat runtime (Tomcat sudah memiliki ketergantungan ini di folder lib).jstl
– dapat dianggap sebagai mesin template.- Ada 3 file di folder “webapp” :
index.jsp
- ini adalah template kami (mirip dengan halaman HTML). Ini akan berisi markup dan skrip. Ini adalah file bernama "indeks" yang diberikan sebagai halaman awal, jika tidak ada konfigurasi, yang kita lihat di langkah 3./static/main.css
- file untuk gaya. Seperti pada proyek sebelumnya, semua yang ada di sini terserah Anda, cat sesuai keinginan./static/jquery-3.6.0.min.js
- ketergantungan frontend yang akan didistribusikan oleh server kami sebagai statis.- Paket "com.tictactoe" akan berisi semua kode Java. Saat ini ada 2 kelas:
Sign
- enum, yang bertanggung jawab atas "cross / zero / void" .Field
adalah bidang kami. Kelas ini memiliki peta "lapangan" . Prinsip penyimpanan data adalah sebagai berikut: sel bidang tic-tac-toe diberi nomor dari nol. Di baris pertama 0, 1 dan 2. Di baris kedua: 3, 4 dan 5. Dan seterusnya. Ada juga 3 metode. "getEmptyFieldIndex" mencari sel kosong pertama (ya, lawan kita tidak akan terlalu pintar). "checkWin" memeriksa apakah permainan sudah berakhir. Jika ada deretan tiga persilangan, ia mengembalikan persilangan; jika ada deretan tiga nol, ia mengembalikan nol. Jika tidak, itu kosong. "getFieldData" - mengembalikan nilai peta "bidang" sebagai daftar yang diurutkan dalam urutan indeks menaik.- Penjelasan tentang template sudah selesai, sekarang Anda dapat memulai tugas. Mari kita mulai dengan menggambar tabel berukuran 3 kali 3. Untuk melakukannya, tambahkan kode berikut ke “index.jsp” :
Kami kemudian akan menghapus angka dalam tabel dan menggantinya dengan tanda silang, nol atau bidang kosong. Juga, di dalam tag "head", sertakan file gaya. Untuk melakukan ini, tambahkan baris:<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">
Konten file gaya terserah Anda. Saya menggunakan yang ini:
Setelah dijalankan, hasil saya terlihat seperti ini: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; }
- Sekarang mari tambahkan fungsionalitas berikut: ketika sel diklik, permintaan akan dikirim ke server, di mana kita akan meneruskan indeks sel yang diklik sebagai parameter. Tugas ini dapat dibagi menjadi dua bagian: mengirim permintaan dari depan, menerima permintaan di server. Mari kita mulai dari depan untuk perubahan.
Mari tambahkan parameter "onclick" ke setiap tag "d" . Dalam nilainya, kami menunjukkan perubahan halaman saat ini ke URL yang ditentukan. Servlet yang akan bertanggung jawab atas logika akan memiliki URL “/logic” . Dan itu akan membutuhkan parameter yang disebut "klik" . Jadi kami akan meneruskan indeks sel yang diklik pengguna.
Anda dapat memeriksa apakah semuanya dilakukan dengan benar melalui panel pengembang di browser. Misalnya, di Chrome, ini terbuka dengan tombol F12 . Sebagai hasil dari mengklik sel dengan indeks 4, gambarnya adalah sebagai berikut: Kami mendapatkan kesalahan karena kami belum membuat servlet yang dapat mengirim server ke alamat "logic" .<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>
- Dalam paket "com.tictactoe" buat kelas "LogicServlet" yang harus diturunkan dari kelas "javax.servlet.http.HttpServlet" . Di kelas, timpa metode "doGet" .
Dan mari tambahkan metode yang akan mendapatkan indeks sel yang diklik. Anda juga perlu menambahkan pemetaan (alamat di mana servlet ini akan mencegat permintaan). Saya sarankan melakukan ini melalui anotasi (tetapi jika Anda menyukai kesulitan, Anda juga dapat menggunakan web.xml). Kode servlet umum:
Sekarang, saat mengklik sel mana saja, kita akan mendapatkan indeks sel ini di server (Anda dapat memastikannya dengan menjalankan server dalam debug). Dan akan ada pengalihan ke halaman yang sama dari mana klik dilakukan.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; } }
- Sekarang kita bisa mengklik, tapi ini belum menjadi permainan. Agar gim memiliki logika, Anda harus menyimpan status gim (di mana persilangan, di mana nol) di antara permintaan. Cara termudah untuk melakukannya adalah dengan menyimpan data ini di sesi. Dengan pendekatan ini, sesi akan disimpan di server, dan klien akan menerima ID sesi dalam cookie bernama “JSESSIONID” . Namun sesi tersebut tidak perlu dibuat setiap saat, melainkan hanya di awal permainan. Mari kita mulai servlet lain untuk ini, yang akan kita sebut "InitServlet" . Kami akan mengganti metode "doGet" di dalamnya , di mana kami akan membuat sesi baru, membuat lapangan bermain, meletakkan lapangan bermain ini dan daftar jenis Masuk atribut sesi, dan kirim "maju" ke index.jsp halaman. Kode:
Dan jangan lupa, mari kita ubah halaman awal yang terbuka di browser setelah memulai server menjadi "/ mulai" : Sekarang setelah memulai ulang server dan mengklik sel mana saja di menu pengembang browser di bagian "Header Permintaan" , akan ada cookie dengan session ID :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); } }
- Saat kita memiliki repositori tempat kita dapat menyimpan status di antara permintaan dari klien (browser), kita dapat mulai menulis logika game. Logika yang kita miliki ada di “LogicServlet” . Kita perlu bekerja dengan metode "doGet" . Mari tambahkan perilaku ini ke metode:
- kita akan mendapatkan objek "bidang" dari jenis Bidang dari sesi (kita akan membawanya ke metode "extractField" ).
- beri tanda silang di mana pengguna mengklik (sejauh ini tanpa pemeriksaan apa pun).
Perilaku belum berubah, tetapi jika Anda memulai server dalam debug dan menyetel breakpoint pada baris tempat pengalihan dikirim, Anda dapat melihat "jeroan" dari objek " data " . Di sana, memang , "CROSS" muncul di bawah indeks yang diklik.@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; }
- Sekarang saatnya menampilkan salib di bagian depan. Untuk melakukan ini, kami akan bekerja dengan file "index.jsp" dan teknologi "JSTL" .
- Di bagian <head> tambahkan:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- Pada tabel di dalam setiap blok <td>, ubah indeks menjadi konstruksi yang memungkinkan Anda menghitung nilai. Misalnya, untuk indeks nol:
<td onclick="window.location='/logic?click=0'">${data.get(0).getSign()}</td>
Sekarang, saat Anda mengklik sel, tanda silang akan muncul di sana:
- Di bagian <head> tambahkan:
- Kami telah bergerak, sekarang giliran "nol". Dan mari tambahkan beberapa tanda centang di sini, agar tanda tidak ditempatkan di sel yang sudah ditempati.
- Anda perlu memeriksa apakah sel yang diklik kosong. Jika tidak, kami tidak melakukan apa pun dan mengarahkan pengguna ke halaman yang sama tanpa mengubah parameter sesi.
- Karena jumlah sel di lapangan ganjil, ada kemungkinan salib ditempatkan, tetapi tidak ada ruang untuk nol. Oleh karena itu, setelah kami memberi tanda silang, kami mencoba mendapatkan indeks dari sel kosong (metode getEmptyFieldIndex dari kelas Field). Jika indeksnya tidak negatif, beri nol di sana. Kode:
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; } }
- Pada tahap ini, Anda dapat memberi tanda silang, jawaban AI dengan nol. Tetapi tidak ada pemeriksaan kapan harus menghentikan permainan. Ini bisa terjadi dalam tiga kasus:
- setelah gerakan salib berikutnya, garis tiga salib terbentuk;
- setelah gerakan kembali berikutnya dengan nol, garis tiga nol terbentuk;
- setelah gerakan salib berikutnya, sel-sel kosong berakhir.
Keunikan dari metode ini adalah jika pemenang ditemukan, kami menambahkan parameter lain ke sesi, yang dengannya kami akan mengubah tampilan di "index.jsp" di paragraf berikut./** * 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; }
- Mari tambahkan panggilan ke metode "checkWin " dua kali ke metode "doGet" . Pertama kali setelah mengatur salib, yang kedua - setelah mengatur nol.
// 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; } }
- Dalam hal perilaku, hampir tidak ada yang berubah (kecuali jika salah satu tanda menang, nol tidak lagi ditempatkan. Mari gunakan parameter "pemenang" di "index.jsp" dan tampilkan pemenangnya. Kami menggunakan arahan setelah tabel:
c:set
c:if
Jika salib menang, pesan “CROSSES WIN!” , jika angka nolnya adalah “NOUGHTS WIN!” . Hasilnya, kita bisa mendapatkan salah satu dari dua prasasti:<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>
- Jika ada pemenang, Anda harus bisa membalas dendam. Untuk melakukan ini, Anda memerlukan tombol yang akan mengirimkan permintaan ke server. Dan server akan membatalkan sesi saat ini dan mengalihkan permintaan kembali ke “/ start” .
- Pada “index.jsp” pada bagian “head” , tulis script “jquery” . Menggunakan perpustakaan ini, kami akan mengirimkan permintaan ke server.
<script src="<c:url value="/static/jquery-3.6.0.min.js"/>"></script>
- Di "index.jsp" di bagian "skrip" , tambahkan fungsi yang dapat mengirim permintaan POST ke server. Kami akan membuat fungsi sinkron, dan ketika respons datang dari server, itu akan memuat ulang halaman saat ini.
<script> function restart() { $.ajax({ url: '/restart', type: 'POST', contentType: 'application/json;charset=UTF-8', async: false, success: function () { location.reload(); } }); } </script>
- Di dalam blok "c:if" , tambahkan tombol yang, ketika diklik, memanggil fungsi yang baru saja kita tulis:
<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>
- Mari buat servlet baru yang akan melayani URL "/restart" .
Setelah kemenangan, tombol “Mulai lagi” akan muncul . Setelah mengkliknya, bidang akan sepenuhnya dihapus, dan permainan akan dimulai kembali.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"); } }
- Pada “index.jsp” pada bagian “head” , tulis script “jquery” . Menggunakan perpustakaan ini, kami akan mengirimkan permintaan ke server.
- Tetap mempertimbangkan situasi terakhir. Bagaimana jika pengguna memberi tanda silang, tidak ada kemenangan, dan tidak ada tempat untuk nol? Maka ini undian, dan kami akan memprosesnya sekarang:
- Di sesi "LogicServlet" , tambahkan parameter lain "draw" , perbarui bidang "data" dan kirim pengalihan ke "index.jsp" :
// 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; }
- Di "index.jsp" kami akan memproses parameter ini:
Sebagai hasil pengundian, kami akan menerima pesan terkait dan tawaran untuk memulai kembali:<c:if test="${draw}"> <h1>IT'S A DRAW</h1> <br> <button onclick="restart()">Start again</button> </c:if>
- Di sesi "LogicServlet" , tambahkan parameter lain "draw" , perbarui bidang "data" dan kirim pengalihan ke "index.jsp" :
Ini melengkapi penulisan game.
Kode kelas dan file tempat mereka bekerja
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);
}
}
LogikaServlet
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;
}
}
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");
}
}
indeks.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;
}
GO TO FULL VERSION