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
+
+
+ | ${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()} |
+
-
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;
+}