TrisGUI
- added tris GUI - added more tris tests - added new methods for helping with tris winner
This commit is contained in:
@@ -2,8 +2,10 @@ package net.berack.upo.ai.problem2;
|
||||
|
||||
import static net.berack.upo.ai.problem2.Tris.Symbol.EMPTY;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Classe che rappresenta il classico gioco del tris, dove per vincere bisogna
|
||||
@@ -93,7 +95,7 @@ public class Tris implements Iterable<Tris.Symbol> {
|
||||
* @throws UnsupportedOperationException nel caso in cui si ha già avuto un vincitore
|
||||
*/
|
||||
public void play(int x, int y) {
|
||||
if(this.haveWinner() != Symbol.EMPTY) throw new UnsupportedOperationException("The game has already finished!");
|
||||
if(this.getWinner() != Symbol.EMPTY) throw new UnsupportedOperationException("The game has already finished!");
|
||||
if(!isPlayAvailable(x, y)) throw new IllegalArgumentException("The state to modify must be Empty!");
|
||||
|
||||
this.tris[this.index(x, y)] = this.currentTurn;
|
||||
@@ -126,7 +128,7 @@ public class Tris implements Iterable<Tris.Symbol> {
|
||||
* @return un array di coordinate disponibili per giocare.
|
||||
*/
|
||||
public Coordinate[] availablePlays() {
|
||||
if(this.haveWinner() != EMPTY) return new Coordinate[0];
|
||||
if(this.getWinner() != EMPTY) return new Coordinate[0];
|
||||
|
||||
var count = 0;
|
||||
for(var i = 0; i < this.tris.length; i++)
|
||||
@@ -148,10 +150,10 @@ public class Tris implements Iterable<Tris.Symbol> {
|
||||
* Indica se il gioco è finito.
|
||||
* Il gioco finisce se si ha un vincitore o se non ci sono più caselle vuote.
|
||||
*
|
||||
* @return vero se iol gioco è finito
|
||||
* @return vero se il gioco è finito
|
||||
*/
|
||||
public boolean isFinished() {
|
||||
if(haveWinner() != EMPTY) return true;
|
||||
if(this.checkTris() != null) return true;
|
||||
|
||||
for(var symbol : this.tris)
|
||||
if(symbol == EMPTY)
|
||||
@@ -159,35 +161,66 @@ public class Tris implements Iterable<Tris.Symbol> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Se si ha un vincitore restiruisce le coordinate delle celle in cui si ha tris.
|
||||
* @return le coordinate delle celle del tris o null se non si ha ancora un vincitore.
|
||||
*/
|
||||
public List<int[]> getWinnerTris() {
|
||||
var list = this.checkTris();
|
||||
if(list == null) return null;
|
||||
|
||||
var coord = new ArrayList<int[]>();
|
||||
for(var index : list) {
|
||||
coord.add(new int[] {
|
||||
index % Tris.LENGTH,
|
||||
index / Tris.LENGTH
|
||||
});
|
||||
}
|
||||
return coord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indica se si ha un vincitore e restituisce chi ha vinto.
|
||||
* @return EMPTY se non c'è ancora un vincitore, altrimenti restituisci il vincitore
|
||||
*/
|
||||
public Symbol haveWinner() {
|
||||
// top left corner -> horizontal and vertical
|
||||
var state = this.tris[0];
|
||||
if(state != Symbol.EMPTY) {
|
||||
if(this.tris[1] == state && this.tris[2] == state) return state;
|
||||
if(this.tris[3] == state && this.tris[6] == state) return state;
|
||||
public Symbol getWinner() {
|
||||
var check = this.checkTris();
|
||||
if(check == null) return EMPTY;
|
||||
return tris[check[0]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Funzione privata per il controllo del tris.
|
||||
* Nel caso ci sia un tris questo metodo restituisce il valore
|
||||
* degli indici di dove si trova.
|
||||
*
|
||||
* @return un array degli indici del tris, altrimenti null;
|
||||
*/
|
||||
private int[] checkTris() {
|
||||
var possibleTris = new int[][] {
|
||||
// top left corner -> horizontal and vertical
|
||||
{0, 1, 2},
|
||||
{0, 3, 6},
|
||||
// bottom right corner -> horizontal and vertical
|
||||
{8, 7, 6},
|
||||
{8, 5, 2},
|
||||
// central -> diagonals, horizontal and vertical
|
||||
{4, 0, 8},
|
||||
{4, 6, 2},
|
||||
{4, 3, 5},
|
||||
{4, 1, 7}
|
||||
};
|
||||
|
||||
for(var check : possibleTris) {
|
||||
var symbol = this.tris[check[0]];
|
||||
|
||||
if(symbol == EMPTY) continue;
|
||||
if(symbol != this.tris[check[1]]) continue;
|
||||
if(symbol != this.tris[check[2]]) continue;
|
||||
return check;
|
||||
}
|
||||
|
||||
// bottom right corner -> horizontal and vertical
|
||||
state = this.tris[8];
|
||||
if(state != Symbol.EMPTY) {
|
||||
if(this.tris[7] == state && this.tris[6] == state) return state;
|
||||
if(this.tris[5] == state && this.tris[2] == state) return state;
|
||||
}
|
||||
|
||||
// central -> diagonals, horizontal and vertical
|
||||
state = this.tris[4];
|
||||
if(state != Symbol.EMPTY) {
|
||||
if(this.tris[0] == state && this.tris[8] == state) return state;
|
||||
if(this.tris[6] == state && this.tris[2] == state) return state;
|
||||
if(this.tris[3] == state && this.tris[5] == state) return state;
|
||||
if(this.tris[1] == state && this.tris[7] == state) return state;
|
||||
}
|
||||
|
||||
return Symbol.EMPTY;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
172
src/main/java/net/berack/upo/ai/problem2/TrisGUI.java
Normal file
172
src/main/java/net/berack/upo/ai/problem2/TrisGUI.java
Normal file
@@ -0,0 +1,172 @@
|
||||
package net.berack.upo.ai.problem2;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBoxMenuItem;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSeparator;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.GridLayout;
|
||||
|
||||
/**
|
||||
* Classe che permette di visualizzare graficamente il gioco Tris
|
||||
* In questa classe si trova un main che crea una istanza di questa finestra
|
||||
*
|
||||
* @author Berack96
|
||||
*/
|
||||
public class TrisGUI extends JFrame {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new TrisGUI();
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
private static final ImageIcon IMAGE_X;
|
||||
private static final ImageIcon IMAGE_O;
|
||||
private static final ImageIcon IMAGE_X_RED;
|
||||
private static final ImageIcon IMAGE_O_RED;
|
||||
private static final ImageIcon IMAGE_EMPTY;
|
||||
static {
|
||||
var loader = TrisGUI.class.getClassLoader();
|
||||
IMAGE_X = new ImageIcon(loader.getResource("tris/value_x.png"));
|
||||
IMAGE_O = new ImageIcon(loader.getResource("tris/value_o.png"));
|
||||
IMAGE_X_RED = new ImageIcon(loader.getResource("tris/value_x_red.png"));
|
||||
IMAGE_O_RED = new ImageIcon(loader.getResource("tris/value_o_red.png"));
|
||||
IMAGE_EMPTY = new ImageIcon(loader.getResource("tris/value_empty.png"));
|
||||
}
|
||||
|
||||
private final MyComponent[][] buttons = new MyComponent[Tris.LENGTH][Tris.LENGTH];
|
||||
private Tris tris = new Tris();
|
||||
private TrisAi ai = new TrisAi(this.tris);
|
||||
private JCheckBoxMenuItem aiFirst;
|
||||
|
||||
/**
|
||||
*/
|
||||
private TrisGUI() {
|
||||
super("Tris");
|
||||
|
||||
var grid = new GridLayout(Tris.LENGTH, Tris.LENGTH);
|
||||
var panel = new JPanel(grid);
|
||||
for(var i = 0; i < Tris.LENGTH; i++) {
|
||||
for(var j = 0; j < Tris.LENGTH; j++) {
|
||||
var comp = new MyComponent(Tris.LENGTH, j, i);
|
||||
panel.add(comp);
|
||||
this.buttons[j][i] = comp;
|
||||
}
|
||||
}
|
||||
|
||||
this.add(panel);
|
||||
this.attachMenu();
|
||||
|
||||
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
this.pack();
|
||||
this.setResizable(false);
|
||||
this.setLocationRelativeTo(null);
|
||||
this.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Metodo utile per non mettere tutto nel costruttore.
|
||||
* Qui viene creata la menubar
|
||||
*/
|
||||
private void attachMenu() {
|
||||
var menuBar = new JMenuBar();
|
||||
|
||||
var menu1 = new JMenu("Game");
|
||||
var item1 = new JMenuItem("Reset");
|
||||
item1.addActionListener(action -> this.reset());
|
||||
menu1.add(item1);
|
||||
|
||||
var separator = new JSeparator();
|
||||
menu1.add(separator);
|
||||
|
||||
var item2 = new JCheckBoxMenuItem("AI Enabled");
|
||||
item2.setSelected(this.ai != null);
|
||||
item2.addChangeListener(action -> this.ai = item2.getState()? new TrisAi(this.tris):null);
|
||||
menu1.add(item2);
|
||||
|
||||
this.aiFirst = new JCheckBoxMenuItem("AI First");
|
||||
this.aiFirst.setSelected(false);
|
||||
menu1.add(this.aiFirst);
|
||||
|
||||
menuBar.add(menu1);
|
||||
this.setJMenuBar(menuBar);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public void reset() {
|
||||
this.tris = new Tris();
|
||||
this.ai = this.ai == null? null:new TrisAi(this.tris);
|
||||
if(this.ai != null && aiFirst.getState()) this.ai.playNext();
|
||||
redraw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dopo questo metodo la finestra verrà ridisegnata (sempre se ci sono stati dei cambiamenti)
|
||||
* Nel caso in cui sia stata precedentemente calcolata la soluzione,
|
||||
* allora verrà evidenziata una tessera con il colore rosso per indicare
|
||||
* la mossa migliore da fare per la risoluzione.
|
||||
*/
|
||||
public void redraw() {
|
||||
for(var arr: this.buttons) {
|
||||
for(var button: arr) {
|
||||
var value = tris.get(button.x, button.y);
|
||||
var newIcon = switch(value) {
|
||||
case VALUE_X -> IMAGE_X;
|
||||
case VALUE_O -> IMAGE_O;
|
||||
case EMPTY -> IMAGE_EMPTY;
|
||||
};
|
||||
|
||||
if(!newIcon.equals(button.getIcon()))
|
||||
button.setIcon(newIcon);
|
||||
}
|
||||
}
|
||||
|
||||
var icon = switch(this.tris.getWinner()) {
|
||||
case VALUE_X -> IMAGE_X_RED;
|
||||
case VALUE_O -> IMAGE_O_RED;
|
||||
default -> null;
|
||||
};
|
||||
|
||||
if(icon != null)
|
||||
for(var coord : this.tris.getWinnerTris())
|
||||
this.buttons[coord[0]][coord[1]].setIcon(icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Classe privata usata come appoggio per la gestione dei pulsanti.
|
||||
* Qui vengono disabilitate alcune impostazioni base dei bottoni e
|
||||
* viene creato un listener per quando viene premuto su di esso.
|
||||
*/
|
||||
private class MyComponent extends JButton {
|
||||
final int x;
|
||||
final int y;
|
||||
|
||||
private MyComponent(int n, int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
||||
this.setBorder(BorderFactory.createLineBorder(Color.BLACK));
|
||||
this.setBackground(Color.WHITE);
|
||||
this.setContentAreaFilled(false);
|
||||
this.setFocusable(false);
|
||||
|
||||
this.setIcon(IMAGE_EMPTY);
|
||||
this.addActionListener(action -> {
|
||||
if(tris.isPlayAvailable(x, y) && !tris.isFinished()) {
|
||||
tris.play(x, y);
|
||||
if(ai != null) ai.playNext();
|
||||
redraw();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
src/main/resources/tris/value_empty.png
Normal file
BIN
src/main/resources/tris/value_empty.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 194 B |
BIN
src/main/resources/tris/value_o.png
Normal file
BIN
src/main/resources/tris/value_o.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
BIN
src/main/resources/tris/value_o_red.png
Normal file
BIN
src/main/resources/tris/value_o_red.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
BIN
src/main/resources/tris/value_x.png
Normal file
BIN
src/main/resources/tris/value_x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/main/resources/tris/value_x_red.png
Normal file
BIN
src/main/resources/tris/value_x_red.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
@@ -3,11 +3,12 @@ package net.berack.upo.ai.problem2;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static net.berack.upo.ai.problem2.Tris.Symbol.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -128,98 +129,156 @@ public class TestTris {
|
||||
|
||||
// horizontal 1 line X
|
||||
var tris = new Tris();
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(1,0);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(1,1);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(0,0);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(1,2);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(2,0);
|
||||
assertEquals(VALUE_X, tris.haveWinner());
|
||||
assertEquals(VALUE_X, tris.getWinner());
|
||||
assertTrue(tris.isFinished());
|
||||
|
||||
var trisPos = new ArrayList<int[]>();
|
||||
trisPos.add(new int[] {0, 0});
|
||||
trisPos.add(new int[] {1, 0});
|
||||
trisPos.add(new int[] {2, 0});
|
||||
|
||||
var trisCoord = tris.getWinnerTris();
|
||||
trisCoord.sort((a, b) -> Arrays.compare(a, b));
|
||||
assertEquals(trisPos.size(), trisCoord.size());
|
||||
for(var i = 0; i < trisPos.size(); i++)
|
||||
assertArrayEquals(trisPos.get(i), trisCoord.get(i));
|
||||
|
||||
|
||||
// diagonal \ O
|
||||
tris = new Tris();
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(2,1);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(1,1);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(2,0);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(2,2);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(1,2);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(0,0);
|
||||
assertEquals(VALUE_O, tris.haveWinner());
|
||||
assertEquals(VALUE_O, tris.getWinner());
|
||||
assertTrue(tris.isFinished());
|
||||
|
||||
trisPos = new ArrayList<int[]>();
|
||||
trisPos.add(new int[] {0, 0});
|
||||
trisPos.add(new int[] {1, 1});
|
||||
trisPos.add(new int[] {2, 2});
|
||||
|
||||
trisCoord = tris.getWinnerTris();
|
||||
trisCoord.sort((a, b) -> Arrays.compare(a, b));
|
||||
assertEquals(trisPos.size(), trisCoord.size());
|
||||
for(var i = 0; i < trisPos.size(); i++)
|
||||
assertArrayEquals(trisPos.get(i), trisCoord.get(i));
|
||||
|
||||
|
||||
// vertical 2 column X
|
||||
tris = new Tris();
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(1,0);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(0,2);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(1,1);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(0,1);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(1,2);
|
||||
assertEquals(VALUE_X, tris.haveWinner());
|
||||
assertEquals(VALUE_X, tris.getWinner());
|
||||
assertTrue(tris.isFinished());
|
||||
|
||||
trisPos = new ArrayList<int[]>();
|
||||
trisPos.add(new int[] {1, 0});
|
||||
trisPos.add(new int[] {1, 1});
|
||||
trisPos.add(new int[] {1, 2});
|
||||
|
||||
trisCoord = tris.getWinnerTris();
|
||||
trisCoord.sort((a, b) -> Arrays.compare(a, b));
|
||||
assertEquals(trisPos.size(), trisCoord.size());
|
||||
for(var i = 0; i < trisPos.size(); i++)
|
||||
assertArrayEquals(trisPos.get(i), trisCoord.get(i));
|
||||
|
||||
|
||||
// No winner
|
||||
tris = new Tris();
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(0,0);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(1,0);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(2,0);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(1,1);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(0,1);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(0,2);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(2,1);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(2,2);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(1,2);
|
||||
assertEquals(EMPTY, tris.haveWinner());
|
||||
assertEquals(EMPTY, tris.getWinner());
|
||||
assertTrue(tris.isFinished());
|
||||
assertNull(tris.getWinnerTris());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWinner2() {
|
||||
var tris = new Tris();
|
||||
|
||||
tris.play(1,1);
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(0,0);
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(0,1);
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(2,1);
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(0,2);
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(2,0);
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(1,0);
|
||||
assertFalse(tris.isFinished());
|
||||
tris.play(2,2);
|
||||
assertTrue(tris.isFinished());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user