diff --git a/.classpath b/.classpath index fe27910..e571fe4 100644 --- a/.classpath +++ b/.classpath @@ -36,5 +36,7 @@ + + diff --git a/com.jgoodies.common_1.8.1.v20240327-0800.jar b/com.jgoodies.common_1.8.1.v20240327-0800.jar new file mode 100644 index 0000000..6b4f411 Binary files /dev/null and b/com.jgoodies.common_1.8.1.v20240327-0800.jar differ diff --git a/com.jgoodies.forms_1.9.0.v20240327-0800.jar b/com.jgoodies.forms_1.9.0.v20240327-0800.jar new file mode 100644 index 0000000..794de8e Binary files /dev/null and b/com.jgoodies.forms_1.9.0.v20240327-0800.jar differ diff --git a/src/main/java/de/mannheim/th/chess/App.java b/src/main/java/de/mannheim/th/chess/App.java index 17af5c1..1e90163 100644 --- a/src/main/java/de/mannheim/th/chess/App.java +++ b/src/main/java/de/mannheim/th/chess/App.java @@ -1,6 +1,9 @@ package de.mannheim.th.chess; -import de.mannheim.th.chess.ui.Ui; +import de.mannheim.th.chess.ui.MainFrame; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Eine einfache Schach App mithilfe von {@linkplain https://github.com/bhlangonijr/chesslib} entwickelt. @@ -9,12 +12,15 @@ import de.mannheim.th.chess.ui.Ui; */ public class App { - private Ui userinterface = new Ui(); + private static final Logger logger = LogManager.getLogger(App.class); + + private static MainFrame userinterface; /** * Main-Methode. * @param args */ public static void main(String[] args) { - System.out.println("Hello World!"); + logger.info("Hello World."); + userinterface = new MainFrame(); } } diff --git a/src/main/java/de/mannheim/th/chess/ui/MainFrame.java b/src/main/java/de/mannheim/th/chess/ui/MainFrame.java new file mode 100644 index 0000000..96235ee --- /dev/null +++ b/src/main/java/de/mannheim/th/chess/ui/MainFrame.java @@ -0,0 +1,158 @@ +package de.mannheim.th.chess.ui; + +import java.awt.EventQueue; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; +import javax.swing.JTextField; +import javax.swing.JLabel; +import javax.swing.JOptionPane; + +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.ArrayList; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JFileChooser; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Color; + +public class MainFrame extends JFrame { + + private ArrayList spiele = new ArrayList<>(); + + private static final long serialVersionUID = 1L; + private JPanel contentPane; + + /** + * Launch the application. + */ + public static void main(String[] args) { + EventQueue.invokeLater(new Runnable() { + public void run() { + try { + MainFrame frame = new MainFrame(); + frame.setVisible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + /** + * Create the frame. + */ + public MainFrame() { + + setBackground(Color.LIGHT_GRAY); + setResizable(true); + setAlwaysOnTop(true); + setTitle("Schach"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setBounds(100, 100, 500, 500); + + contentPane = new JPanel(); + contentPane.setBackground(new Color(90, 90, 90)); + contentPane.setForeground(Color.BLACK); + contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + + setContentPane(contentPane); + + contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); + + contentPane.add(Box.createVerticalStrut(10)); + + JLabel lblNewLabel = new JLabel("Schach"); + lblNewLabel.setForeground(Color.BLACK); + lblNewLabel.setFont(new Font("Serif", Font.BOLD, 60)); + lblNewLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + contentPane.add(lblNewLabel); + + contentPane.add(Box.createVerticalStrut(10)); + + JLabel lblNewLabel_1 = new JLabel("by Dominik, Marius und Matias"); + lblNewLabel_1.setFont(new Font("Calibri", Font.ITALIC, 24)); + lblNewLabel_1.setAlignmentX(Component.CENTER_ALIGNMENT); + contentPane.add(lblNewLabel_1); + + contentPane.add(Box.createVerticalStrut(75)); + + JButton btnNewButton = new JButton("Neues Spiel starten"); + + btnNewButton.setBackground(Color.LIGHT_GRAY); + btnNewButton.setForeground(Color.BLACK); + btnNewButton.setFont(new Font("Tahoma", Font.BOLD, 16)); + btnNewButton.setAlignmentX(Component.CENTER_ALIGNMENT); + btnNewButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + + SpielFrame sp = new SpielFrame(); + spiele.add(sp); + + } + + }); + contentPane.add(btnNewButton); + + contentPane.add(Box.createVerticalStrut(15)); + + JButton btnNewButton_1 = new JButton("Vergangenes Spiel laden"); + + btnNewButton_1.setBackground(Color.LIGHT_GRAY); + btnNewButton_1.setForeground(Color.BLACK); + btnNewButton_1.setFont(new Font("Tahoma", Font.BOLD, 16)); + btnNewButton_1.setAlignmentX(Component.CENTER_ALIGNMENT); + btnNewButton_1.addActionListener(new ActionListener() { + + + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser dateiWaehler = new JFileChooser(); + JFrame jfFile = new JFrame(); + int auswahl = dateiWaehler.showOpenDialog(jfFile); + + if (auswahl == JFileChooser.APPROVE_OPTION) { + File ausgewaehlteDatei = dateiWaehler.getSelectedFile(); + JOptionPane.showMessageDialog(jfFile, "Gewählte Datei:\n" + ausgewaehlteDatei.getAbsolutePath()); + + // Uebergabe zu Logik zum extrahieren der Daten + } + } + + }); + + contentPane.add(btnNewButton_1); + + contentPane.add(Box.createVerticalStrut(15)); + + JButton btnNewButton_2 = new JButton("Spiel beenden"); + + btnNewButton_2.setBackground(Color.LIGHT_GRAY); + btnNewButton_2.setForeground(Color.BLACK); + btnNewButton_2.setFont(new Font("Tahoma", Font.BOLD, 16)); + btnNewButton_2.setAlignmentX(Component.CENTER_ALIGNMENT); + btnNewButton_2.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + System.exit(0); + } + + }); + + contentPane.add(btnNewButton_2); + setVisible(true); + } +} diff --git a/src/main/java/de/mannheim/th/chess/ui/SpielFrame.java b/src/main/java/de/mannheim/th/chess/ui/SpielFrame.java new file mode 100644 index 0000000..926ae2c --- /dev/null +++ b/src/main/java/de/mannheim/th/chess/ui/SpielFrame.java @@ -0,0 +1,308 @@ +package de.mannheim.th.chess.ui; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.github.bhlangonijr.chesslib.Board; +import com.github.bhlangonijr.chesslib.File; +import com.github.bhlangonijr.chesslib.Rank; +import com.github.bhlangonijr.chesslib.Square; + +import de.mannheim.th.chess.App; +import de.mannheim.th.chess.domain.Game; + +import java.awt.EventQueue; +import java.awt.Font; + +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Cursor; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class SpielFrame extends JFrame { + + private static final Logger logger = LogManager.getLogger(App.class); + + private static final long serialVersionUID = 1L; + private JPanel contentPane; + private ArrayList buttons = new ArrayList<>(); + private List clickableButtons = new ArrayList<>(); + private HashMap positions = new HashMap<>(); + private HashMap belegungen = new HashMap<>(); + private HashMap farben = new HashMap<>(); + private JPanel panelLinks, panelRechts; + private Game game; + private String symbolChoosed; + private JButton buttonChoosed; + private boolean playerWhite = true; + private boolean moveFinished = false; + + /** + * Launch the application. Die Main-Methode für den WindowBuilder. + */ + public static void main(String[] args) { + EventQueue.invokeLater(new Runnable() { + public void run() { + try { + SpielFrame frame = new SpielFrame(); + frame.setVisible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + /** + * Create the frame. + */ + public SpielFrame() { + + game = new Game(); + + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setBounds(100, 100, 1920, 1080); + setTitle("Schach"); + setAlwaysOnTop(true); + + JPanel contentPane = new JPanel(); + contentPane.setLayout(new BorderLayout()); + setContentPane(contentPane); + + // Linkes Panel mit GridLayout 8x8 für Schachbrett + panelLinks = new JPanel(new GridLayout(8, 8)); + + erstelleBrett(); + + // Rechtes Panel für Steuerung oder zusätzliche Eingaben + panelRechts = new JPanel(); + panelRechts.setBackground(Color.LIGHT_GRAY); + + // JSplitPane horizontal (linke und rechte Hälfte) + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panelLinks, panelRechts); + splitPane.setResizeWeight(0.70); + splitPane.setDividerSize(5); + splitPane.setEnabled(false); + + contentPane.add(splitPane, BorderLayout.CENTER); + setVisible(true); + } + + /** + * Erstellt alle Buttons und fügt sie dem Frame hinzu. + */ + private void erstelleBrett() { + for (int i = 0; i < 64; i++) { + JButton b = new JButton(); + b.setFocusPainted(false); + b.setFont(new Font("Arial", Font.PLAIN, 30)); + + if ((i / 8 + i % 8) % 2 == 0) { + logger.info("Helles Feld erstellt." + i); + b.setBackground(new Color(90, 90, 90)); + } else { + logger.info("Dunkles Feld erstellt." + i); + b.setBackground(new Color(65, 65, 65)); + } + + b.setForeground(Color.WHITE); + b.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); + b.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + + // setzt alle Roten Felder zurück + for (JButton b : farben.keySet()) { + + if (b.getBackground().equals(new Color(230, 100, 100))) { + + b.setBackground(farben.get(b)); + } + } + + // wenn weiß dran + if (playerWhite) { + + // wenn gerade Figur ausgewählt wird... + buttonChoosed = (JButton) e.getSource(); + symbolChoosed = belegungen.get(buttonChoosed); + + // setzt cursor auf spielfigur für die animation + String pfad = "src/main/resources/" + (int) symbolChoosed.toCharArray()[2] + ".png"; + + // Bild laden und Cursor im gesamten Frame setzen + Image image = Toolkit.getDefaultToolkit().getImage(pfad); + Image scaled = image.getScaledInstance(32, 32, Image.SCALE_SMOOTH); + Cursor figurCursor = Toolkit.getDefaultToolkit().createCustomCursor(scaled, new Point(0, 0), + "figurCursor"); + setCursor(figurCursor); + + // filtert möglichen Züge heraus + int position = positions.get(buttonChoosed); + + clickableButtons = game + .getLegalMoves( + Square.encode(Rank.allRanks[7 - position / 8], File.allFiles[position % 8])) + .stream().peek(System.out::println).map(m -> m.getTo()).peek(System.out::println) + .map(s -> 56 - s.getRank().ordinal() * 8 + s.getFile().ordinal()) + .collect(Collectors.toList()); + + //filtert mögliche Züge und nicht mögliche Züge in eine Map aus Listen + Map> buttonsSeperated = buttons.stream() + .collect(Collectors.partitioningBy(b -> clickableButtons.contains(buttons.indexOf(b)))); + + for(Boolean list: buttonsSeperated.keySet()) { + + if(list) { + //alle möglichen felder rot markieren + for (JButton b : positions.keySet()) { + // wenn button ein möglicher zug ist + if (clickableButtons.contains(positions.get(b))) { + farben.put(b, b.getBackground()); // damit sich gemerkt werden kann welches feld welche farbe vorher hatte + b.setBackground(new Color(230, 100, 100)); + + } + } + + }else { + //den rest der buttons ausser die möglichen züge deaktivieren + List andere = buttonsSeperated.get(list); + + for(JButton b: andere) { + if(!belegungen.get(b).split("-")[0].equals("w")) { + b.setEnabled(false); + } + } + } + } + + + + + // alle weisen squares deaktivieren, damit dannach klar ist wer dran ist + for (JButton b : belegungen.keySet()) { + if (belegungen.get(b).split("-")[0].equals("b")) { + b.setEnabled(false); + } + } + + } else { + + // wenn gerade Figur ausgewählt wird... + buttonChoosed = (JButton) e.getSource(); + symbolChoosed = belegungen.get(buttonChoosed); + // System.out.println(symbolChoosed+" wurde gewählt."); + // setzt cursor auf spielfigur für die animation + String pfad = "src/main/resources/" + (int) symbolChoosed.toCharArray()[2] + ".png"; + + // Bild laden und Cursor im gesamten Frame setzen + Image image = Toolkit.getDefaultToolkit().getImage(pfad); + Image scaled = image.getScaledInstance(32, 32, Image.SCALE_SMOOTH); + Cursor figurCursor = Toolkit.getDefaultToolkit().createCustomCursor(scaled, new Point(0, 0), + "figurCursor"); + setCursor(figurCursor); + + // filtert möglichen Züge heraus + int position = positions.get(buttonChoosed); + List clickableButtons = new ArrayList<>(); + clickableButtons = game + .getLegalMoves( + Square.encode(Rank.allRanks[7 - position / 8], File.allFiles[position % 8])) + .stream().peek(System.out::println).map(m -> m.getTo()).peek(System.out::println) + .map(s -> 56 - s.getRank().ordinal() * 8 + s.getFile().ordinal()) + .collect(Collectors.toList()); + + for (JButton b : positions.keySet()) { + // wenn button ein möglicher zug ist + if (clickableButtons.contains(positions.get(b))) { + b.setBackground(new Color(230, 100, 100)); + } + } + + // alle schwarzen squares deaktivieren, damit dannach klar ist wer dran ist + for (JButton b : belegungen.keySet()) { + if (belegungen.get(b).split("-")[0].equals("w")) { + b.setEnabled(false); + } + } + + } + + // alle anderen Buttons nicht ckickbar zu machen + + // Button Icon zurücksetzen + + // Buttonposition merken (in MoveListe oder so) + + // wenn Button platzierd werden soll... + + // neuen Button in Moveliste eintragen + + // Icon ändern + + // Modus auf auswählen setzen und spielerwechsel markieren + + // spielerwechsel + if (moveFinished) + playerWhite = !playerWhite; + + } + }); + + panelLinks.add(b); + buttons.add(b); + positions.put(b, i); + + } + + ladeBrett(); + + } + + /** + * holt sich FEN-Zeichenkette und extrahiert daraus die Positionen der Figuren + */ + private void ladeBrett() { + // System.out.println(game.toFEN()); + + char[] fen = game.toFEN().replaceAll("/", "").split(" ")[0].toCharArray(); + int i = 0; + for (int j = 0; j < fen.length; j++) { + if (Character.isDigit(fen[j])) { + int leerfelder = Character.getNumericValue(fen[j]); + for (int k = 0; k < leerfelder; k++) { + belegungen.put(buttons.get(i), "n-n"); + buttons.get(i).setEnabled(false); // erstmal deaktivieren, weil leere Felder nicht ckickbar sein sollten. + i++; + } + continue; + }else if (fen[j] >= 65 && fen[j] <= 90) { // ein Großbuchstabe, also + belegungen.put(buttons.get(i), "w-" + fen[j]); + } else if (fen[j] >= 97 && fen[j] <= 122) { // ein Kleinbuchstabe, also + belegungen.put(buttons.get(i), "b-" + fen[j]); + buttons.get(i).setEnabled(false); // erstmal deaktivieren, damit weiß beginnen kann + } + buttons.get(i).setIcon(new ImageIcon("src/main/resources/" + (int) fen[j] + ".png")); + + i++; + + } + } +} diff --git a/src/main/java/de/mannheim/th/chess/ui/Ui.java b/src/main/java/de/mannheim/th/chess/ui/Ui.java deleted file mode 100644 index 8af8c15..0000000 --- a/src/main/java/de/mannheim/th/chess/ui/Ui.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.mannheim.th.chess.ui; - -import java.util.ArrayList; - -import de.mannheim.th.chess.utl.GameReader; - -/** - * Zeigt das Main-Menü der App an. - */ -public class Ui{ - - private ArrayList gamewindows = new ArrayList<>(); - private GameReader reader = new GameReader(); - - public Ui() { - - } -} \ No newline at end of file diff --git a/src/main/java/de/mannheim/th/chess/utl/Clock.java b/src/main/java/de/mannheim/th/chess/utl/Clock.java index 88f7e52..c575d06 100644 --- a/src/main/java/de/mannheim/th/chess/utl/Clock.java +++ b/src/main/java/de/mannheim/th/chess/utl/Clock.java @@ -3,10 +3,138 @@ package de.mannheim.th.chess.utl; /** * Zeigt die Zeitangabe während eines Spiels eines Spielers an. */ -public class Clock{ +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Font; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.Timer; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class Clock extends Thread implements Runnable { + private volatile boolean whiteToMove = true; + private volatile boolean gameHasFinished = false; + private static final Logger clockLogger = LogManager.getLogger(Clock.class); + private int minutes; - public Clock() { - + public Clock(String mode) { + setMode(mode); + //run(); } + public void pressClock() { + whiteToMove = !whiteToMove; + if (whiteToMove) { + clockLogger.info("Weiß ist am Zug"); + } else { + clockLogger.info("Schwarz ist am Zug"); + } + } + + public void endGame() { + gameHasFinished = true; + } + + public void run() { + JFrame clockFrame = new JFrame("Clock"); + + JPanel player1Panel = new JPanel(); + player1Panel.setBackground(Color.BLACK); + JPanel player2Panel = new JPanel(); + player2Panel.setBackground(Color.BLACK); + clockFrame.setBounds(1000, 500, 10000, 10000); + clockFrame.setLayout(new BorderLayout()); + JLabel clock1 = new JLabel(" " + minutes + ":00 "); + clock1.setForeground(Color.WHITE); + clock1.setFont(new Font("Arial", Font.BOLD, 50)); + JLabel clock2 = new JLabel(" " + minutes + ":00 "); + clock2.setForeground(Color.WHITE); + clock2.setFont(new Font("Arial", Font.BOLD, 50)); + player1Panel.add(clock1); + player2Panel.add(clock2); + JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, player1Panel, player2Panel); + split.setFont(new Font("Arial", Font.BOLD, 50)); + clockFrame.add(split); + + var min1 = new AtomicInteger(minutes); + var sec1 = new AtomicInteger(0); + var min2 = new AtomicInteger(minutes); + var sec2 = new AtomicInteger(0); + clockFrame.pack(); + clockFrame.setVisible(true); + + var t = new Timer(1000, (ae) -> { + + if (!gameHasFinished) { + + StringBuilder clockShower = new StringBuilder(); + if (whiteToMove) { + if (sec1.intValue() == 00) { + sec1.set(60); + min1.decrementAndGet(); + } + if (min1.intValue() < 10) { + clockShower.append("0"); + } + clockShower.append(min1.get()); + clockShower.append(":"); + if (sec1.intValue() < 10) { + clockShower.append("0"); + } + clockShower.append(sec1.decrementAndGet()); + clock1.setText(clockShower.toString()); + + } else { + if (sec2.intValue() == 00) { + sec2.set(60); + min2.decrementAndGet(); + } + if (min2.intValue() < 10) { + clockShower.append("0"); + } + clockShower.append(min2.get()); + clockShower.append(":"); + if (sec2.intValue() < 10) { + clockShower.append("0"); + } + clockShower.append(sec2.decrementAndGet()); + clock2.setText(clockShower.toString()); + } + clockFrame.repaint(); + if ((sec1.intValue() == 0 && min1.intValue() == 0) || (sec2.intValue() == 0 && min2.intValue() == 0)) { + endGame(); + } + } }); + + t.start(); + } + public static void main(String[] args) throws InterruptedException { + Clock st = new Clock("classic"); + st.start(); + st.pressClock(); + } + + private void setMode(String mode) { + switch(mode) { + case "blitz": + minutes = 5; + clockLogger.info("Neue Blitz-Uhr wurde erstellt"); + break; + case "rapid": + minutes = 10; + clockLogger.info("Neue Schnellschach-Uhr wurde erstellt"); + break; + case "classic": + minutes = 120; + clockLogger.info("Neue klassische Schachuhr wurde erstellt"); + break; + } + } + } \ No newline at end of file diff --git a/src/main/resources/107.png b/src/main/resources/107.png new file mode 100644 index 0000000..225f869 Binary files /dev/null and b/src/main/resources/107.png differ diff --git a/src/main/resources/110.png b/src/main/resources/110.png new file mode 100644 index 0000000..8e3d04e Binary files /dev/null and b/src/main/resources/110.png differ diff --git a/src/main/resources/112.png b/src/main/resources/112.png new file mode 100644 index 0000000..c432d38 Binary files /dev/null and b/src/main/resources/112.png differ diff --git a/src/main/resources/113.png b/src/main/resources/113.png new file mode 100644 index 0000000..0d94a1c Binary files /dev/null and b/src/main/resources/113.png differ diff --git a/src/main/resources/114.png b/src/main/resources/114.png new file mode 100644 index 0000000..b9748e8 Binary files /dev/null and b/src/main/resources/114.png differ diff --git a/src/main/resources/66.png b/src/main/resources/66.png new file mode 100644 index 0000000..26dae01 Binary files /dev/null and b/src/main/resources/66.png differ diff --git a/src/main/resources/75.png b/src/main/resources/75.png new file mode 100644 index 0000000..d734164 Binary files /dev/null and b/src/main/resources/75.png differ diff --git a/src/main/resources/78.png b/src/main/resources/78.png new file mode 100644 index 0000000..2d716b1 Binary files /dev/null and b/src/main/resources/78.png differ diff --git a/src/main/resources/80.png b/src/main/resources/80.png new file mode 100644 index 0000000..e98fae2 Binary files /dev/null and b/src/main/resources/80.png differ diff --git a/src/main/resources/81.png b/src/main/resources/81.png new file mode 100644 index 0000000..a4fe68c Binary files /dev/null and b/src/main/resources/81.png differ diff --git a/src/main/resources/82.png b/src/main/resources/82.png new file mode 100644 index 0000000..a805de4 Binary files /dev/null and b/src/main/resources/82.png differ diff --git a/src/main/resources/98.png b/src/main/resources/98.png new file mode 100644 index 0000000..453cb32 Binary files /dev/null and b/src/main/resources/98.png differ