diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e0400f0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.debug.settings.onBuildFailureProceed": true +} diff --git a/Model/soundsystem/ChessBoardListener.java b/Model/soundsystem/ChessBoardListener.java new file mode 100644 index 0000000..494a00e --- /dev/null +++ b/Model/soundsystem/ChessBoardListener.java @@ -0,0 +1,7 @@ +package Model.soundsystem; + +public interface ChessBoardListener { + void onBoardWon(PieceColor winner, int boardIndex); + void onMoveMade(int boardIndex); // New method for global turn management + +} diff --git a/Model/soundsystem/ChessGame.java b/Model/soundsystem/ChessGame.java new file mode 100644 index 0000000..210539a --- /dev/null +++ b/Model/soundsystem/ChessGame.java @@ -0,0 +1,445 @@ +package Model.soundsystem; + +import javax.swing.*; + +import java.awt.*; +import java.awt.event.*; + +enum PieceType { + KING, QUEEN, ROOK, BISHOP, KNIGHT, PAWN, NONE +} + +enum PieceColor { + WHITE, BLACK, NONE +} + +@SuppressWarnings("serial") +class ChessBoardPanel extends JPanel implements ActionListener { + private JButton[][] squares = new JButton[8][8]; + private piece[][] board = new piece[8][8]; + private boolean pieceSelected = false; + private int selectedRow, selectedCol; + private boolean gameOver = false; + private JLabel overlayLabel; + private PieceColor currentPlayer; + private JLayeredPane layeredPane; + + private ChessBoardListener listener; + private int boardIndex; + + public ChessBoardPanel() { + setLayout(new BorderLayout()); + + JPanel boardPanel = new JPanel(new GridLayout(8, 8)); + initializeBoardState(); + initializeGUI(boardPanel); + + overlayLabel = new JLabel("", SwingConstants.CENTER); + overlayLabel.setFont(new Font("SansSerif", Font.BOLD, 96)); + overlayLabel.setForeground(new Color(0, 0, 0, 150)); // semi-transparent svart + overlayLabel.setOpaque(false); + overlayLabel.setBackground(new Color(0, 0, 0, 0)); + overlayLabel.setVisible(false); + + layeredPane = new JLayeredPane(); + boardPanel.setBounds(0, 0, 600, 600); + overlayLabel.setBounds(0, 0, 600, 600); + layeredPane.setPreferredSize(new Dimension(600, 600)); + layeredPane.add(boardPanel, JLayeredPane.DEFAULT_LAYER); + layeredPane.add(overlayLabel, JLayeredPane.PALETTE_LAYER); + + removeAll(); + setLayout(new BorderLayout()); + add(layeredPane, BorderLayout.CENTER); + + currentPlayer = PieceColor.WHITE; + + } + + public void setListener(ChessBoardListener listener, int index) { + this.listener = listener; + this.boardIndex = index; + } + + public void showOverlaySymbol(String symbol, int fontSize, Color color) { + overlayLabel.setText(symbol); // Update the text of the overlay label + overlayLabel.setFont(new Font("SansSerif", Font.BOLD, fontSize)); // Set the font size + overlayLabel.setForeground(color); // Set the color of the symbol + overlayLabel.setVisible(true); // Make the overlay label visible + revalidate(); // Revalidate the panel to ensure layout updates + repaint(); // Repaint the panel to reflect changes + } + + // Set up the starting positions for all pieces. + private void initializeBoardState() { + // Fill all squares with an empty piece. + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) { + board[i][j] = new piece(); + } + } + // Black pieces (top of board) + board[0][0] = new piece(PieceType.ROOK, PieceColor.BLACK); + board[0][1] = new piece(PieceType.KNIGHT, PieceColor.BLACK); + board[0][2] = new piece(PieceType.BISHOP, PieceColor.BLACK); + board[0][3] = new piece(PieceType.QUEEN, PieceColor.BLACK); + board[0][4] = new piece(PieceType.KING, PieceColor.BLACK); + board[0][5] = new piece(PieceType.BISHOP, PieceColor.BLACK); + board[0][6] = new piece(PieceType.KNIGHT, PieceColor.BLACK); + board[0][7] = new piece(PieceType.ROOK, PieceColor.BLACK); + for (int j = 0; j < 8; j++) { + board[1][j] = new piece(PieceType.PAWN, PieceColor.BLACK); + } + // White pieces (bottom of board) + board[7][0] = new piece(PieceType.ROOK, PieceColor.WHITE); + board[7][1] = new piece(PieceType.KNIGHT, PieceColor.WHITE); + board[7][2] = new piece(PieceType.BISHOP, PieceColor.WHITE); + board[7][3] = new piece(PieceType.QUEEN, PieceColor.WHITE); + board[7][4] = new piece(PieceType.KING, PieceColor.WHITE); + board[7][5] = new piece(PieceType.BISHOP, PieceColor.WHITE); + board[7][6] = new piece(PieceType.KNIGHT, PieceColor.WHITE); + board[7][7] = new piece(PieceType.ROOK, PieceColor.WHITE); + for (int j = 0; j < 8; j++) { + board[6][j] = new piece(PieceType.PAWN, PieceColor.WHITE); + } + } + + // Set up the board GUI. + private void initializeGUI(JPanel boardPanel) { + Font font = new Font("Symbola", Font.PLAIN, 32); + + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + JButton button = new JButton(board[row][col].getSymbol()); + button.setFont(font); + button.setFocusPainted(false); + button.setPreferredSize(new Dimension(60, 60)); + button.setMargin(new Insets(0, 0, 0, 0)); + button.setOpaque(true); + button.setBorderPainted(false); + + if ((row + col) % 2 == 0) { + button.setBackground(Color.WHITE); + } else { + button.setBackground(Color.GRAY); + } + + button.addActionListener(this); + squares[row][col] = button; + boardPanel.add(button); + } + } + } + + // Handle a square click. + public void actionPerformed(ActionEvent e) { + if (gameOver) { + return; + } + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + if (e.getSource() == squares[row][col]) { + handleSquareClick(row, col); + return; + } + } + } + } + + // Process a click on square (row, col) + // Reset the timer when the turn switches + private void handleSquareClick(int row, int col) { + if (!pieceSelected) { + if (!board[row][col].getSymbol().equals("") && board[row][col].color == currentPlayer) { + pieceSelected = true; + selectedRow = row; + selectedCol = col; + resetAllSquareColors(); + squares[row][col].setBackground(Color.YELLOW); + highlightValidMoves(row, col); + } + } else { + if (isValidMove(board[selectedRow][selectedCol], selectedRow, selectedCol, row, col)) { + board[row][col] = board[selectedRow][selectedCol]; + board[selectedRow][selectedCol] = new piece(); + updateSquare(selectedRow, selectedCol); + updateSquare(row, col); + + // Notify MainGamePanel that a move was made + if (listener != null) { + listener.onMoveMade(boardIndex); + } + + checkGameOver(); + + } + resetAllSquareColors(); + pieceSelected = false; + } + } + + // Update a single square’s display. + private void updateSquare(int row, int col) { + squares[row][col].setText(board[row][col].getSymbol()); + } + + // Reset a square's background to its default. + private void resetSquareColor(int row, int col) { + if ((row + col) % 2 == 0) + squares[row][col].setBackground(Color.WHITE); + else + squares[row][col].setBackground(Color.GRAY); + } + + // Checks if moving the piece from (fromRow, fromCol) to (toRow, toCol) is + // legal. + private boolean isValidMove(piece piece, int fromRow, int fromCol, int toRow, int toCol) { + // Cannot capture your own piece. + if (board[toRow][toCol].color == piece.color) { + return false; + } + int dRow = toRow - fromRow; + int dCol = toCol - fromCol; + + switch (piece.type) { + case PAWN: + return isValidPawnMove(piece, fromRow, fromCol, toRow, toCol, dRow, dCol); + case KNIGHT: + // L-shaped move. + if ((Math.abs(dRow) == 2 && Math.abs(dCol) == 1) || + (Math.abs(dRow) == 1 && Math.abs(dCol) == 2)) { + return true; + } + break; + case BISHOP: + if (Math.abs(dRow) == Math.abs(dCol)) + return clearDiagonal(fromRow, fromCol, toRow, toCol); + break; + case ROOK: + if (dRow == 0 || dCol == 0) + return clearStraight(fromRow, fromCol, toRow, toCol); + break; + case QUEEN: + if (dRow == 0 || dCol == 0) + return clearStraight(fromRow, fromCol, toRow, toCol); + if (Math.abs(dRow) == Math.abs(dCol)) + return clearDiagonal(fromRow, fromCol, toRow, toCol); + break; + case KING: + // King moves one square in any direction. + if (Math.abs(dRow) <= 1 && Math.abs(dCol) <= 1) + return true; + break; + default: + break; + } + return false; + } + + private void highlightValidMoves(int row, int col) { + // Reset any previous highlights + resetAllSquareColors(); + + // Get the selected piece + piece selectedPiece = board[row][col]; + + Color yellowTint = new Color(246, 246, 117); + + // Check all squares on the board + for (int targetRow = 0; targetRow < 8; targetRow++) { + for (int targetCol = 0; targetCol < 8; targetCol++) { + // If the move is valid, highlight the square + if (isValidMove(selectedPiece, row, col, targetRow, targetCol)) { + squares[targetRow][targetCol].setBackground(yellowTint); // Highlight color + } + } + } +} + +private void resetAllSquareColors() { + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + if ((row + col) % 2 == 0) { + squares[row][col].setBackground(Color.WHITE); // Default white square + } else { + squares[row][col].setBackground(Color.GRAY); // Default gray square + } + } + } +} + + // Pawn movement validation. + private boolean isValidPawnMove(piece piece, int fromRow, int fromCol, int toRow, int toCol, int dRow, int dCol) { + int direction = (piece.color == PieceColor.WHITE) ? -1 : 1; + // Standard one-square move forward. + if (dCol == 0 && dRow == direction && board[toRow][toCol].type == PieceType.NONE) { + return true; + } + // Double move from starting row. + if (dCol == 0 && dRow == 2 * direction && board[toRow][toCol].type == PieceType.NONE) { + if ((piece.color == PieceColor.WHITE && fromRow == 6) + || (piece.color == PieceColor.BLACK && fromRow == 1)) { + // Ensure intermediate square is clear. + if (board[fromRow + direction][fromCol].type == PieceType.NONE) { + return true; + } + } + } + // Diagonal capture. + if (Math.abs(dCol) == 1 && dRow == direction && board[toRow][toCol].type != PieceType.NONE) { + return true; + } + return false; + } + + // Check if all squares between (fromRow,fromCol) and (toRow,toCol) are empty + // for straight moves. + private boolean clearStraight(int fromRow, int fromCol, int toRow, int toCol) { + int stepRow = Integer.compare(toRow, fromRow); + int stepCol = Integer.compare(toCol, fromCol); + int currentRow = fromRow + stepRow, currentCol = fromCol + stepCol; + while (currentRow != toRow || currentCol != toCol) { + if (board[currentRow][currentCol].type != PieceType.NONE) { + return false; + } + currentRow += stepRow; + currentCol += stepCol; + } + return true; + } + + // Check if all squares between (fromRow,fromCol) and (toRow,toCol) are empty + // for diagonal moves. + private boolean clearDiagonal(int fromRow, int fromCol, int toRow, int toCol) { + int stepRow = (toRow - fromRow) > 0 ? 1 : -1; + int stepCol = (toCol - fromCol) > 0 ? 1 : -1; + int currentRow = fromRow + stepRow, currentCol = fromCol + stepCol; + while (currentRow != toRow && currentCol != toCol) { + if (board[currentRow][currentCol].type != PieceType.NONE) { + return false; + } + currentRow += stepRow; + currentCol += stepCol; + } + return true; + } + + // Check if a king of the specified color is still present. + private boolean isKingPresent(PieceColor color) { + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + if (board[row][col].type == PieceType.KING && board[row][col].color == color) { + return true; + } + } + } + return false; + } + + // Checks if the game is over (if either king is missing) and disables the board + // if so. + private void checkGameOver() { + if (!isKingPresent(PieceColor.WHITE) || !isKingPresent(PieceColor.BLACK)) { + gameOver = true; + PieceColor winnerColor = isKingPresent(PieceColor.WHITE) ? PieceColor.WHITE : PieceColor.BLACK; + disableBoard(); + + String winnerSymbol = (winnerColor == PieceColor.WHITE) ? "X" : "O"; + Color symbolColor; + if (winnerSymbol == "X") { + symbolColor = Color.BLUE; + } else { + symbolColor = Color.RED; + } + showOverlaySymbol(winnerSymbol, 256, symbolColor); + + // JOptionPane.showMessageDialog(this, "Game Over! " + winnerColor + " wins."); + + if (listener != null) { + listener.onBoardWon(winnerColor, boardIndex); + } + } + } + + // Disables all buttons to prevent further moves. + void disableBoard() { + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + squares[row][col].setEnabled(false); + } + } + } + + public void setCurrentPlayer(PieceColor pieceColor) { + this.currentPlayer = pieceColor; + } + + @Override + public void doLayout() { + super.doLayout(); // Let the default layout manager do its work first + + // Get the actual size allocated to this ChessBoardPanel by its parent layout manager + int width = getWidth(); + int height = getHeight(); + + // Make the layeredPane (which holds the board and overlay) fill the entire + // space of this ChessBoardPanel, allowing it to stretch. + if (layeredPane != null) { // Check if layeredPane is initialized + layeredPane.setBounds(0, 0, width, height); + + // Make the components *inside* the layeredPane (the actual grid panel and the overlay label) + // also fill the entire layeredPane. + for (Component comp : layeredPane.getComponents()) { + if (comp != null) { // Check if component exists + comp.setBounds(0, 0, width, height); + } + } + + // Optional: Adjust font size based on new dimensions if needed for better visuals + // This is more complex and might require calculating font size based on square size. + // For now, we are just stretching the layout. + } + } +} + +public class ChessGame { + public static void main(String[] args) { + // Launch the persistent main menu that allows multiple boards. + SwingUtilities.invokeLater(new Runnable() { + public void run() { + createMainMenu(); + } + }); + } + + // Creates the main menu window. + private static void createMainMenu() { + JFrame menuFrame = new JFrame("Chess Game - Main Menu"); + menuFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + menuFrame.setLayout(new FlowLayout()); + + JButton newBoardButton = new JButton("New Board"); + newBoardButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + createNewBoard(); + } + }); + menuFrame.add(newBoardButton); + + menuFrame.setSize(300, 100); + menuFrame.setLocationRelativeTo(null); + menuFrame.setVisible(true); + } + + // Creates a new chess board window. + public static void createNewBoard() { + JFrame boardFrame = new JFrame("Chess Board"); + boardFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + ChessBoardPanel boardPanel = new ChessBoardPanel(); + boardFrame.add(boardPanel); + boardFrame.setSize(600, 600); + boardFrame.setLocationByPlatform(true); + boardFrame.setVisible(true); + } + +} diff --git a/Model/soundsystem/MainGamePanel.java b/Model/soundsystem/MainGamePanel.java new file mode 100644 index 0000000..8e47df3 --- /dev/null +++ b/Model/soundsystem/MainGamePanel.java @@ -0,0 +1,218 @@ +package Model.soundsystem; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; + +public class MainGamePanel extends JPanel implements ChessBoardListener { + private ChessBoardPanel[] boards = new ChessBoardPanel[9]; + private PieceColor[][] boardWinners = new PieceColor[3][3]; // Hvem som vant hvert brett + private PieceColor currentPlayer = PieceColor.WHITE; + private JPanel boardGrid; + + // Player-specific timer labels + private JLabel whiteTimerLabel = new JLabel("60", SwingConstants.CENTER); + private JLabel blackTimerLabel = new JLabel("60", SwingConstants.CENTER); + private JLabel turnLabel = new JLabel("WHITE", SwingConstants.CENTER); // Label for current turn + + private Timer whiteTimer; + private Timer blackTimer; + private int whiteTimeRemaining = 60; // 60 seconds for White + private int blackTimeRemaining = 60; // 60 seconds for Black + + public MainGamePanel() { + setLayout(new BorderLayout()); + + boardGrid = new JPanel(new GridLayout(3, 3, 5, 5)); + for (int i = 0; i < 9; i++) { + boards[i] = new ChessBoardPanel(); + boards[i].setListener(this, i); // viktig! + boardGrid.add(boards[i]); + } + + boardGrid.setOpaque(false); + boardGrid.setBackground(Color.BLACK); + + // Wrap the boardGrid in a container panel + JPanel container = new JPanel(new BorderLayout()); + container.setBackground(Color.BLACK); + container.add(boardGrid, BorderLayout.CENTER); + + // Add padding to the left to push the grid to the right + JPanel leftPadding = new JPanel(new GridLayout(3, 1, 0, 0)); // Three rows: White Timer, Turn Label, Black Timer + leftPadding.setPreferredSize(new Dimension(200, 0)); // Adjust width as needed + + // Add timer labels and turn label to the padding panel + whiteTimerLabel.setFont(new Font("SansSerif", Font.BOLD, 48)); + whiteTimerLabel.setBorder(BorderFactory.createLineBorder(Color.RED, 3)); // Red border for White + blackTimerLabel.setFont(new Font("SansSerif", Font.BOLD, 48)); + blackTimerLabel.setForeground(Color.WHITE); + blackTimerLabel.setBackground(Color.BLACK); + blackTimerLabel.setOpaque(true); + turnLabel.setFont(new Font("SansSerif", Font.BOLD, 30)); // Larger font for the turn label + turnLabel.setOpaque(true); + turnLabel.setBackground(Color.LIGHT_GRAY); // Background color for the turn label + turnLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK)); // Add a border to the label + + leftPadding.add(whiteTimerLabel); + leftPadding.add(turnLabel); // Add the turn label in the middle + leftPadding.add(blackTimerLabel); + + // Add the padding panel to the container + container.add(leftPadding, BorderLayout.WEST); + + add(container, BorderLayout.CENTER); + + // Initialize all boards as "no winner yet" + for (int row = 0; row < 3; row++) { + for (int col = 0; col < 3; col++) { + boardWinners[row][col] = PieceColor.NONE; + } + } + + initializeTimers(); + + } + + + @Override + public void onBoardWon(PieceColor winner, int boardIndex) { + int row = boardIndex / 3; + int col = boardIndex % 3; + boardWinners[row][col] = winner; + + // Sjekk om noen har vunnet Tic Tac Toe + if (checkTicTacToeWin(winner)) { + disableAllBoards(); + + JOptionPane.showMessageDialog(this, winner + " wins the entire game!"); + } + else if (checkTicTacToeDraw()) { + disableAllBoards(); + + JOptionPane.showMessageDialog(this, "Draw. :-("); + } else { + // Fortsett spillet + currentPlayer = (currentPlayer == PieceColor.WHITE) ? PieceColor.BLACK : PieceColor.WHITE; + } + } + + private boolean checkTicTacToeDraw() { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (boardWinners[i][j] == PieceColor.NONE) { + return false; + } + } + } + stopTimer(); + return true; + } + + + // Sjekker om en spiller har fått tre på rad + private boolean checkTicTacToeWin(PieceColor color) { + for (int i = 0; i < 3; i++) { + // Rader + if (boardWinners[i][0] == color && boardWinners[i][1] == color && boardWinners[i][2] == color) { + stopTimer(); + return true; + } + // Kolonner + if (boardWinners[0][i] == color && boardWinners[1][i] == color && boardWinners[2][i] == color) { + stopTimer(); + return true; + } + } + // Diagonaler + if (boardWinners[0][0] == color && boardWinners[1][1] == color && boardWinners[2][2] == color) { + stopTimer(); + return true; + } + if (boardWinners[0][2] == color && boardWinners[1][1] == color && boardWinners[2][0] == color) { + stopTimer(); + return true; + } + return false; + } + + private void disableAllBoards() { + for (ChessBoardPanel board : boards) { + board.disableBoard(); // finnes allerede i ChessBoardPanel + } + } + + private void initializeTimers() { + // Timer for White + whiteTimer = new Timer(1000, e -> { + whiteTimeRemaining--; + whiteTimerLabel.setText("" + whiteTimeRemaining); + if (whiteTimeRemaining <= 0) { + whiteTimer.stop(); + JOptionPane.showMessageDialog(this, "White's time is up! Black wins!"); + disableAllBoards(); + } + }); + + // Timer for Black + blackTimer = new Timer(1000, e -> { + blackTimeRemaining--; + blackTimerLabel.setText("" + blackTimeRemaining); + if (blackTimeRemaining <= 0) { + blackTimer.stop(); + JOptionPane.showMessageDialog(this, "Black's time is up! White wins!"); + disableAllBoards(); + } + }); + + // Start the timer for the first player (White) + whiteTimer.start(); + } + + public void swapTurnOnAllBoards(PieceColor newPlayer) { + for (int i = 0; i < 9; i++) { + boards[i].setCurrentPlayer(newPlayer); + } + } + + public void onMoveMade(int boardIndex) { + if (currentPlayer == PieceColor.WHITE) { + whiteTimeRemaining += 5; + } else { + blackTimeRemaining += 5; + } + + // Switch turns globally + currentPlayer = (currentPlayer == PieceColor.WHITE) ? PieceColor.BLACK : PieceColor.WHITE; + swapTurnOnAllBoards(currentPlayer); + + // Update the turn label + turnLabel.setText(currentPlayer == PieceColor.WHITE ? "WHITE" : "BLACK"); + + // Highlight the current player's timer + if (currentPlayer == PieceColor.WHITE) { + whiteTimerLabel.setBorder(BorderFactory.createLineBorder(Color.RED, 3)); // Red border for White + blackTimerLabel.setBorder(BorderFactory.createEmptyBorder()); // Remove border for Black + } else { + blackTimerLabel.setBorder(BorderFactory.createLineBorder(Color.RED, 3)); // Red border for Black + whiteTimerLabel.setBorder(BorderFactory.createEmptyBorder()); // Remove border for White + } + + // Stop the current player's timer and start the next player's timer + if (currentPlayer == PieceColor.WHITE) { + blackTimer.stop(); + whiteTimer.start(); + } else { + whiteTimer.stop(); + blackTimer.start(); + } + } + + public void stopTimer() { + whiteTimer.stop(); + blackTimer.stop(); + } + +} diff --git a/Model/soundsystem/chessGridLauncher.java b/Model/soundsystem/chessGridLauncher.java new file mode 100644 index 0000000..036e103 --- /dev/null +++ b/Model/soundsystem/chessGridLauncher.java @@ -0,0 +1,32 @@ +package Model.soundsystem; + +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; + +import javax.swing.*; + +public class chessGridLauncher { + public static void main(String[] args) { + soundMakerwaw backgroundSound = new soundMakerwaw("mario-theme.wav"); + backgroundSound.loop(); + SwingUtilities.invokeLater(() -> { + + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gd = ge.getDefaultScreenDevice(); + + JFrame frame = new JFrame("Ultimate Chess Tic Tac Toe"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setUndecorated(true); + + frame.add(new MainGamePanel()); + + if (gd.isFullScreenSupported()) { + gd.setFullScreenWindow(frame); + } else { + + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + }); + } +} diff --git a/Model/soundsystem/mario-theme.wav b/Model/soundsystem/mario-theme.wav new file mode 100644 index 0000000..fe6382d Binary files /dev/null and b/Model/soundsystem/mario-theme.wav differ diff --git a/Model/soundsystem/piece.java b/Model/soundsystem/piece.java new file mode 100644 index 0000000..0608e8e --- /dev/null +++ b/Model/soundsystem/piece.java @@ -0,0 +1,45 @@ +package Model.soundsystem; + +enum PieceType { KING, QUEEN, ROOK, BISHOP, KNIGHT, PAWN, NONE } +enum PieceColor { WHITE, BLACK, NONE } + +public class piece { + PieceType type; + PieceColor color; + + public piece(PieceType type, PieceColor color) { + this.type = type; + this.color = color; + } + + public piece() { + this.type = PieceType.NONE; + this.color = PieceColor.NONE; + } + + // Returns a Unicode symbol for the piece. + public String getSymbol() { + if (color == PieceColor.WHITE) { + switch(type) { + case KING: return "\u2654"; + case QUEEN: return "\u2655"; + case ROOK: return "\u2656"; + case BISHOP: return "\u2657"; + case KNIGHT: return "\u2658"; + case PAWN: return "\u2659"; + default: return ""; + } + } else if (color == PieceColor.BLACK) { + switch(type) { + case KING: return "\u265A"; + case QUEEN: return "\u265B"; + case ROOK: return "\u265C"; + case BISHOP: return "\u265D"; + case KNIGHT: return "\u265E"; + case PAWN: return "\u265F"; + default: return ""; + } + } + return ""; + } +} diff --git a/Model/soundsystem/soundMakerwaw.java b/Model/soundsystem/soundMakerwaw.java new file mode 100644 index 0000000..0ecde84 --- /dev/null +++ b/Model/soundsystem/soundMakerwaw.java @@ -0,0 +1,44 @@ +package Model.soundsystem; + +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Clip; +public class soundMakerwaw { + + private Clip clip; + + // Hentet fra: https://stackoverflow.com/questions/26305/how-can-i-play-sound-in-java + // hentet 23.04.2024, svart av Bilesh Ganguly, modifisert videre med hjelp av medstudent Jesper Hammer + + public soundMakerwaw(String filename) { + try { + this.clip = AudioSystem.getClip(); + AudioInputStream inputStream = AudioSystem.getAudioInputStream( + soundMakerwaw.class.getResourceAsStream(filename)); + this.clip.open(inputStream); + } catch (Exception e) { + System.err.println(e); + } + } + + + public void play(){ + this.clip.setFramePosition(0); + this.clip.start(); + } + + public void loop(){ + this.clip.loop(-1); + this.play(); + } + + public void stop(){ + this.clip.stop(); + } + + + + + + } + diff --git a/Model/soundsystem/sounds/sax.mp3 b/Model/soundsystem/sounds/sax.mp3 new file mode 100644 index 0000000..d57bb9b Binary files /dev/null and b/Model/soundsystem/sounds/sax.mp3 differ diff --git a/Model/soundsystem/victory-theme.wav b/Model/soundsystem/victory-theme.wav new file mode 100644 index 0000000..2fafc78 Binary files /dev/null and b/Model/soundsystem/victory-theme.wav differ diff --git a/Model/view/Inf101Graphics.java b/Model/view/Inf101Graphics.java new file mode 100644 index 0000000..0be802d --- /dev/null +++ b/Model/view/Inf101Graphics.java @@ -0,0 +1,254 @@ +package Model.view; + +// University of Bergen INF101 helper methods for graphics with Swing. +// Methods for drawing images are adapted with permission from +// https://kosbie.net/cmu/spring-08/15-100/handouts/DrawImageFromFile.java +// +// You may use this code freely. It is provided as-is, without any warranty. + +import javax.imageio.ImageIO; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Shape; +import java.awt.geom.Dimension2D; +import java.awt.image.BufferedImage; +import java.awt.geom.Point2D; +import java.awt.geom.AffineTransform; +import java.io.IOException; +import java.io.InputStream; +import java.io.File; + +public class Inf101Graphics { + + ////////////////////////////////////// + /// String helper methods + ///////////////////////////////////// + + /** + * Draw a string centered at the given point. + * + * @param g The graphics context to draw on + * @param s The string to draw + * @param x The x coordinate + * @param y The y coordinate + */ + public static void drawCenteredString(Graphics g, String s, double x, double y) { + FontMetrics metrics = g.getFontMetrics(); + double txtX = x - ((double) metrics.stringWidth(s))/2; + double txtY = y - ((double) metrics.getHeight())/2 + metrics.getAscent(); + g.drawString(s, (int) Math.round(txtX), (int) Math.round(txtY)); + } + + /** + * Draw a string centered at the given point. + * + * @param g The graphics context to draw on + * @param s The string to draw + * @param p The point on which to center the string + */ + public static void drawCenteredString(Graphics g, String s, Point2D p) { + Inf101Graphics.drawCenteredString(g, s, p.getX(), p.getY()); + } + + /** + * Draw a string centered in the given rectangle. The string will be + * vertically centered and horizontally centered with respect to the + * given rectangle. If the string is too wide or to tall to fit in the + * rectangle, it will overflow. If the width and the height are 0, the + * string will be centered around the x and y coordinates. + * + * @param g The graphics context to draw on + * @param s The string to draw + * @param x The left edge of the rectangle + * @param y The top edge of the rectangle + * @param width The width of the rectangle + * @param height The height of the rectangle + */ + public static void drawCenteredString(Graphics g, String s, double x, double y, + double width, double height) { + drawCenteredString(g, s, x + width/2, y + height/2); + } + + /** + * Draw a string centered in the given shape. The string will be + * vertically centered and horizontally centered with respect to the + * bounding box of the shape. If the string is too wide or to tall to + * fit within the bounds, it will overflow. + * + * @param g The graphics context to draw on + * @param s The string to draw + * @param shape in whose bounding box the string will be centered + */ + public static void drawCenteredString(Graphics g, String s, Shape shape) { + Inf101Graphics.drawCenteredString(g, s, shape.getBounds2D().getCenterX(), + shape.getBounds2D().getCenterY()); + } + + ////////////////////////////////////// + /// Image helper methods + ///////////////////////////////////// + + /** + * Draw the given image rotated and scaled with a top-left at x,y + * + * @param g The graphics context to draw on + * @param image The image to draw + * @param x The x coordinate of the top-left corner + * @param y The y coordinate of the top-left corner + * @param scale The scale factor (1.0 is 100%) + * @param radians The angle to rotate the image in radians + */ + public static void drawImage(Graphics g, Image image, double x, double y, + double scale, double radians) { + double imageWidth = image.getWidth(null); + double imageHeight = image.getHeight(null); + Dimension2D newSize = getImageSize(image, scale, radians); + AffineTransform transform = new AffineTransform(); + transform.translate(x+newSize.getWidth()/2,y+newSize.getHeight()/2); // last (not first!) + transform.rotate(radians); + transform.scale(scale,scale); + transform.translate(-imageWidth/2, -imageHeight/2); // first + ((Graphics2D)g).drawImage(image,transform,null); + } + + /** + * Draw the given image scaled with a top-left at x,y + * + * @param g The graphics context to draw on + * @param image The image to draw + * @param x The x coordinate of the top-left corner + * @param y The y coordinate of the top-left corner + * @param scale The scale factor (1.0 is 100%) + */ + public static void drawImage(Graphics g, Image image, double x, double y, + double scale) { + Inf101Graphics.drawImage(g, image, x, y, scale, 0); + } + + /** + * Draw the given image rotated and scaled with a center at x,y + * + * @param g The graphics context to draw on + * @param image The image to draw + * @param cx The x coordinate of the center + * @param cy The y coordinate of the center + * @param scale The scale factor (1.0 is 100%) + * @param radians The angle to rotate the image in radians + */ + public static void drawCenteredImage(Graphics g, Image image, double cx, double cy, + double scale, double radians) { + Dimension2D newSize = getImageSize(image, scale, radians); + drawImage(g, image, cx - (newSize.getWidth())/2, + cy - (newSize.getHeight())/2, scale, radians); + } + + /** + * Draw the given image scaled with a center at x,y + * + * @param g The graphics context to draw on + * @param image The image to draw + * @param cx The x coordinate of the center + * @param cy The y coordinate of the center + * @param scale The scale factor (1.0 is 100%) + */ + public static void drawCenteredImage(Graphics g, Image image, double cx, double cy, + double scale) { + Inf101Graphics.drawCenteredImage(g, image, cx, cy, scale, 0); + } + + /** + * Get the size of the (bounding box of the) image after it has been + * scaled and rotated + * + * @param image The image + * @param scale The scale factor (1.0 is 100%) + * @param radians The angle to rotate the image in radians + * @return The size of the (bounding box of) the image after it has been + * scaled and rotated + */ + public static Dimension2D getImageSize(Image image, double scale, double radians) { + double imageWidth = image.getWidth(null); + double imageHeight = image.getHeight(null); + AffineTransform transform = new AffineTransform(); + transform.rotate(radians); + transform.scale(scale,scale); + transform.translate(-imageWidth/2, -imageHeight/2); // first + double[] x = { 0, imageWidth, imageWidth , 0 }; + double[] y = { 0, 0 , imageHeight, imageHeight }; + double minx=0, maxx=0, miny=0, maxy=0; + Point2D.Double src = new Point2D.Double(); + Point2D.Double dst = new Point2D.Double(); + for (int i=0; i<4; i++) { + src.setLocation(x[i],y[i]); + transform.transform(src,dst); + if (i == 0) { + minx = maxx = dst.getX(); + miny = maxy = dst.getY(); + } + else { + minx = Math.min(dst.getX(),minx); + miny = Math.min(dst.getY(),miny); + maxx = Math.max(dst.getX(),maxx); + maxy = Math.max(dst.getY(),maxy); + } + } + final double width = maxx - minx; + final double height = maxy - miny; + return new Dimension2D() { + @Override + public double getWidth() { + return width; + } + + @Override + public double getHeight() { + return height; + } + + @Override + public void setSize(double width, double height) { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Converts an image file into a BufferedImage. Note that in a maven + * project, the requested image file must be in the src/main/resources + * folder. It accepts most common image formats, such as jpg, png, etc. + * + * @param filename The name of the image file + * @return The image as a BufferedImage + * @throws RuntimeException if the image file is not found + */ + public static BufferedImage loadImageFromResources(String filename) { + if (!filename.startsWith("/")) { + filename = "/" + filename; + } + InputStream is = Inf101Graphics.class.getResourceAsStream(filename); + if (is == null) { + throw new RuntimeException("Could not find image file in resources: " + filename); + } + try { + return ImageIO.read(is); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Converts an image file into a BufferedImage. + * + * @param file A file object with location the image file + * @return The image as a BufferedImage, or null if not found or not an image + */ + public static BufferedImage loadImageFromFile(File file) { + try { + return ImageIO.read(file); + } catch (IOException | IllegalArgumentException e) { + return null; + } + } +} diff --git a/Model/view/chessview.java b/Model/view/chessview.java new file mode 100644 index 0000000..2d332de --- /dev/null +++ b/Model/view/chessview.java @@ -0,0 +1,7 @@ +package Model.view; + + +public class chessview { + + +} diff --git a/README.md b/README.md index 25199fd..051b313 100644 --- a/README.md +++ b/README.md @@ -61,3 +61,25 @@ Prosjektet skal leveres **innen 12:00 på søndag**. Lag en tag med navnet "innl git tag innlevering git push --tags ``` +![alt text](image.png) +![alt text](image-1.png) + + +### Gruppenavn og Medlemmer +- **Gruppenavn:** Kaffe Bataljonen +- **Medlemmer:** + - Lukas + - August + - Luwei + - Henrik + +### Idé og Implementasjon +**Idé:** +Vi har laget et sjakkspill med en unik vri: spillere kan spille på flere brett samtidig, og vinneren er den som først får tre på rad i et Tic-Tac-Toe-mønster. + +**Implementasjon:** +Vi har brukt Java og Swing for å lage GUI-et. Prosjektet består av flere komponenter: +- **MainGamePanel:** Hovedpanelet som styrer spillet. +- **ChessBoardPanel:** Individuelle sjakkbrett. +- **Timer:** Implementert for å holde styr på spillernes tid. +Vi har også lagt til funksjonalitet for å vise vinnende linjer og bytte tur mellom spillere. \ No newline at end of file diff --git a/image-1.png b/image-1.png new file mode 100644 index 0000000..c157178 Binary files /dev/null and b/image-1.png differ diff --git a/image.png b/image.png new file mode 100644 index 0000000..71d7368 Binary files /dev/null and b/image.png differ diff --git a/to-do.md b/to-do.md new file mode 100644 index 0000000..f7c5340 --- /dev/null +++ b/to-do.md @@ -0,0 +1,4 @@ +[] + +[] +