diff --git a/.gitignore b/.gitignore
index 8a30d25..c0e1299 100644
--- a/.gitignore
+++ b/.gitignore
@@ -396,3 +396,6 @@ FodyWeavers.xsd
# JetBrains Rider
*.sln.iml
+
+# CUSTOM
+target
\ No newline at end of file
diff --git a/ProgettoImplementativo_A_22-23.pdf b/ProgettoImplementativo_A_22-23.pdf
new file mode 100644
index 0000000..d5d9134
Binary files /dev/null and b/ProgettoImplementativo_A_22-23.pdf differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..f38f7f7
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,16 @@
+
+
+ 4.0.0
+
+ net.berack.upo
+ upo-ai
+ 1.0-SNAPSHOT
+
+
+ 1.17
+ 1.17
+
+
+
\ No newline at end of file
diff --git a/src/main/java/net/berack/upo/ai/Main.java b/src/main/java/net/berack/upo/ai/Main.java
new file mode 100644
index 0000000..b801ed0
--- /dev/null
+++ b/src/main/java/net/berack/upo/ai/Main.java
@@ -0,0 +1,8 @@
+package net.berack.upo.ai;
+
+public class Main {
+
+ public static void main(String[] args) {
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/berack/upo/ai/problem1/AStar.java b/src/main/java/net/berack/upo/ai/problem1/AStar.java
new file mode 100644
index 0000000..6dca0d5
--- /dev/null
+++ b/src/main/java/net/berack/upo/ai/problem1/AStar.java
@@ -0,0 +1,159 @@
+package net.berack.upo.ai.problem1;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.PriorityQueue;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * Una classe che risolve problemi utilizzando l'algoritmo A*.
+ * @author Berack96
+ * @param State la classe degli stati in cui si trova il problema da risolvere
+ * @param Action la classe di azioni che si possono compiere da uno stato e l'altro
+ */
+public class AStar {
+
+ private Function actions;
+ private BiFunction transition;
+
+ private BiFunction heuristic;
+ private TriFunction cost;
+
+ /**
+ * Crea una istanza dell'algoritmo A* con una funzione di azioni possibili da compiere dato uno stato
+ * e una funzione di transizione che dato uno stato permette di raggiungerne un'altro tramite un'azione.
+ *
+ * @param actions la funzione che da uno stato permette di vedere le possibili azioni da compiere
+ * @param transition la funzione di tansizione che ci permette di raggiungere uno stato da un'altro tramite un'azione
+ */
+ public AStar(Function actions, BiFunction transition) {
+ this.actions = Objects.requireNonNull(actions);
+ this.transition = Objects.requireNonNull(transition);
+
+ this.cost = (s1,s2, a) -> 1;
+ this.heuristic = (s1,s2) -> 0;
+ }
+
+ /**
+ * Permette di impostare la funzione di costo che indica, facendo un'azione su uno stato e raggiungendone un'altro, quanto devo spendere, ovvero il costo.
+ * Di default la funzione di costo equivale sempre ad 1
+ * @param cost la funzione di costo
+ * @return se stesso in modo da poter concatenare i vari set (un pò come una factory)
+ */
+ public AStar setCost(TriFunction cost) {
+ this.cost = Objects.requireNonNull(cost);
+ return this;
+ }
+
+ /**
+ * Permette di impostare una funzione di transizione che indica, facendo un'azione su uno stato, a quale stato possiamo arrivare.
+ * @param transition la funzione di transizione
+ * @return se stesso in modo da poter concatenare i vari set (un pò come una factory)
+ */
+ public AStar setTransition(BiFunction transition) {
+ this.transition = Objects.requireNonNull(transition);
+ return this;
+ }
+
+ /**
+ * Permette di impostare una funzione euristica che indica, prendendo uno stato e uno stato goal, la distanza di costo approssimata per arrivare al goal
+ * @param heuristic la funzione euristica
+ * @return se stesso in modo da poter concatenare i vari set (un pò come una factory)
+ */
+ public AStar setHeuristic(BiFunction heuristic) {
+ this.heuristic = Objects.requireNonNull(heuristic);
+ return this;
+ }
+
+ /**
+ * Permette di impostare una funzione che indica, a partire da uno stato, quali sono le azioni che posso svolgere
+ * @param actions la funzione di azioni
+ * @return se stesso in modo da poter concatenare i vari set (un pò come una factory)
+ */
+ public AStar setActions(Function actions) {
+ this.actions = Objects.requireNonNull(actions);
+ return this;
+ }
+
+ /**
+ * Permette di risolvere il problema dopo aver indicato le varie funzioni.
+ * In input viene richiesto uno stato iniziale e uno stato goal da raggiungere dallo stato corrente.
+ * La funzione utilizzata per il raggiungimento dello stato goal è equals(), quindi è richiesto che
+ * la classe State abbia implementato correttamente la funzione equals().
+ *
+ * @param initial lo stato corrente
+ * @param goal lo stato da raggiungere
+ * @return una sequenza di azioni per poter raggiungere lo stato goal oppure null se non è possibile
+ */
+ public List solve(State initial, State goal) {
+ Objects.requireNonNull(initial);
+ Objects.requireNonNull(goal);
+
+ NodeState found = null;
+ var list = new PriorityQueue();
+ list.add(new NodeState(null, initial, null, 0));
+
+ while(list.size() > 0) {
+ var current = list.poll();
+ if(current.state.equals(goal)) {
+ found = current;
+ break;
+ }
+
+ for(var action : this.actions.apply(current.state)) try {
+
+ var next = this.transition.apply(current.state, action);
+
+ var cost = this.cost.apply(current.state, next, action);
+ var dist = this.heuristic.apply(next, goal);
+
+ list.add(new NodeState(current, next, action, current.cost + cost + dist));
+
+ } catch (Exception ignore) {}
+ }
+
+ if(found == null) return null;
+
+ var path = new ArrayList();
+ while(found != null) {
+ path.add(found.action);
+ found = found.parent;
+ }
+
+ Collections.reverse(path);
+ return path;
+ }
+
+
+ /**
+ * Classe privata per mantenere i dati all'interno della PriorityQueue.
+ */
+ private class NodeState implements Comparable {
+ NodeState parent;
+ State state;
+ Action action;
+ int cost;
+
+ NodeState(NodeState parent, State state, Action action, int cost) {
+ this.parent = parent;
+ this.state = state;
+ this.action = action;
+ this.cost = cost;
+ }
+
+ @Override
+ public int compareTo(NodeState other) {
+ return this.cost - other.cost;
+ }
+ }
+
+ /**
+ * Interfaccia privata per poter indicare una TriFunction, ovvero una funzione che accetta in input 3 parametri
+ */
+ @FunctionalInterface private interface TriFunction {
+ R apply(T t, U u, V v);
+ }
+}
diff --git a/src/main/java/net/berack/upo/ai/problem1/Puzzle8.java b/src/main/java/net/berack/upo/ai/problem1/Puzzle8.java
new file mode 100644
index 0000000..97be53c
--- /dev/null
+++ b/src/main/java/net/berack/upo/ai/problem1/Puzzle8.java
@@ -0,0 +1,231 @@
+package net.berack.upo.ai.problem1;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Classe utilizzata per la rappresentazione del problema delle 8 tessere.
+ * In essa ci sono dei metodi base per il modello del problema tra cui getter e setter.
+ * Oltre a questi ci sono dei metodi per la risoluzione del problema sia manuale,
+ * che automatico tramite l'algoritmo A*
+ *
+ * @author Berack96
+ */
+public class Puzzle8 implements Iterable {
+ public static final int LENGTH = 3;
+
+ /**
+ * Possibili movimenti della tessera "vuota"
+ */
+ public enum Move {
+ UP,
+ DOWN,
+ LEFT,
+ RIGHT
+ }
+
+ private int[] puzzle = new int[LENGTH * LENGTH];
+ private int blank = 0;
+
+ /**
+ * Genera una nuova istanza del problema con le tessere posizionate in modo casuale.
+ */
+ public Puzzle8() {
+ var values = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8);
+ Collections.shuffle(values);
+
+ for(var i = 0; i < this.puzzle.length; i++) {
+ this.puzzle[i] = values.get(i).intValue();
+ if(this.puzzle[i] == 0) this.blank = i;
+ }
+ }
+
+ /**
+ * Genera una nuova istanza del problema con le tessere posizionate come il
+ * problema passato in input e dopo aver eseguito la mossa indicata.
+ *
+ * @param original il puzzle di partenza che si vuole copiare
+ * @param move la mossa da eseguire
+ *
+ * @throws UnsupportedOperationException nel caso la mossa non sia disponibile.
+ */
+ public Puzzle8(Puzzle8 original, Move move) {
+ Arrays.copyOf(original.puzzle, this.puzzle.length);
+ this.blank = original.blank;
+
+ this.move(move);
+ }
+
+ /**
+ * Inizializza il problema con gli interi passati.
+ * L'array inserito deve essere di 9 valori dato che la matrice è di 3x3.
+ * I valori saranno posizionati a tre a tre nella matrice, quindi i primi 3 saranno nella
+ * prima riga, i secondi tre nella seconda e i terzi saranno nella terza riga.
+ * Nel caso in cui vengano passati meno valori una eccezione verrà sollevata.
+ * Gli elementi inseriti devono essere UNICI e compresi tra 0 e 8 estremi inclusi.
+ *
+ * @param values i valori a cui inizializzare il problema.
+ * @throws IllegalArgumentException nel caso in cui
+ */
+ public Puzzle8(int...values) {
+ if(values == null || values.length != LENGTH*LENGTH)
+ throw new IllegalArgumentException("The size of the array must be " + LENGTH*LENGTH);
+
+ var check = new int[LENGTH * LENGTH];
+ Arrays.copyOf(values, values.length);
+
+ for(var i = 0; i < this.puzzle.length; i++) {
+ var curr = this.puzzle[i];
+ var ok = true;
+
+ ok = (curr >= 0 && curr < check.length);
+ if(ok) {
+ if(curr == 0) this.blank = i;
+
+ check[curr] += 1;
+ ok = (check[curr] == 1);
+ }
+
+ if(!ok) throw new IllegalArgumentException("The input values must be UNIQUE integers between 0 and " + (LENGTH*LENGTH -1) + " inclusive");
+ }
+ }
+
+
+ /**
+ * Indica la grandezza della tavoletta che contiene le tessere.
+ * Essa è una matrice 3x3.
+ *
+ * @see #LENGTH
+ * @return la grandezza del lato della matrice
+ */
+ public int size() {
+ return LENGTH;
+ }
+
+ /**
+ * Permette di ricevere il valore della tessera posizionata nella zona richiesta.
+ * La zona è indicata come una matrice 3x3 dove la prima zona si trova nelle coordinate
+ * 0x0 e l'ultima nella zona 2x2.
+ * Il valore varia da 1 a 8 ed il valore della tessera "vuota" equivale a 0.
+ *
+ * @param x la prima coordinata, i possibili valori sono 0, 1, 2
+ * @param y la seconda coordinata, i possibili valori sono 0, 1, 2
+ * @return il valore della tessera nelle coordinate selezionate
+ */
+ public int get(int x, int y) {
+ return puzzle[x * LENGTH + y];
+ }
+
+ /**
+ * Genera un array di mosse disponibili a partire dalla configurazione corrente.
+ * Questo metodo esiste dato che non tutte le mosse sono disponibili in tutti i casi.
+ * per esempio se mi trovo nell'angolo in basso a dx potrò fare solo
+ * le mosse UP e LEFT.
+ *
+ * @return un array di mosse disponibili
+ */
+ public Move[] availableMoves() {
+ return switch(this.blank) {
+ case 0 -> new Move[] { Move.DOWN, Move.RIGHT };
+ case 1 -> new Move[] { Move.DOWN, Move.LEFT, Move.RIGHT };
+ case 2 -> new Move[] { Move.DOWN, Move.LEFT };
+ case 3 -> new Move[] { Move.UP, Move.DOWN, Move.RIGHT };
+ case 4 -> new Move[] { Move.UP, Move.DOWN, Move.LEFT, Move.RIGHT };
+ case 5 -> new Move[] { Move.UP, Move.DOWN, Move.LEFT };
+ case 6 -> new Move[] { Move.UP, Move.RIGHT };
+ case 7 -> new Move[] { Move.UP, Move.LEFT, Move.RIGHT };
+ case 8 -> new Move[] { Move.UP, Move.LEFT };
+ default -> new Move[] {};
+ };
+ }
+
+ /**
+ * Indica se la mossa inserita sia una mossa valida, dato che non tutte le mosse sono disponibili in tutti i casi.
+ * per esempio se mi trovo nell'angolo in basso a dx potrò fare solo le mosse UP e LEFT.
+ * @param move la mossa da testare
+ * @return VERO se la mossa è valida altrimenti falso
+ */
+ public boolean isValidMove(Move move) {
+ return switch(move) {
+ case UP -> this.blank >= LENGTH;
+ case DOWN -> this.blank <= 2*LENGTH - 1;
+ case LEFT -> this.blank % LENGTH != 0;
+ case RIGHT -> this.blank % LENGTH != LENGTH - 1;
+ };
+ }
+
+ /**
+ * Muove la tessera "vuota" in una delle direzioni indicate.
+ * Essa viene scambiata con una delle tessere adiacenti.
+ *
+ * Nota: non tutte le mosse sono disponibili in tutti i casi.
+ * per esempio se mi trovo nell'angolo in basso a dx potrò fare solo
+ * le mosse UP e LEFT.
+ *
+ * @see #availableMoves()
+ * @see #isValidMove(Move)
+ * @see #solve()
+ *
+ * @param move
+ * @throws UnsupportedOperationException nel caso in cui la mossa inserita non sia valida
+ */
+ public void move(Move move) {
+ if(!this.isValidMove(move))
+ throw new UnsupportedOperationException("Move not available");
+
+ var other = switch(move) {
+ case UP -> this.blank - LENGTH;
+ case DOWN -> this.blank + LENGTH;
+ case LEFT -> this.blank - 1;
+ case RIGHT -> this.blank + 1;
+ };
+
+ this.puzzle[this.blank] = this.puzzle[other];
+ this.puzzle[other] = 0;
+ this.blank = other;
+ }
+
+ /**
+ * Usa l'algoritmo A* per la soluzione del problema
+ * @param goal lo stato goal in cui arrivare
+ * @return una sequenza di mosse per poter risolvere il problema o null se non risolvibile
+ */
+ public List solve(Puzzle8 goal) {
+ var aStar = new AStar(Puzzle8::availableMoves, (p, m) -> new Puzzle8(p, m))
+ .setHeuristic((p1, p2) -> {
+ var sum = 0;
+ var n = p1.puzzle.length;
+ var index = new int[n];
+
+ for(var i = 0; i < n; i++) index[p1.puzzle[i]] = i;
+ for(var i = 0; i < n; i++) {
+ var curr = p2.puzzle[i];
+ var i2 = index[curr];
+
+ sum += Math.abs((i / LENGTH) - (i2 / LENGTH)) + Math.abs((i % LENGTH) - (i2 % LENGTH));
+ }
+ return sum;
+ });
+
+ return aStar.solve(this, Objects.requireNonNull(goal));
+ }
+
+
+ @Override
+ public boolean equals(Object obj) {
+ if(!obj.getClass().isInstance(this)) return false;
+ return Arrays.equals(((Puzzle8) obj).puzzle, this.puzzle);
+ }
+
+ @Override
+ public Iterator iterator() {
+ return new Iterator() {
+ int current = 0;
+ @Override public boolean hasNext() { return current < puzzle.length; }
+ @Override public Integer next() { return puzzle[current++]; }
+ };
+ }
+}
diff --git a/src/main/java/net/berack/upo/ai/problem2/Tris.java b/src/main/java/net/berack/upo/ai/problem2/Tris.java
new file mode 100644
index 0000000..03816bf
--- /dev/null
+++ b/src/main/java/net/berack/upo/ai/problem2/Tris.java
@@ -0,0 +1,29 @@
+package net.berack.upo.ai.problem2;
+
+import java.util.Arrays;
+
+public class Tris {
+ public static final int LENGTH = 3;
+
+ /**
+ * Possibili stati delle zone
+ */
+ public enum State {
+ EMPTY,
+ VALUE_X,
+ VALUE_O
+ }
+
+ private State[] tris = new State[LENGTH * LENGTH];
+
+ /**
+ * Crea una nuova istanza del gioco con tutti gli spazi vuoti
+ */
+ public Tris() {
+ Arrays.fill(tris, State.EMPTY);
+ }
+
+ public Tris(Tris current, State state, int x, int y) {
+
+ }
+}