From 70370eab2beed9bbdee8f5b1bac052c67df24ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9DDenis?= Date: Mon, 30 Oct 2023 19:15:18 +0200 Subject: [PATCH] complete project --- .idea/.gitignore | 8 ++ .idea/encodings.xml | 7 + .idea/misc.xml | 12 ++ .idea/vcs.xml | 6 + pom.xml | 12 +- src/main/java/com/tictactoe/Field.java | 43 ++++-- src/main/java/com/tictactoe/InitServlet.java | 35 +++++ src/main/java/com/tictactoe/LogicServlet.java | 126 ++++++++++++++++++ .../java/com/tictactoe/RestartServlet.java | 16 +++ src/main/webapp/index.jsp | 52 +++++++- src/main/webapp/static/main.css | 11 ++ 11 files changed, 316 insertions(+), 12 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml 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/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..13566b81 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 00000000..aa00ffab --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..b784bbc8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index cb226e88..772ef54d 100644 --- a/pom.xml +++ b/pom.xml @@ -12,8 +12,8 @@ UTF-8 - 18 - 18 + 1.7 + 1.7 @@ -37,6 +37,14 @@ maven-war-plugin 3.3.2 + + org.apache.maven.plugins + maven-compiler-plugin + + 9 + 9 + + \ No newline at end of file diff --git a/src/main/java/com/tictactoe/Field.java b/src/main/java/com/tictactoe/Field.java index c52d2a0d..00c1341a 100644 --- a/src/main/java/com/tictactoe/Field.java +++ b/src/main/java/com/tictactoe/Field.java @@ -1,8 +1,6 @@ package com.tictactoe; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; public class Field { @@ -21,11 +19,23 @@ public Field() { field.put(8, Sign.EMPTY); } + public Field(Map anotherField){ + field = new HashMap<>(anotherField); + } + public Map getField() { return field; } - public int getEmptyFieldIndex() { + public int getFieldIndex() { + Field imagineField = new Field(field); + + int index = getWinningIndex(Sign.NOUGHT, imagineField); //find winning move + if(index >= 0) return index; + + index = getWinningIndex(Sign.CROSS, imagineField); //predict cross winning + if(index >= 0) return index; + return field.entrySet().stream() .filter(e -> e.getValue() == Sign.EMPTY) .map(Map.Entry::getKey) @@ -39,7 +49,7 @@ public List getFieldData() { .collect(Collectors.toList()); } - public Sign checkWin() { + public boolean checkWin(Sign sign) { List> winPossibilities = List.of( List.of(0, 1, 2), List.of(3, 4, 5), @@ -52,11 +62,26 @@ 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 (field.get(winPossibility.get(0)) == sign + && field.get(winPossibility.get(1)) == sign + && field.get(winPossibility.get(2)) == sign) { + return true; } } - return Sign.EMPTY; + return false; } + public int getWinningIndex(Sign sign, Field imagineField) { + for (Map.Entry entry: field.entrySet()) { + if(entry.getValue() == Sign.EMPTY){ + imagineField.getField().put(entry.getKey(), sign); + if(imagineField.checkWin(sign)){ + return entry.getKey(); + } + imagineField.getField().put(entry.getKey(), Sign.EMPTY); + } + } + return -1; + } + + } \ No newline at end of file diff --git a/src/main/java/com/tictactoe/InitServlet.java b/src/main/java/com/tictactoe/InitServlet.java new file mode 100644 index 00000000..b2eef4c8 --- /dev/null +++ b/src/main/java/com/tictactoe/InitServlet.java @@ -0,0 +1,35 @@ +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 { + // Створення нової сесії + HttpSession currentSession = req.getSession(true); + + // Створення ігрового поля + Field field = new Field(); + Map fieldData = field.getField(); + + // Отримання списку значень поля + List data = field.getFieldData(); + + // Додавання до сесії параметрів поля (потрібно буде для зберігання стану між запитами) + currentSession.setAttribute("field", field); + // та значень поля, що відсортовані за індексом (потрібно для промальовки хрестиків і нуликів) + currentSession.setAttribute("data", data); + + // Перенаправлення запиту на сторінку index.jsp через сервер + getServletContext().getRequestDispatcher("/index.jsp").forward(req, resp); + } +} \ No newline at end of file diff --git a/src/main/java/com/tictactoe/LogicServlet.java b/src/main/java/com/tictactoe/LogicServlet.java new file mode 100644 index 00000000..e9a39962 --- /dev/null +++ b/src/main/java/com/tictactoe/LogicServlet.java @@ -0,0 +1,126 @@ +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="LogicServer", 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); + return; + } + + // Ставимо хрестик в ячейці, на яку клікнув користувач + field.getField().put(index, Sign.CROSS); + + if (checkWin(resp, currentSession, field)) { + return; + } + + // Отримуємо порожню ячейку поля + int emptyFieldIndex = field.getFieldIndex(); + + 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 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; + } + + private boolean checkWin(HttpServletResponse response, HttpSession currentSession, Field field) throws IOException { + if (field.checkWin(Sign.CROSS)) { + // Додаємо прапорець, який показує, що хтось переміг + currentSession.setAttribute("winner", Sign.CROSS); + + // Рахуємо список значків + List data = field.getFieldData(); + + // Оновлюємо цей список у сесії + currentSession.setAttribute("data", data); + + // Шлемо редирект + response.sendRedirect("/index.jsp"); + return true; + } + if(field.checkWin(Sign.NOUGHT)){ + // Додаємо прапорець, який показує, що хтось переміг + currentSession.setAttribute("winner", Sign.NOUGHT); + + // Рахуємо список значків + List data = field.getFieldData(); + + // Оновлюємо цей список у сесії + currentSession.setAttribute("data", data); + + // Шлемо редирект + response.sendRedirect("/index.jsp"); + return true; + } + return false; + } +} diff --git a/src/main/java/com/tictactoe/RestartServlet.java b/src/main/java/com/tictactoe/RestartServlet.java new file mode 100644 index 00000000..019c3ce8 --- /dev/null +++ b/src/main/java/com/tictactoe/RestartServlet.java @@ -0,0 +1,16 @@ +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"); + } +} diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp index 964cc071..e50fcefc 100644 --- a/src/main/webapp/index.jsp +++ b/src/main/webapp/index.jsp @@ -1,16 +1,66 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@page import="com.tictactoe.Sign" %> + <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> Tic-Tac-Toe + + +

Tic-Tac-Toe

+ + + + + + + + + + + + + + + + - diff --git a/src/main/webapp/static/main.css b/src/main/webapp/static/main.css index e69de29b..b2debc92 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; +}
${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()}