Commit 5cddc8a9 authored by Anna Maria Eilertsen's avatar Anna Maria Eilertsen
Browse files

introduced solution code inspired of lab4. Known bug in win condition

parent c045a1bb
package inf101.v20.lab4.grid;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
*
* A Grid contains a set of elements
*
*/
public class Grid<T> implements IGrid<T>, Iterable<T>{
private List<T> cells;
private int height;
private int width;
/**
*
* Construct a grid with the given dimensions.
*
* @param width
* @param height
* @param initElement
* What the cells should initially hold (possibly null)
*/
public Grid(int width, int height, T initElement) {
if(width <= 0 || height <= 0)
throw new IllegalArgumentException();
this.height = height;
this.width = width;
cells = new ArrayList<>(height * width);
for (int i = 0; i < height * width; ++i) {
cells.add(initElement);
}
}
@Override
public int getHeight() {
return height; // TODO: fyll inn
}
@Override
public int getWidth() {
return width; // TODO: fyll inn
}
@Override
public void set(int x, int y, T elem) {
if(x < 0 || x >= width)
throw new IndexOutOfBoundsException();
if(y < 0 || y >= height)
throw new IndexOutOfBoundsException();
// TODO: fyll inn
cells.set(coordinateToIndex(x, y), elem);
}
private int coordinateToIndex(int x, int y) {
return x + y*width;
}
@Override
public T get(int x, int y) {
if(x < 0 || x >= width)
throw new IndexOutOfBoundsException();
if(y < 0 || y >= height)
throw new IndexOutOfBoundsException();
return cells.get(coordinateToIndex(x, y)); // TODO: fyll inn
}
@Override
public IGrid<T> copy() {
Grid<T> newGrid = new Grid<>(getWidth(), getHeight(), null);
for (int x = 0; x < width; x++)
for(int y = 0; y < height; y++)
newGrid.set(x, y, get(x, y));
return newGrid;
}
@Override
public Iterator<T> iterator() {
return cells.iterator();
}
}
package inf101.v20.lab4.grid;
/**
* IGrid is a generic grid
*
* @author Anna Eilertsen - anna.eilertsen@uib.no
*
*/
public interface IGrid<T> {
/**
* @return The height of the grid.
*/
int getHeight();
/**
* @return The width of the grid.
*/
int getWidth();
/**
*
* Set the contents of the cell in the given x,y location.
*
* y must be greater than or equal to 0 and less than getHeight().
* x must be greater than or equal to 0 and less than getWidth().
*
* @param x The column of the cell to change the contents of.
* @param y The row of the cell to change the contents of.
* @param element The contents the cell is to have.
*/
void set(int x, int y, T element);
/**
*
* Get the contents of the cell in the given x,y location.
*
* y must be greater than or equal to 0 and less than getHeight().
* x must be greater than or equal to 0 and less than getWidth().
*
* @param x The column of the cell to get the contents of.
* @param y The row of the cell to get contents of.
*/
T get(int x, int y);
/**
* Make a copy
*
* @return A shallow copy of the grid, with the same elements
*/
IGrid<T> copy();
}
\ No newline at end of file
package inf101.v20.lab4.mnkgames;
public class ConnectFour extends MNKGame{
ConnectFour() {
super(7, 6, 4);
}
@Override
public void putPieceAt(int x, int y) {
putPieceAt(x);
}
/**
* Special rules for the Connect Four
*
* Players can only drop pieces into column, they cannot decide which
* row they should land in.
*
* Pieces will stop at the first available row
*
* @param x the column to which the piece is added
*/
public void putPieceAt(int x) {
assert x >= 0 && x < getWidth();
int firstAvailableRow = -1;
for(int y=0; y<getHeight(); y++, firstAvailableRow = y-1) {
if(getPieceAt(x, y) != Piece.NONE) {
break;
}
}
//precondition check
if(firstAvailableRow == -1)
throw new IllegalStateException("There were no available spots in column " + x);
//add the current piece
super.putPieceAt(x, firstAvailableRow);
}
}
\ No newline at end of file
package inf101.v20.lab4.mnkgames;
public class GUIMain {
public static void main(String[] args) {
MNKGameGUI.run(() -> new ConnectFour());
// MNKGameGUI.run(() -> new TicTacToe());
}
}
package inf101.v20.lab4.mnkgames;
import inf101.v20.lab4.grid.Grid;
public abstract class MNKGame {
private int width;
private int height;
private int k;
private Grid<Piece> grid;
private Piece currentPlayer;
enum Piece { NONE, BLACK, WHITE;
public Piece next() {
return this == Piece.BLACK ? Piece.WHITE : Piece.BLACK;
}
}
/**
*
* @param m width: dimensions along x-axis
* @param n height: dimensions along y-axis
* @param k row length win condition
*/
MNKGame(int m, int n, int k){
width = m;
height = n;
this.k = k;
grid = new Grid<MNKGame.Piece>(width, height, Piece.NONE);
currentPlayer = Piece.BLACK;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getRowsToWin() {
return k;
}
public Piece getPieceAt(int x, int y) {
if( x >= width || x < 0 || y >= height || y < 0)
throw new IllegalArgumentException("Invalid coordinate");
return grid.get(x, y);
}
/**
* Add a piece belonging to the current player to position (x, y)
*
* @param x the x-coordinate such that 0 <= x < width
* @param y the y-coordinate such that 0 <= y < height
*/
public void putPieceAt(int x, int y) {
if( x >= width || x < 0 || y >= height || y < 0)
throw new IllegalArgumentException("Cannot add a piece to this position");
if(grid.get(x, y) != Piece.NONE)
throw new IllegalArgumentException("Position is already filled");
grid.set(x, y, currentPlayer);
currentPlayer = currentPlayer.next();
}
public boolean hasWinner() {
return getWinner()!=Piece.NONE;
}
public Piece getWinner() {
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int count = 0;
Piece current = grid.get(x, y);
if(current==Piece.NONE)
continue;
//horizontal
for (int i = x-k+1; i<=x+k-1; i++) {
System.out.println("Looking at \n" + debugToString(i, y));
if(i <0 || i>=getWidth())
continue;
if(grid.get(i, y) != current) {
count = 0;
}
else {
count++;
}
if(count==k)
return current;
}
count = 0;
//vertical
for (int j = y-k+1; j >=0 && j<=y+k-1; j++) {
System.out.println("Looking at \n" + debugToString(x, j));
if(j <0 || j>=getHeight())
continue;
if(grid.get(x, j) != current) {
count = 0;
}
else {
count++;
}
if(count==k)
return current;
}
count = 0;
//diagonal (0,0) -> (l, l) where l := min(m, n)
for (int i = x-k+1, j=y-k+1; i<=x+k-1 && j<=y+k-1; i++, j++) {
System.out.println("Looking at \n" + debugToString(i, j));
if(i <0 || j <0 || i>=getWidth() || j>=getHeight())
continue;
if(grid.get(i, j) != current) {
count = 0;
}
else {
count++;
}
if(count==k)
return current;
}
//diagonal (0,n) -> (m, 0) where l := min(m, n)
for (int i = x+k-1, j=y-k+1; i>=x-k+1 && j<=y+k-1; i--, j++) {
System.out.println("Looking at \n" + debugToString(i, j));
if(i <0 || j <0 || i>=getWidth() || j>=getHeight())
continue;
if(grid.get(i, j) != current) {
count = 0;
}
else {
count++;
}
if(count==k)
return current;
}
}
}
return Piece.NONE;
}
@Override
public String toString() {
String r = "";
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
r += " " + getPieceAt(i, j).name().charAt(0);
}
r += "\n";
}
return r;
}
public String debugToString(int x, int y) {
String r = "";
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
if(i == x && j == y) {
r+= " " + "X";
continue;
}
r += " " + getPieceAt(i, j).name().charAt(0);
}
r += "\n";
}
return r;
}
}
package inf101.v20.lab4.mnkgames;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import inf101.v20.lab4.mnkgames.MNKGame.Piece;
public class MNKGameComponent extends Component {
private static final long serialVersionUID = 4548104480314044678L;
private MNKGame game;
/**
* The height of each cell in pixels.
*/
private final int cellHeight = 20;
/**
* The width of each cell in pixels.
*/
private final int cellWidth = 20;
/**
* The size of the space between each cell and between the cell and the edge
* of the window.
*/
private final int padding = 1;
public MNKGameComponent(MNKGame game){
this.game = game;
}
/*
* (non-Javadoc)
*
* @see java.awt.Component#paint(java.awt.Graphics) Paints the labyrinth.
*/
@Override
public void paint(Graphics g) {
for (int x = 0; x < game.getWidth(); x++) {
for (int y = 0; y < game.getHeight(); y++) {
g.setColor(getColor(game.getPieceAt(x, y)));
g.fillRect(x * (cellHeight + padding) + padding, //
getHeight() - (y * (cellHeight + padding) + padding) - cellHeight, //
cellHeight, cellWidth);
}
}
}
/**
* TODO duplicated in MNKGameGUI
*
* Maps from Piece values to colors
*
* NB: White maps to red RN
*
* @param pieceAt
* @return
*/
protected Color getColor(Piece pieceAt) {
switch(pieceAt) {
case BLACK : return Color.BLACK;
case WHITE : return Color.RED;
default: return Color.WHITE;
}
}
}
\ No newline at end of file
package inf101.v20.lab4.mnkgames;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.function.Supplier;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import inf101.v20.lab4.grid.Grid;
import inf101.v20.lab4.mnkgames.MNKGame.Piece;
/**
*
* @author Anna Eilertsen
*/
public class MNKGameGUI extends JPanel implements ActionListener{
private static final long serialVersionUID = 8755882090377973497L;
/**
*
* Initializes a JFrame in which we place the a CellAutomataGUI containing
* the given ILabyrinth.
*
* @param ca
*/
public static void run(Supplier<MNKGame> gameSupplier) {
JFrame f = new JFrame("mnkGame");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MNKGameGUI ap = new MNKGameGUI(gameSupplier);
ap.initialize();
f.add("Center", ap);
f.pack();
f.setVisible(true);
}
MNKGame game;
private JButton setBoardButton;
private JLabel message;
private Grid<GamePanel> grid;
private Supplier<MNKGame> gameSupplier;
public MNKGameGUI(Supplier<MNKGame> gameSupplier) {
this.gameSupplier = gameSupplier;
this.game = gameSupplier.get();
this.grid = new Grid<GamePanel>(game.getWidth(), game.getHeight(), null);
}
/*
* (non-Javadoc)
*
* @see
* java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*
* Called whenever a button is pressed or the timer tells us its time to
* make a step.
*/
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == setBoardButton) {
reset();
}
}
private void reset() {
game = gameSupplier.get();
updateMessage();
this.updateUI();
removeAll();
initialize();
}
private void updateBoardUI() {
grid.forEach(item -> item.updateToGame());
MNKGameGUI.super.updateUI();
if(game.hasWinner()) {
System.out.println("Winner!");
stop();
}
}
private void stop() {
updateMessage();
grid.forEach(item -> item.removeMouseListeners());
}
//Class that defines what happens (i.e: the color changes) when a panel is clicked
class BoxListener extends MouseAdapter{
public void mousePressed(MouseEvent me){
GamePanel clickedPanel =(GamePanel)me.getSource(); // get the reference to the box that was clicked
clickedPanel.click();
}
}
/**
* Maps from Piece values to colors
*
* NB: White maps to red RN
*
* @param pieceAt
* @return
*/
protected Color getColor(Piece pieceAt) {
switch(pieceAt) {
case BLACK : return Color.getHSBColor(90, 65, 20.3f);
case WHITE : return Color.getHSBColor(308, 26, 34);
default: return Color.WHITE;
}
}
public Dimension getPreferredSize() {
return new Dimension(100*game.getHeight(), 100*game.getWidth());
}
private class GamePanel extends JPanel{
private int x, y;
private static final long serialVersionUID = 1L;
public GamePanel(int x, int y) {
this.x = x;
this.y = y;
}
public void removeMouseListeners() {
MouseListener[] mouseListeners = getMouseListeners();
for (int i = 0; i < mouseListeners.length; i++) {
this.removeMouseListener(mouseListeners[i]);
}
}
private void updateToGame() {
setBackground(getColor(game.getPieceAt(x, y)));
if(filled()) {
makeUnClickable();
}
}
private void makeUnClickable() {
removeMouseListeners();