332 lines
No EOL
9.8 KiB
Java
332 lines
No EOL
9.8 KiB
Java
/*
|
|
Connect four - written in java
|
|
Copyright (C) 2020 Oliver Schappacher and Dorian Zedler
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package de.itsblue.ConnectFour;
|
|
|
|
import javax.swing.*;
|
|
import java.awt.*;
|
|
|
|
import de.itsblue.ConnectFour.Plate.PlateType;
|
|
|
|
/**
|
|
* GameBoard is a fully usable connect4 game board. It can take plates and
|
|
* insert them into a given column and check if somebody won the game.
|
|
*
|
|
* @author Oliver Schappacher
|
|
* @author Dorian Zedler
|
|
*/
|
|
public class GameBoard extends JPanel {
|
|
|
|
/**
|
|
*
|
|
*/
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
/**
|
|
* The rows of the board
|
|
*/
|
|
private int boardRows = 6;
|
|
|
|
/**
|
|
* The columns of the board
|
|
*/
|
|
private int boardColumns = 7;
|
|
|
|
/**
|
|
* Array containing all plate containers
|
|
*/
|
|
private PlateContainer[][] BoardContainers = new PlateContainer[boardColumns][boardRows];
|
|
|
|
/**
|
|
* If set to true, the board will not accept any insertions. Will be set to
|
|
* false by the clear() function.
|
|
*/
|
|
private boolean boardLocked = false;
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
GameBoard() {
|
|
this.initBoard();
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
GameBoard(int rows, int colums) {
|
|
this.boardRows = rows;
|
|
this.boardColumns = colums;
|
|
this.initBoard();
|
|
}
|
|
|
|
/**
|
|
* Function to insert a plate into a specific column
|
|
*
|
|
* @param plate Plate object to insert
|
|
* @param column The column to insert the plate into
|
|
* @return "ok" if the inserton was successfull, "err" if the column is full,
|
|
* PlateType as string if a plate type has won
|
|
*/
|
|
public String insertPlate(Plate plate, int column) {
|
|
|
|
// check if the column is out of range
|
|
if (column > boardColumns - 1 || this.boardLocked)
|
|
return "err";
|
|
|
|
// search for an empty row
|
|
for (int i = boardRows - 1; i >= 0; i--) {
|
|
if (!this.BoardContainers[column][i].containsPlate()) {
|
|
// if the container is empty -> add the plate
|
|
this.BoardContainers[column][i].insertPlate(plate);
|
|
|
|
PlateType winCheckResult = this.checkContainerForWin(column, i);
|
|
|
|
if (winCheckResult == null)
|
|
return "ok";
|
|
|
|
return winCheckResult.toString();
|
|
}
|
|
}
|
|
|
|
return "err";
|
|
}
|
|
|
|
/**
|
|
* Function to clear the board
|
|
*/
|
|
public void clearBoard() {
|
|
// search for an empty row
|
|
for (int c = 0; c < boardColumns; c++) {
|
|
for (int r = 0; r < boardRows; r++) {
|
|
// create the container
|
|
this.BoardContainers[c][r].removePlate();
|
|
}
|
|
}
|
|
|
|
this.boardLocked = false;
|
|
}
|
|
|
|
/**
|
|
* Function to fill the board with containers
|
|
*/
|
|
private void initBoard() {
|
|
|
|
// configure the main layout
|
|
this.setLayout(new GridLayout(this.boardRows, this.boardColumns));
|
|
|
|
// fill the grid with containers
|
|
for (int i = 0; i < boardRows; i++) {
|
|
for (int j = 0; j < boardColumns; j++) {
|
|
// create the container
|
|
this.BoardContainers[j][i] = new PlateContainer();
|
|
|
|
// add the container
|
|
this.add(this.BoardContainers[j][i]);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Function to calculate a size for the containers to fit a given dimension
|
|
*
|
|
* @param parentSize dimension for the containers to fit into
|
|
* @return suggested container size
|
|
*/
|
|
private int getSuggestedContainerSize(Dimension parentSize) {
|
|
int containerSize;
|
|
|
|
if (parentSize.getWidth() / this.boardColumns > parentSize.getHeight() / this.boardRows)
|
|
containerSize = (int) parentSize.getHeight() / this.boardRows;
|
|
else
|
|
containerSize = (int) parentSize.getWidth() / this.boardColumns;
|
|
|
|
return containerSize;
|
|
}
|
|
|
|
/**
|
|
* Function to check if a certain container is the beginning of a four plate log
|
|
* chain of plates of the same type what would indicate the end of the game. If
|
|
* a matching chain is found, the involved containers are highlited.
|
|
*
|
|
* @param c column of the container to check
|
|
* @param r row of the container to check
|
|
* @return <code>null</code> if there was no matching chain or the PlateType of
|
|
* the chain
|
|
*/
|
|
public PlateType checkContainerForWin(int c, int r) {
|
|
|
|
// if there is no plate in the container to check
|
|
// -> return false
|
|
if (this.getPlateContainer(c, r) == null || !this.getPlateContainer(c, r).containsPlate())
|
|
return null;
|
|
|
|
// check all possible winnings clockwise
|
|
for (int pc = 0; pc < 8; pc++) {
|
|
|
|
int sum = 0;
|
|
PlateContainer[] currentContainers = this.getPlateContainerRow(c, r, pc);
|
|
|
|
for (PlateContainer plateContainer : currentContainers) {
|
|
if (plateContainer != null && plateContainer.containsPlate())
|
|
sum += plateContainer.getContainedPlate().getValue();
|
|
}
|
|
|
|
if (Math.abs(sum) == 4) {
|
|
this.boardLocked = true;
|
|
|
|
for (PlateContainer plateContainer : currentContainers) {
|
|
plateContainer.highlight();
|
|
}
|
|
|
|
return this.getPlateContainer(c, r).getContainedPlate().getType();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Function to get the container at a certain column and row.
|
|
*
|
|
* @param c column of the container
|
|
* @param r row of the container
|
|
* @return <code>null</code> if there is no container at the given posistion or
|
|
* the container at the given position
|
|
*/
|
|
public PlateContainer getPlateContainer(int c, int r) {
|
|
if (this.BoardContainers.length > c && c >= 0 && this.BoardContainers[c].length > r && r >= 0)
|
|
return this.BoardContainers[c][r];
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Function to get a row of four containers starting at a given one
|
|
*
|
|
* @param c column of the container to start at
|
|
* @param r row of the conatiner to start at
|
|
* @param direction direction of the row (0-7)
|
|
* @return list of containers starting with the given one
|
|
*/
|
|
private PlateContainer[] getPlateContainerRow(int c, int r, int direction) {
|
|
|
|
PlateContainer[] containers = new PlateContainer[4];
|
|
|
|
if (direction < 0 || direction > 7)
|
|
return containers;
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
int checkC = 0;
|
|
int checkR = 0;
|
|
|
|
switch (direction) {
|
|
case 0:
|
|
// check top
|
|
checkC = c;
|
|
checkR = r + i;
|
|
break;
|
|
case 1:
|
|
// check top right vert
|
|
checkC = c + i;
|
|
checkR = r + i;
|
|
break;
|
|
case 2:
|
|
// check right
|
|
checkC = c + i;
|
|
checkR = r;
|
|
break;
|
|
case 3:
|
|
// check bottom right vert
|
|
checkC = c + i;
|
|
checkR = r - i;
|
|
break;
|
|
case 4:
|
|
// check bottom
|
|
checkC = c;
|
|
checkR = r - i;
|
|
break;
|
|
case 5:
|
|
// check bottom left vert
|
|
checkC = c - i;
|
|
checkR = r - i;
|
|
break;
|
|
case 6:
|
|
// check left
|
|
checkC = c - i;
|
|
checkR = r;
|
|
break;
|
|
case 7:
|
|
// check top left vert
|
|
checkC = c - i;
|
|
checkR = r + i;
|
|
break;
|
|
}
|
|
|
|
containers[i] = this.getPlateContainer(checkC, checkR);
|
|
}
|
|
|
|
return containers;
|
|
}
|
|
|
|
/**
|
|
* Function to get the column count
|
|
*
|
|
* @return the column count of the board
|
|
*/
|
|
public int getColumns() {
|
|
return this.boardColumns;
|
|
}
|
|
|
|
/**
|
|
* Function to get the row count
|
|
*
|
|
* @return the row count of the board
|
|
*/
|
|
public int getRows() {
|
|
return this.boardRows;
|
|
}
|
|
|
|
/**
|
|
* Override paint function to rescale all containers on every repaint
|
|
*/
|
|
@Override
|
|
public void paint(Graphics g) {
|
|
|
|
// update the size of all containers
|
|
for (PlateContainer[] plateContainers : BoardContainers) {
|
|
for (PlateContainer plateContainer : plateContainers) {
|
|
plateContainer.setPreferredSize(new Dimension(this.getSuggestedContainerSize(this.getSize()),
|
|
this.getSuggestedContainerSize(this.getSize())));
|
|
}
|
|
}
|
|
|
|
super.paint(g);
|
|
}
|
|
|
|
/**
|
|
* Override setPrefferedSize function to force aspect ratio
|
|
*/
|
|
@Override
|
|
public void setPreferredSize(Dimension preferredSize) {
|
|
Dimension newSize = new Dimension(this.getSuggestedContainerSize(preferredSize) * this.boardColumns,
|
|
this.getSuggestedContainerSize(preferredSize) * this.boardRows);
|
|
super.setPreferredSize(newSize);
|
|
}
|
|
} |