From 12978cf42ff4816c407ec35b9c26dc9aae0955ab Mon Sep 17 00:00:00 2001 From: Sergey Date: Thu, 8 May 2025 07:01:48 +0300 Subject: [PATCH] Complete project-servlet --- pom.xml | 17 ++- src/main/java/com/tictactoe/Field.java | 38 +++--- src/main/java/com/tictactoe/InitServlet.java | 36 ++++++ src/main/java/com/tictactoe/LogicServlet.java | 108 ++++++++++++++++++ .../java/com/tictactoe/RestartServlet.java | 18 +++ src/main/webapp/index.jsp | 51 ++++++++- src/main/webapp/static/main.css | 11 ++ 7 files changed, 251 insertions(+), 28 deletions(-) create mode 100644 src/main/java/com/tictactoe/InitServlet.java create mode 100644 src/main/java/com/tictactoe/LogicServlet.java create mode 100644 src/main/java/com/tictactoe/RestartServlet.java diff --git a/pom.xml b/pom.xml index cb226e88..9d23a1a9 100644 --- a/pom.xml +++ b/pom.xml @@ -18,15 +18,20 @@ - javax.servlet - javax.servlet-api - 4.0.1 + jakarta.servlet + jakarta.servlet-api + 6.1.0 provided - javax.servlet - jstl - 1.2 + jakarta.servlet.jsp.jstl + jakarta.servlet.jsp.jstl-api + 3.0.2 + + + org.glassfish.web + jakarta.servlet.jsp.jstl + 3.0.1 diff --git a/src/main/java/com/tictactoe/Field.java b/src/main/java/com/tictactoe/Field.java index c52d2a0d..5ee6ca31 100644 --- a/src/main/java/com/tictactoe/Field.java +++ b/src/main/java/com/tictactoe/Field.java @@ -6,34 +6,34 @@ import java.util.stream.Collectors; public class Field { - private final Map field; + private final Map innerField; public Field() { - field = new HashMap<>(); - field.put(0, Sign.EMPTY); - field.put(1, Sign.EMPTY); - field.put(2, Sign.EMPTY); - field.put(3, Sign.EMPTY); - field.put(4, Sign.EMPTY); - field.put(5, Sign.EMPTY); - field.put(6, Sign.EMPTY); - field.put(7, Sign.EMPTY); - field.put(8, Sign.EMPTY); + innerField = new HashMap<>(); + innerField.put(0, Sign.EMPTY); + innerField.put(1, Sign.EMPTY); + innerField.put(2, Sign.EMPTY); + innerField.put(3, Sign.EMPTY); + innerField.put(4, Sign.EMPTY); + innerField.put(5, Sign.EMPTY); + innerField.put(6, Sign.EMPTY); + innerField.put(7, Sign.EMPTY); + innerField.put(8, Sign.EMPTY); } - public Map getField() { - return field; + public Map getInnerField() { + return innerField; } public int getEmptyFieldIndex() { - return field.entrySet().stream() + return innerField.entrySet().stream() .filter(e -> e.getValue() == Sign.EMPTY) .map(Map.Entry::getKey) .findFirst().orElse(-1); } - public List getFieldData() { - return field.entrySet().stream() + public List getInnerFieldData() { + return innerField.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .map(Map.Entry::getValue) .collect(Collectors.toList()); @@ -52,9 +52,9 @@ public Sign checkWin() { ); for (List winPossibility : winPossibilities) { - if (field.get(winPossibility.get(0)) == field.get(winPossibility.get(1)) - && field.get(winPossibility.get(0)) == field.get(winPossibility.get(2))) { - return field.get(winPossibility.get(0)); + if (innerField.get(winPossibility.get(0)) == innerField.get(winPossibility.get(1)) + && innerField.get(winPossibility.get(0)) == innerField.get(winPossibility.get(2))) { + return innerField.get(winPossibility.get(0)); } } return Sign.EMPTY; diff --git a/src/main/java/com/tictactoe/InitServlet.java b/src/main/java/com/tictactoe/InitServlet.java new file mode 100644 index 00000000..970dd1b4 --- /dev/null +++ b/src/main/java/com/tictactoe/InitServlet.java @@ -0,0 +1,36 @@ +package com.tictactoe; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.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 { + // Создание новой сессии + HttpSession currentSession = req.getSession(true); + + // Создание игрового поля + Field field = new Field(); + + // Получение списка значений поля + List data = field.getInnerFieldData(); + + // Добавление в сессию параметров поля (нужно будет для хранения состояния между запросами) + currentSession.setAttribute("field", field); + // и значений поля, отсортированных по индексу (нужно для отрисовки крестиков и ноликов) + currentSession.setAttribute("data", data); + + // Перенаправление запроса на страницу index.jsp через сервер + getServletContext().getRequestDispatcher("/index.jsp").forward(req, resp); + } +} diff --git a/src/main/java/com/tictactoe/LogicServlet.java b/src/main/java/com/tictactoe/LogicServlet.java new file mode 100644 index 00000000..428da57a --- /dev/null +++ b/src/main/java/com/tictactoe/LogicServlet.java @@ -0,0 +1,108 @@ +package com.tictactoe; + +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + +import java.io.IOException; +import java.util.Enumeration; +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 { + // Получаем текущую сессию + HttpSession currentSession = req.getSession(); + // Получаем объект игрового поля из сессии + Field field = extractField(currentSession); + + // получаем индекс ячейки, по которой произошел клик + int index = getSelectedIndex(req); + Sign currentSign = field.getInnerField().get(index); + + // Проверяем, что ячейка, по которой был клик пустая. + // Иначе ничего не делаем и отправляем пользователя на ту же страницу без изменений + // параметров в сессии + if (Sign.EMPTY != currentSign || field.checkWin() == Sign.CROSS || field.checkWin() == Sign.NOUGHT) { + RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/index.jsp"); + dispatcher.forward(req, resp); + return; + } + // ставим крестик в ячейке, по которой кликнул пользователь + field.getInnerField().put(index, Sign.CROSS); + if (checkWin(resp, currentSession, field)) { + return; + } + + // Получаем пустую ячейку поля + int emptyFieldIndex = field.getEmptyFieldIndex(); + + if (emptyFieldIndex >= 0) { + field.getInnerField().put(emptyFieldIndex, Sign.NOUGHT); + if (checkWin(resp, currentSession, field)) { + return; + } + } + // Если пустой ячейки нет и никто не победил - значит это ничья + else { + // Добавляем в сессию флаг, который сигнализирует что произошла ничья + currentSession.setAttribute("draw", true); + + // Считаем список значков + List data = field.getInnerFieldData(); + + // Обновляем этот список в сессии + currentSession.setAttribute("data", data); + + // Шлем редирект + resp.sendRedirect("/index.jsp"); + return; + } + + // Считаем список значков + List data = field.getInnerFieldData(); + + // Обновляем объект поля и список значков в сессии + currentSession.setAttribute("data", data); + resp.sendRedirect("/index.jsp"); + } + + private boolean checkWin(HttpServletResponse response, HttpSession currentSession, Field field) throws IOException { + Sign winner = field.checkWin(); + if (Sign.CROSS == winner || Sign.NOUGHT == winner) { + // Добавляем флаг, который показывает что кто-то победил + currentSession.setAttribute("winner", winner); + + // Считаем список значков + List data = field.getInnerFieldData(); + + // Обновляем этот список в сессии + currentSession.setAttribute("data", data); + + // Шлем редирект + response.sendRedirect("/index.jsp"); + return true; + } + return false; + } + + 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; + } + + private int getSelectedIndex(HttpServletRequest req) { + return Integer.parseInt(req.getParameter("click")); + } + +} diff --git a/src/main/java/com/tictactoe/RestartServlet.java b/src/main/java/com/tictactoe/RestartServlet.java new file mode 100644 index 00000000..1f630ecf --- /dev/null +++ b/src/main/java/com/tictactoe/RestartServlet.java @@ -0,0 +1,18 @@ +package com.tictactoe; + +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.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"); + } +} diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp index 964cc071..3dcfe26c 100644 --- a/src/main/webapp/index.jsp +++ b/src/main/webapp/index.jsp @@ -1,16 +1,61 @@ +<%@ page import="com.tictactoe.Sign" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> - +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> Tic-Tac-Toe + +

Tic-Tac-Toe

+ + + + + + + + + + + + + + + + +
${data.get(0).getSign()}${data.get(1).getSign()}${data.get(2).getSign()}
${data.get(3).getSign()}${data.get(4).getSign()}${data.get(5).getSign()}
${data.get(6).getSign()}${data.get(7).getSign()}${data.get(8).getSign()}
+
+ + - + +

CROSSES WIN!

+ +
+ +

NOUGHTS WIN!

+ +
+ +

IT'S A DRAW

+
+ +
diff --git a/src/main/webapp/static/main.css b/src/main/webapp/static/main.css index e69de29b..33049a9b 100644 --- a/src/main/webapp/static/main.css +++ b/src/main/webapp/static/main.css @@ -0,0 +1,11 @@ +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; +} \ No newline at end of file