diff --git a/.gitignore b/.gitignore index 8562103f..33469f3b 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,5 @@ build/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store +/.idea/ diff --git a/README.md b/README.md new file mode 100644 index 00000000..60bb6667 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# Tic-Tac-Toe + +## Description +This project is an implementation of the classic game "Tic-Tac-Toe" in Java using Servlets, JSP, and JSTL. The game operates through a web interface. + +## Installation +To run this project, you will need Maven and an application server that supports Servlets, such as Apache Tomcat. + +### Installation Steps: +1. Clone the repository: + ```bash + git clone https://github.com/AriiSib/project-servlet.git + ``` +2. Navigate to the project directory: + ```bash + cd project-servlet + ``` +3. Compile and build the project using Maven: + ```bash + mvn clean install + ``` +4. Deploy the generated WAR file on your application server. + The application was developed on Tomcat 10.1.24. + Application context:`/` + +## Usage +After deployment, open the URL of your application server in a browser (e.g., `http://localhost:8080/start`) to start the game. diff --git a/pom.xml b/pom.xml index cb226e88..1aa05692 100644 --- a/pom.xml +++ b/pom.xml @@ -18,16 +18,24 @@ - 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.0 + + + 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..971a890e 100644 --- a/src/main/java/com/tictactoe/Field.java +++ b/src/main/java/com/tictactoe/Field.java @@ -53,7 +53,8 @@ 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))) { + && field.get(winPossibility.get(0)) == field.get(winPossibility.get(2)) + && field.get(winPossibility.get(0)) != Sign.EMPTY) { return field.get(winPossibility.get(0)); } } diff --git a/src/main/java/com/tictactoe/InitServlet.java b/src/main/java/com/tictactoe/InitServlet.java new file mode 100644 index 00000000..ee9a2a10 --- /dev/null +++ b/src/main/java/com/tictactoe/InitServlet.java @@ -0,0 +1,29 @@ +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(); + + Field field = new Field(); + Map fieldData = field.getField(); + List data = field.getFieldData(); + + currentSession.setAttribute("field", field); + currentSession.setAttribute("data", data); + + 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..5a6e8838 --- /dev/null +++ b/src/main/java/com/tictactoe/LogicServlet.java @@ -0,0 +1,83 @@ +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.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.getField().get(index); + if (Sign.EMPTY != currentSign) { + RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/index.jsp"); + dispatcher.forward(req, resp); + } + + field.getField().put(index, Sign.CROSS); + if (checkWin(resp, currentSession, field)) { + return; + } + + int emptyFieldIndex = field.getEmptyFieldIndex(); + + if (emptyFieldIndex >= 0) { + field.getField().put(emptyFieldIndex, Sign.NOUGHT); + if (checkWin(resp, currentSession, field)) { + return; + } + } else { + currentSession.setAttribute("draw", true); + List data = field.getFieldData(); + currentSession.setAttribute("data", data); + resp.sendRedirect("/index.jsp"); + return; + } + + List data = field.getFieldData(); + currentSession.setAttribute("data", data); + currentSession.setAttribute("field", field); + + 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.getFieldData(); + 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 request) { + String click = request.getParameter("click"); + boolean isNumeric = click.chars().allMatch(Character::isDigit); + return isNumeric ? Integer.parseInt(click) : 0; + } +} diff --git a/src/main/java/com/tictactoe/RestartServlet.java b/src/main/java/com/tictactoe/RestartServlet.java new file mode 100644 index 00000000..4ca284ab --- /dev/null +++ b/src/main/java/com/tictactoe/RestartServlet.java @@ -0,0 +1,18 @@ +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 java.io.IOException; + +@WebServlet(name = "RestartServlet", value = "/restart") +public class RestartServlet extends HttpServlet { + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + req.getSession().invalidate(); + resp.sendRedirect("/start"); + } +} diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp index 964cc071..bf161e52 100644 --- a/src/main/webapp/index.jsp +++ b/src/main/webapp/index.jsp @@ -1,17 +1,65 @@ +<%@ 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

+
+ +
+ + \ No newline at end of file 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