From f655435b6bd27940e903fc66b7eb7c0109ccfdb9 Mon Sep 17 00:00:00 2001
From: ayazb7 <60544937+ayazb7@users.noreply.github.com>
Date: Thu, 7 Apr 2022 01:05:33 +0100
Subject: [PATCH] Adding Rotation and Swap mechanics

---
 .idea/comp1206-cw.iml                         |   9 +
 tetrecs/.idea/uiDesigner.xml                  | 124 +++++++
 .../soton/comp1206/component/GameBlock.java   | 341 +++++++++---------
 .../soton/comp1206/component/GameBoard.java   | 324 +++++++++--------
 .../soton/comp1206/component/PieceBoard.java  |  19 +-
 .../comp1206/event/NextPieceListener.java     |   7 +
 .../comp1206/event/RightClickListener.java    |   5 +
 .../java/uk/ac/soton/comp1206/game/Game.java  |  37 +-
 .../java/uk/ac/soton/comp1206/game/Grid.java  |  12 +-
 .../soton/comp1206/scene/ChallengeScene.java  |  56 ++-
 .../soton/comp1206/component/GameBoard.class  | Bin 4175 -> 4739 bytes
 .../uk/ac/soton/comp1206/game/Game.class      | Bin 6194 -> 7125 bytes
 .../uk/ac/soton/comp1206/game/Grid.class      | Bin 2919 -> 2663 bytes
 .../soton/comp1206/scene/ChallengeScene.class | Bin 7401 -> 8475 bytes
 tetrecs/tetrecs.iml                           |  34 ++
 15 files changed, 632 insertions(+), 336 deletions(-)
 create mode 100644 .idea/comp1206-cw.iml
 create mode 100644 tetrecs/.idea/uiDesigner.xml
 create mode 100644 tetrecs/src/main/java/uk/ac/soton/comp1206/event/NextPieceListener.java
 create mode 100644 tetrecs/src/main/java/uk/ac/soton/comp1206/event/RightClickListener.java
 create mode 100644 tetrecs/tetrecs.iml

diff --git a/.idea/comp1206-cw.iml b/.idea/comp1206-cw.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/.idea/comp1206-cw.iml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/tetrecs/.idea/uiDesigner.xml b/tetrecs/.idea/uiDesigner.xml
new file mode 100644
index 0000000..e96534f
--- /dev/null
+++ b/tetrecs/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Palette2">
+    <group name="Swing">
+      <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+      </item>
+      <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
+        <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+        <initial-values>
+          <property name="text" value="Button" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="RadioButton" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="CheckBox" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="Label" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+          <preferred-size width="-1" height="20" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+      </item>
+    </group>
+  </component>
+</project>
\ No newline at end of file
diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBlock.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBlock.java
index 272c525..50106ed 100644
--- a/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBlock.java
+++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBlock.java
@@ -11,179 +11,186 @@ import org.apache.logging.log4j.Logger;
 
 /**
  * The Visual User Interface component representing a single block in the grid.
- *
+ * <p>
  * Extends Canvas and is responsible for drawing itself.
- *
+ * <p>
  * Displays an empty square (when the value is 0) or a coloured square depending on value.
- *
+ * <p>
  * The GameBlock value should be bound to a corresponding block in the Grid model.
  */
 public class GameBlock extends Canvas {
 
-    private static final Logger logger = LogManager.getLogger(GameBlock.class);
-
-    /**
-     * The set of colours for different pieces
-     */
-    public static final Color[] COLOURS = {
-            Color.TRANSPARENT,
-            Color.DEEPPINK,
-            Color.RED,
-            Color.ORANGE,
-            Color.YELLOW,
-            Color.YELLOWGREEN,
-            Color.LIME,
-            Color.GREEN,
-            Color.DARKGREEN,
-            Color.DARKTURQUOISE,
-            Color.DEEPSKYBLUE,
-            Color.AQUA,
-            Color.AQUAMARINE,
-            Color.BLUE,
-            Color.MEDIUMPURPLE,
-            Color.PURPLE
-    };
-
-    private final GameBoard gameBoard;
-
-    private final double width;
-    private final double height;
-
-    /**
-     * The column this block exists as in the grid
-     */
-    private final int x;
-
-    /**
-     * The row this block exists as in the grid
-     */
-    private final int y;
-
-    /**
-     * The value of this block (0 = empty, otherwise specifies the colour to render as)
-     */
-    private final IntegerProperty value = new SimpleIntegerProperty(0);
-
-    /**
-     * Create a new single Game Block
-     * @param gameBoard the board this block belongs to
-     * @param x the column the block exists in
-     * @param y the row the block exists in
-     * @param width the width of the canvas to render
-     * @param height the height of the canvas to render
-     */
-    public GameBlock(GameBoard gameBoard, int x, int y, double width, double height) {
-        this.gameBoard = gameBoard;
-        this.width = width;
-        this.height = height;
-        this.x = x;
-        this.y = y;
-
-        //A canvas needs a fixed width and height
-        setWidth(width);
-        setHeight(height);
-
-        //Do an initial paint
-        paint();
-
-        //When the value property is updated, call the internal updateValue method
-        value.addListener(this::updateValue);
-    }
-
-    /**
-     * When the value of this block is updated,
-     * @param observable what was updated
-     * @param oldValue the old value
-     * @param newValue the new value
-     */
-    private void updateValue(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
-        paint();
-    }
-
-    /**
-     * Handle painting of the block canvas
-     */
-    public void paint() {
-        //If the block is empty, paint as empty
-        if(value.get() == 0) {
-            paintEmpty();
-        } else {
-            //If the block is not empty, paint with the colour represented by the value
-            paintColor(COLOURS[value.get()]);
-        }
-    }
-
-    /**
-     * Paint this canvas empty
-     */
-    private void paintEmpty() {
-        var gc = getGraphicsContext2D();
-
-        //Clear
-        gc.clearRect(0.0D, 0.0D, this.width, this.height);
-
-        //Fill
-        Color gradStart = Color.color(0.0D, 0.0D, 0.6D, 0.3D);
-        Color gradEnd = Color.color(0.0D, 0.0D, 0.2D, 0.5D);
-        gc.setFill(new LinearGradient(0.0D, 0.0D, 1.0D, 1.0D, true, CycleMethod.REFLECT, new Stop(0.0D, gradStart), new Stop(1.0D, gradEnd)));
-        gc.fillRect(0.0D, 0.0D, this.width, this.height);
-
-        //Border
-        gc.setStroke(Color.WHITE);
-        gc.setGlobalAlpha(0.7);
-        gc.strokeRect(0,0,width,height);
-    }
-
-    /**
-     * Paint this canvas with the given colour
-     * @param colour the colour to paint
-     */
-    private void paintColor(Paint colour) {
-        var gc = getGraphicsContext2D();
-
-        //Clear
-        gc.clearRect(0,0,width,height);
-
-        //Colour fill
-        gc.setFill(colour);
-        gc.setGlobalAlpha(1);
-        gc.fillRect(0,0, width, height);
-
-        //Border
-        gc.setStroke(Color.BLACK);
-        gc.strokeRect(0,0,width,height);
-    }
-
-    /**
-     * Get the column of this block
-     * @return column number
-     */
-    public int getX() {
-        return x;
-    }
-
-    /**
-     * Get the row of this block
-     * @return row number
-     */
-    public int getY() {
-        return y;
-    }
-
-    /**
-     * Get the current value held by this block, representing it's colour
-     * @return value
-     */
-    public int getValue() {
-        return this.value.get();
-    }
-
-    /**
-     * Bind the value of this block to another property. Used to link the visual block to a corresponding block in the Grid.
-     * @param input property to bind the value to
-     */
-    public void bind(ObservableValue<? extends Number> input) {
-        value.bind(input);
+  private static final Logger logger = LogManager.getLogger(GameBlock.class);
+
+  /**
+   * The set of colours for different pieces
+   */
+  public static final Color[] COLOURS = {
+      Color.TRANSPARENT,
+      Color.DEEPPINK,
+      Color.RED,
+      Color.ORANGE,
+      Color.YELLOW,
+      Color.YELLOWGREEN,
+      Color.LIME,
+      Color.GREEN,
+      Color.DARKGREEN,
+      Color.DARKTURQUOISE,
+      Color.DEEPSKYBLUE,
+      Color.AQUA,
+      Color.AQUAMARINE,
+      Color.BLUE,
+      Color.MEDIUMPURPLE,
+      Color.PURPLE
+  };
+
+  private final GameBoard gameBoard;
+
+  private final double width;
+  private final double height;
+
+  /**
+   * The column this block exists as in the grid
+   */
+  private final int x;
+
+  /**
+   * The row this block exists as in the grid
+   */
+  private final int y;
+
+  /**
+   * The value of this block (0 = empty, otherwise specifies the colour to render as)
+   */
+  private final IntegerProperty value = new SimpleIntegerProperty(0);
+
+  /**
+   * Create a new single Game Block
+   *
+   * @param gameBoard the board this block belongs to
+   * @param x         the column the block exists in
+   * @param y         the row the block exists in
+   * @param width     the width of the canvas to render
+   * @param height    the height of the canvas to render
+   */
+  public GameBlock(GameBoard gameBoard, int x, int y, double width, double height) {
+    this.gameBoard = gameBoard;
+    this.width = width;
+    this.height = height;
+    this.x = x;
+    this.y = y;
+
+    //A canvas needs a fixed width and height
+    setWidth(width);
+    setHeight(height);
+
+    //Do an initial paint
+    paint();
+
+    //When the value property is updated, call the internal updateValue method
+    value.addListener(this::updateValue);
+  }
+
+  /**
+   * When the value of this block is updated,
+   *
+   * @param observable what was updated
+   * @param oldValue   the old value
+   * @param newValue   the new value
+   */
+  private void updateValue(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+    paint();
+  }
+
+  /**
+   * Handle painting of the block canvas
+   */
+  public void paint() {
+    //If the block is empty, paint as empty
+    if (value.get() == 0) {
+      paintEmpty();
+    } else {
+      //If the block is not empty, paint with the colour represented by the value
+      paintColor(COLOURS[value.get()]);
     }
+  }
+
+  /**
+   * Paint this canvas empty
+   */
+  private void paintEmpty() {
+    var gc = getGraphicsContext2D();
+
+    //Clear
+    gc.clearRect(0.0D, 0.0D, this.width, this.height);
+
+    //Fill
+    Color gradStart = Color.color(0.0D, 0.0D, 0.6D, 0.3D);
+    Color gradEnd = Color.color(0.0D, 0.0D, 0.2D, 0.5D);
+    gc.setFill(new LinearGradient(0.0D, 0.0D, 1.0D, 1.0D, true, CycleMethod.REFLECT, new Stop(0.0D, gradStart), new Stop(1.0D, gradEnd)));
+    gc.fillRect(0.0D, 0.0D, this.width, this.height);
+
+    //Border
+    gc.setStroke(Color.WHITE);
+    gc.setGlobalAlpha(0.7);
+    gc.strokeRect(0, 0, width, height);
+  }
+
+  /**
+   * Paint this canvas with the given colour
+   *
+   * @param colour the colour to paint
+   */
+  private void paintColor(Paint colour) {
+    var gc = getGraphicsContext2D();
+
+    //Clear
+    gc.clearRect(0, 0, width, height);
+
+    //Colour fill
+    gc.setFill(colour);
+    gc.setGlobalAlpha(1);
+    gc.fillRect(0, 0, width, height);
+
+    //Border
+    gc.setStroke(Color.BLACK);
+    gc.strokeRect(0, 0, width, height);
+  }
+
+  /**
+   * Get the column of this block
+   *
+   * @return column number
+   */
+  public int getX() {
+    return x;
+  }
+
+  /**
+   * Get the row of this block
+   *
+   * @return row number
+   */
+  public int getY() {
+    return y;
+  }
+
+  /**
+   * Get the current value held by this block, representing it's colour
+   *
+   * @return value
+   */
+  public int getValue() {
+    return this.value.get();
+  }
+
+  /**
+   * Bind the value of this block to another property. Used to link the visual block to a corresponding block in the Grid.
+   *
+   * @param input property to bind the value to
+   */
+  public void bind(ObservableValue<? extends Number> input) {
+    value.bind(input);
+  }
 
 }
diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBoard.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBoard.java
index b587148..1131d32 100644
--- a/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBoard.java
+++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBoard.java
@@ -1,175 +1,199 @@
 package uk.ac.soton.comp1206.component;
 
+import javafx.scene.input.MouseButton;
 import javafx.scene.input.MouseEvent;
 import javafx.scene.layout.GridPane;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import uk.ac.soton.comp1206.event.BlockClickedListener;
+import uk.ac.soton.comp1206.event.RightClickListener;
 import uk.ac.soton.comp1206.game.Grid;
 
 /**
  * A GameBoard is a visual component to represent the visual GameBoard.
  * It extends a GridPane to hold a grid of GameBlocks.
- *
+ * <p>
  * The GameBoard can hold an internal grid of it's own, for example, for displaying an upcoming block. It also be
  * linked to an external grid, for the main game board.
- *
+ * <p>
  * The GameBoard is only a visual representation and should not contain game logic or model logic in it, which should
  * take place in the Grid.
  */
 public class GameBoard extends GridPane {
 
-    private static final Logger logger = LogManager.getLogger(GameBoard.class);
-
-    /**
-     * Number of columns in the board
-     */
-    private final int cols;
-
-    /**
-     * Number of rows in the board
-     */
-    private final int rows;
-
-    /**
-     * The visual width of the board - has to be specified due to being a Canvas
-     */
-    private final double width;
-
-    /**
-     * The visual height of the board - has to be specified due to being a Canvas
-     */
-    private final double height;
-
-    /**
-     * The grid this GameBoard represents
-     */
-    final Grid grid;
-
-    /**
-     * The blocks inside the grid
-     */
-    GameBlock[][] blocks;
-
-    /**
-     * The listener to call when a specific block is clicked
-     */
-    private BlockClickedListener blockClickedListener;
-
-
-    /**
-     * Create a new GameBoard, based off a given grid, with a visual width and height.
-     * @param grid linked grid
-     * @param width the visual width
-     * @param height the visual height
-     */
-    public GameBoard(Grid grid, double width, double height) {
-        this.cols = grid.getCols();
-        this.rows = grid.getRows();
-        this.width = width;
-        this.height = height;
-        this.grid = grid;
-
-        //Build the GameBoard
-        build();
+  private static final Logger logger = LogManager.getLogger(GameBoard.class);
+
+  /**
+   * Number of columns in the board
+   */
+  private final int cols;
+
+  /**
+   * Number of rows in the board
+   */
+  private final int rows;
+
+  /**
+   * The visual width of the board - has to be specified due to being a Canvas
+   */
+  private final double width;
+
+  /**
+   * The visual height of the board - has to be specified due to being a Canvas
+   */
+  private final double height;
+
+  /**
+   * The grid this GameBoard represents
+   */
+  final Grid grid;
+
+  /**
+   * The blocks inside the grid
+   */
+  GameBlock[][] blocks;
+
+  /**
+   * The listener to call when a specific block is clicked
+   */
+  protected BlockClickedListener blockClickedListener;
+
+  protected RightClickListener rightClickListener;
+
+  /**
+   * Create a new GameBoard, based off a given grid, with a visual width and height.
+   *
+   * @param grid   linked grid
+   * @param width  the visual width
+   * @param height the visual height
+   */
+  public GameBoard(Grid grid, double width, double height) {
+    this.cols = grid.getCols();
+    this.rows = grid.getRows();
+    this.width = width;
+    this.height = height;
+    this.grid = grid;
+
+    //Build the GameBoard
+    build();
+  }
+
+  /**
+   * Create a new GameBoard with it's own internal grid, specifying the number of columns and rows, along with the
+   * visual width and height.
+   *
+   * @param cols   number of columns for internal grid
+   * @param rows   number of rows for internal grid
+   * @param width  the visual width
+   * @param height the visual height
+   */
+  public GameBoard(int cols, int rows, double width, double height) {
+    this.cols = cols;
+    this.rows = rows;
+    this.width = width;
+    this.height = height;
+    this.grid = new Grid(cols, rows);
+
+    //Build the GameBoard
+    build();
+  }
+
+  /**
+   * Get a specific block from the GameBoard, specified by it's row and column
+   *
+   * @param x column
+   * @param y row
+   * @return game block at the given column and row
+   */
+  public GameBlock getBlock(int x, int y) {
+    return blocks[x][y];
+  }
+
+  /**
+   * Build the GameBoard by creating a block at every x and y column and row
+   */
+  protected void build() {
+    logger.info("Building grid: {} x {}", cols, rows);
+
+    setMaxWidth(width);
+    setMaxHeight(height);
+
+    setGridLinesVisible(true);
+
+    blocks = new GameBlock[cols][rows];
+
+    for (var y = 0; y < rows; y++) {
+      for (var x = 0; x < cols; x++) {
+        createBlock(x, y);
+      }
     }
-
-    /**
-     * Create a new GameBoard with it's own internal grid, specifying the number of columns and rows, along with the
-     * visual width and height.
-     *
-     * @param cols number of columns for internal grid
-     * @param rows number of rows for internal grid
-     * @param width the visual width
-     * @param height the visual height
-     */
-    public GameBoard(int cols, int rows, double width, double height) {
-        this.cols = cols;
-        this.rows = rows;
-        this.width = width;
-        this.height = height;
-        this.grid = new Grid(cols,rows);
-
-        //Build the GameBoard
-        build();
-    }
-
-    /**
-     * Get a specific block from the GameBoard, specified by it's row and column
-     * @param x column
-     * @param y row
-     * @return game block at the given column and row
-     */
-    public GameBlock getBlock(int x, int y) {
-        return blocks[x][y];
-    }
-
-    /**
-     * Build the GameBoard by creating a block at every x and y column and row
-     */
-    protected void build() {
-        logger.info("Building grid: {} x {}",cols,rows);
-
-        setMaxWidth(width);
-        setMaxHeight(height);
-
-        setGridLinesVisible(true);
-
-        blocks = new GameBlock[cols][rows];
-
-        for(var y = 0; y < rows; y++) {
-            for (var x = 0; x < cols; x++) {
-                createBlock(x,y);
-            }
-        }
-    }
-
-    /**
-     * Create a block at the given x and y position in the GameBoard
-     * @param x column
-     * @param y row
-     */
-    protected GameBlock createBlock(int x, int y) {
-        var blockWidth = width / cols;
-        var blockHeight = height / rows;
-
-        //Create a new GameBlock UI component
-        GameBlock block = new GameBlock(this, x, y, blockWidth, blockHeight);
-
-        //Add to the GridPane
-        add(block,x,y);
-
-        //Add to our block directory
-        blocks[x][y] = block;
-
-        //Link the GameBlock component to the corresponding value in the Grid
-        block.bind(grid.getGridProperty(x,y));
-
-        //Add a mouse click handler to the block to trigger GameBoard blockClicked method
-        block.setOnMouseClicked((e) -> blockClicked(e, block));
-
-        return block;
-    }
-
-    /**
-     * Set the listener to handle an event when a block is clicked
-     * @param listener listener to add
-     */
-    public void setOnBlockClick(BlockClickedListener listener) {
-        this.blockClickedListener = listener;
+  }
+
+  /**
+   * Create a block at the given x and y position in the GameBoard
+   *
+   * @param x column
+   * @param y row
+   */
+  protected GameBlock createBlock(int x, int y) {
+    var blockWidth = width / cols;
+    var blockHeight = height / rows;
+
+    //Create a new GameBlock UI component
+    GameBlock block = new GameBlock(this, x, y, blockWidth, blockHeight);
+
+    //Add to the GridPane
+    add(block, x, y);
+
+    //Add to our block directory
+    blocks[x][y] = block;
+
+    //Link the GameBlock component to the corresponding value in the Grid
+    block.bind(grid.getGridProperty(x, y));
+
+    //Add a mouse click handler to the block to trigger GameBoard blockClicked method
+    //block.setOnMouseClicked((e) -> blockClicked(e, block));
+
+    block.setOnMouseClicked(e -> {
+      if (e.getButton() == MouseButton.PRIMARY) {
+        blockClicked(block);
+      } else {
+        blockRightClicked(block);
+      }
+    });
+    return block;
+  }
+
+  /**
+   * Set the listener to handle an event when a block is clicked
+   *
+   * @param listener listener to add
+   */
+  public void setOnBlockClick(BlockClickedListener listener) {
+    this.blockClickedListener = listener;
+  }
+
+  public void setOnRightClick(RightClickListener listener) {
+    this.rightClickListener = listener;
+  }
+
+  /**
+   * Triggered when a block is clicked. Call the attached listener.
+   *
+   * @param block block clicked on
+   */
+  private void blockClicked(GameBlock block) {
+    logger.info("Block clicked: {}", block);
+
+    if (blockClickedListener != null) {
+      blockClickedListener.blockClicked(block);
     }
+  }
 
-    /**
-     * Triggered when a block is clicked. Call the attached listener.
-     * @param event mouse event
-     * @param block block clicked on
-     */
-    private void blockClicked(MouseEvent event, GameBlock block) {
-        logger.info("Block clicked: {}", block);
-
-        if(blockClickedListener != null) {
-            blockClickedListener.blockClicked(block);
-        }
+  private void blockRightClicked(GameBlock block) {
+    if (this.rightClickListener != null) {
+      this.rightClickListener.rightClicked();
     }
+  }
 
 }
diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/component/PieceBoard.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/PieceBoard.java
index c2d35fd..5abd200 100644
--- a/tetrecs/src/main/java/uk/ac/soton/comp1206/component/PieceBoard.java
+++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/PieceBoard.java
@@ -1,16 +1,29 @@
 package uk.ac.soton.comp1206.component;
 
+import uk.ac.soton.comp1206.event.BlockClickedListener;
 import uk.ac.soton.comp1206.game.GamePiece;
 
 public class PieceBoard extends GameBoard {
 
-  private GamePiece pieceToDisplay;
-
   public PieceBoard(double width, double height) {
     super(3, 3, width, height);
   }
 
+  public void displayPiece(GamePiece piece, int placeX, int placeY) {
+    clear();
+    this.grid.playPiece(piece, placeX + 1, placeY + 1);
+  }
+
   public void displayPiece(GamePiece piece) {
-    this.pieceToDisplay = piece;
+    displayPiece(piece, 0, 0);
+  }
+
+  public void clear() {
+    this.grid.clear();
+  }
+
+  public void setOnClick(BlockClickedListener handler) {
+    this.blockClickedListener = handler;
   }
+
 }
diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/event/NextPieceListener.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/event/NextPieceListener.java
new file mode 100644
index 0000000..09971bd
--- /dev/null
+++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/event/NextPieceListener.java
@@ -0,0 +1,7 @@
+package uk.ac.soton.comp1206.event;
+
+import uk.ac.soton.comp1206.game.GamePiece;
+
+public interface NextPieceListener {
+  void nextPiece(GamePiece paramGamePiece);
+}
diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/event/RightClickListener.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/event/RightClickListener.java
new file mode 100644
index 0000000..9b2c5ab
--- /dev/null
+++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/event/RightClickListener.java
@@ -0,0 +1,5 @@
+package uk.ac.soton.comp1206.event;
+
+public interface RightClickListener {
+  void rightClicked();
+}
diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Game.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Game.java
index af904f4..fb971dd 100644
--- a/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Game.java
+++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Game.java
@@ -5,6 +5,7 @@ import javafx.beans.property.SimpleIntegerProperty;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import uk.ac.soton.comp1206.component.GameBlock;
+import uk.ac.soton.comp1206.event.NextPieceListener;
 import uk.ac.soton.comp1206.utility.Multimedia;
 
 import java.util.ArrayList;
@@ -43,6 +44,8 @@ public class Game {
 
     private int remainder = 0;
 
+    protected NextPieceListener nextPieceHandler = null;
+
     /**
      * Create a new game with the specified rows and columns. Creates a corresponding grid model.
      * @param cols number of columns
@@ -77,7 +80,6 @@ public class Game {
 
         this.nextPiece = spawnPiece();
         nextPiece();
-        this.currentPiece = spawnPiece();
     }
 
     /**
@@ -93,14 +95,19 @@ public class Game {
         logger.info("Block clicked at x = {}  y = {}",x,y);
         boolean placed = grid.playPiece(currentPiece, x, y);
         if (placed) {
+            logger.info("Placing block at x:{} y:{}",x,y);
+            Multimedia.playSound("place.wav");
             this.afterPiece();
             this.nextPiece();
+        } else {
+            logger.info("Cannot place piece here");
+            Multimedia.playSound("fail.wav");
         }
     }
 
     public GamePiece spawnPiece() {
         Random rand = new Random();
-        GamePiece piece = GamePiece.createPiece(3, rand.nextInt(3));
+        GamePiece piece = GamePiece.createPiece(rand.nextInt(15), rand.nextInt(3));
         logger.info("Spawning piece: {}" , piece);
         return piece;
     }
@@ -109,6 +116,10 @@ public class Game {
         this.currentPiece = this.nextPiece;
         this.nextPiece = spawnPiece();
 
+        if (this.nextPieceHandler != null) {
+            this.nextPieceHandler.nextPiece(this.currentPiece);
+        }
+
         logger.info("Current piece is : {}", this.currentPiece);
         logger.info("Next piece is: {}", this.nextPiece);
 
@@ -195,6 +206,12 @@ public class Game {
         }
     }
 
+    public void swapPiece() {
+        GamePiece tempPiece = this.currentPiece;
+        this.currentPiece = this.nextPiece;
+        this.nextPiece = tempPiece;
+    }
+
     /**
      * Get the grid model inside this game representing the game state of the board
      * @return game grid model
@@ -219,6 +236,22 @@ public class Game {
         return rows;
     }
 
+    public GamePiece getCurrentPiece() {
+        return currentPiece;
+    }
+
+    public GamePiece getNextPiece() {
+        return nextPiece;
+    }
+
+    public void setOnNextPiece(NextPieceListener handler) {
+        this.nextPieceHandler = handler;
+    }
+
+    public void rotatePiece(int rotations) {
+        this.currentPiece.rotate(rotations);
+     }
+
     public IntegerProperty getLevel() {
         return level;
     }
diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Grid.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Grid.java
index 7905236..1e66ea3 100644
--- a/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Grid.java
+++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Grid.java
@@ -101,8 +101,6 @@ public class Grid {
                 int value = blocks[x][y];
                 if (value == 0) continue;
                 if (get(x + placeX, y + placeY) != 0) {
-                    logger.info("Cannot place piece here");
-                    Multimedia.playSound("fail.wav");
                     return false;
                 }
             }
@@ -113,8 +111,6 @@ public class Grid {
     public boolean playPiece(GamePiece gamePiece, int placeX, int placeY) {
         if (!this.canPlayPiece(gamePiece, placeX, placeY)) return false;
 
-        logger.info("Placing block at x:{} y:{}",placeX,placeY);
-        Multimedia.playSound("place.wav");
         int[][] blocks = gamePiece.getBlocks();
         placeX = placeX - 1;
         placeY = placeY - 1;
@@ -129,6 +125,14 @@ public class Grid {
         return true;
     }
 
+    public void clear() {
+        for (int y = 0; y < this.rows; y++) {
+            for (int x = 0; x < this.cols; x++) {
+                this.grid[x][y].set(0);
+            }
+        }
+    }
+
     /**
      * Get the number of columns in this game
      * @return number of columns
diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/ChallengeScene.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/ChallengeScene.java
index b5fbf96..d2461ba 100644
--- a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/ChallengeScene.java
+++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/ChallengeScene.java
@@ -15,6 +15,7 @@ import uk.ac.soton.comp1206.component.GameBlock;
 import uk.ac.soton.comp1206.component.GameBoard;
 import uk.ac.soton.comp1206.component.PieceBoard;
 import uk.ac.soton.comp1206.game.Game;
+import uk.ac.soton.comp1206.game.GamePiece;
 import uk.ac.soton.comp1206.ui.GamePane;
 import uk.ac.soton.comp1206.ui.GameWindow;
 import uk.ac.soton.comp1206.utility.Multimedia;
@@ -29,6 +30,10 @@ public class ChallengeScene extends BaseScene {
     private static final Logger logger = LogManager.getLogger(MenuScene.class);
     protected Game game;
 
+    protected GameBoard board;
+    protected PieceBoard currentPieceBoard;
+    protected PieceBoard nextPieceBoard;
+
     /**
      * Create a new Single Player challenge scene
      * @param gameWindow the Game Window
@@ -50,7 +55,6 @@ public class ChallengeScene extends BaseScene {
         root = new GamePane(gameWindow.getWidth(),gameWindow.getHeight());
 
         StackPane challengePane = new StackPane();
-        challengePane.setBackground(new Background(new BackgroundFill[] { new BackgroundFill((Paint) Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY) }));
         challengePane.setMaxWidth(gameWindow.getWidth());
         challengePane.setMaxHeight(gameWindow.getHeight());
         challengePane.getStyleClass().add("menu-background");
@@ -114,11 +118,12 @@ public class ChallengeScene extends BaseScene {
     public HBox buildBoards() {
         HBox boards = new HBox();
 
-        var board = new GameBoard(game.getGrid(),gameWindow.getWidth()/2,gameWindow.getWidth()/2);
+        this.board = new GameBoard(game.getGrid(),gameWindow.getWidth()/2,gameWindow.getWidth()/2);
         //Handle block on gameboard grid being clicked
-        board.setOnBlockClick(this::blockClicked);
+        this.board.setOnBlockClick(this::blockClicked);
+        this.board.setOnRightClick(this::rotateBlock);
 
-        boards.getChildren().add(board);
+        boards.getChildren().add(this.board);
 
         VBox gameInfo = new VBox();
 
@@ -143,11 +148,13 @@ public class ChallengeScene extends BaseScene {
         incomingTitle.getStyleClass().add("heading");
         gameInfo.getChildren().add(incomingTitle);
 
-        PieceBoard currentPieceBoard = new PieceBoard(gameWindow.getWidth()/6, gameWindow.getWidth()/6);
-        gameInfo.getChildren().add(currentPieceBoard);
+        this.currentPieceBoard = new PieceBoard(gameWindow.getWidth()/6, gameWindow.getWidth()/6);
+        this.currentPieceBoard.setOnClick(this::rotateBlock);
+        gameInfo.getChildren().add(this.currentPieceBoard);
 
-        PieceBoard nextPieceBoard = new PieceBoard(gameWindow.getWidth()/10, gameWindow.getWidth()/10);
-        gameInfo.getChildren().add(nextPieceBoard);
+        this.nextPieceBoard = new PieceBoard(gameWindow.getWidth()/10, gameWindow.getWidth()/10);
+        this.nextPieceBoard.setOnClick(this::swapPiece);
+        gameInfo.getChildren().add(this.nextPieceBoard);
 
         gameInfo.setAlignment(Pos.CENTER);
         gameInfo.setSpacing(10);
@@ -160,6 +167,13 @@ public class ChallengeScene extends BaseScene {
         return boards;
     }
 
+    private void swapPiece(GameBlock gameBlock) {
+        logger.info("Swapping current and next piece");
+        this.currentPieceBoard.displayPiece(this.game.getNextPiece());
+        this.nextPieceBoard.displayPiece(this.game.getCurrentPiece());
+        this.game.swapPiece();
+    }
+
     /**
      * Handle when a block is clicked
      * @param gameBlock the Game Block that was clocked
@@ -168,14 +182,27 @@ public class ChallengeScene extends BaseScene {
         game.blockClicked(gameBlock);
     }
 
+    private void rotateBlock(int rotations) {
+        logger.info("Rotating block");
+        Multimedia.playSound("rotate.wav");
+        this.game.rotatePiece(rotations);
+        this.currentPieceBoard.displayPiece(this.game.getCurrentPiece());
+    }
+
+    private void rotateBlock(GameBlock gameBlock) {
+        rotateBlock(1);
+    }
+
+    private void rotateBlock() {
+        rotateBlock(1);
+    }
 
     /**
      * Setup the game object and model
      */
     public void setupGame() {
         logger.info("Starting a new challenge");
-        Multimedia.playBackgroundMusic("game_start.wav", false);
-        Multimedia.getBackgroundPlayer().setOnEndOfMedia(() -> Multimedia.playBackgroundMusic("game.wav", true));
+
         //Start new game
         game = new Game(5, 5);
     }
@@ -186,7 +213,16 @@ public class ChallengeScene extends BaseScene {
     @Override
     public void initialise() {
         logger.info("Initialising Challenge");
+        Multimedia.playBackgroundMusic("game_start.wav", false);
+        Multimedia.getBackgroundPlayer().setOnEndOfMedia(() -> Multimedia.playBackgroundMusic("game.wav", true));
+
+        this.game.setOnNextPiece(this::nextPiece);
         game.start();
     }
 
+    protected void nextPiece(GamePiece nextPiece) {
+        logger.info("Next piece to place: " + nextPiece);
+        this.currentPieceBoard.displayPiece(nextPiece);
+        this.nextPieceBoard.displayPiece(this.game.getNextPiece());
+    }
 }
diff --git a/tetrecs/target/classes/uk/ac/soton/comp1206/component/GameBoard.class b/tetrecs/target/classes/uk/ac/soton/comp1206/component/GameBoard.class
index 5f8a073ae3bae068119a84b6ff70d71bff47c4bc..7d7f7ec15155e1956eee50ce3814870034b662b0 100644
GIT binary patch
delta 1538
zcmX@F(5%XJ>ff$?3=9mm497Qe^|LB*GVn3v@i62w6fiOf6=kMplsM;PCTII(7MG;v
zr4}(V=ue)=E;hN2l}#)NL)Myup=k0PRte4$9tJyxQbq>#$#qPs{F-6h4CM?J><pDW
z3{?!(lRvT+3UD*jGSsm%)blVjFt9N)=uSSsA__9q5Ytpf1|Eb%Qd77Xni!hd8CrN4
zS{d3H8Du9HvdIcEGH|A+mN=D`l;r0zGAL<iPF~0*DGuRVb24-=bh0ya@i25V^e{5W
zO)g}U5QfMxGOz~(dHOmAMKUrdfb>{F#I3m)_!ufcL_ZJ11cqux2EnA9{N!we>p%{f
z#4wqiVG0k!REBAc49b%Q*=06AXIsuF!e5+P;-805%gA7)0Sh)!gfJ{ntu@0YZ{x^e
zl%KqiOMbEfr#;^mE{3fP+t?Yl^DyjS*tt2ElZ8=dHzR|%hEG;vSz=m+erj22UWvXd
zh_u!W<7Z{q3v%l|9){HnYXle$OrFbS#&VdEL3Q#yPVve5Tx{IMDn9x7*`)>S3`Zyb
z=Q3vGoNUZ(Db2!=&5*;uz`z2Ea0UhjZU#mMR)*;ej0`g-*K=E$&tza=P-5U<U|?`%
zU}11$;9+oQ5Ml6OkYw;=kZ15>P+{<8&|vUmn8m=rpvAz#z`&r%Fq>h{<PY4ctaHJp
zNb%SpnG(ss!Vty4$q>!J#}LCH%n-{U$q>h&#*oOM#gGIx#e{*Cfq_9AY)UVWF8e$N
zf3P_Rd8DjCu3=+fWME*(2CG$IU}j)o-~_2<00B;h`3yo}c}|7}3_=V{3@i+43?Lmd
zCX4ZwMT7L~F>o_5FcdLJFqAMTFqATAFqAXsGE_3yGE_6TGt@8yGSo6eFw`)_F;p|8
zFf_pJ@njGMo6f{w&!7r69psKwsND-EKj)QYoH3b|FTEaQ3na`s7`Pca8N?ZS7}Oa0
z8T1$?F<3B6WpHDd#SqM}5Nxs?0~-SaLm1Q)5DVsmFt8m=4BQOf42u|K7?>Hv8SEJr
zGsrNoGN>^aFf3tM%D~RR$gqrIIVgM>7#Qprm>8HC7$(2w;|05c3&dks$-v663T&4c
zn9l~ci;ZCgLk<HY10%yqhE=F$FtBN9Z)M2c$iTqB!~pUS#H_UptPJaLnpMES$iM{3
z-wcfP3^N!Q7}6M+!B)y{V$c&R)ZWQZ%*>#*g(348122fu-pNqL!~n8`8El6TIQ?v8
z;AYs)AjGhPL55)$l3lWJcguqP&d9Kkfq{XUfl-Em36w#h9tZ<FqMm_4P-_cA?lA@)
z5Vez`h7rjkK5(+x%fQaCk3oRp0Fotwa7zSHEfEl40_7~IB{>XCU?U`SwlOqrVrXB+
zz^$ddjiGlr0|$ui+gJ~F8Yti(o|?_T#W06Kh+!UsIKu+4eUPLp0k=;AY99lG1jAZ}
zbqq{k@3Am239&G6Fsz3pVg?2VMR4+FVBi$u+rcn#GlL!|{20OE#|D;?U|?q0paG7!
ojSQO@7#MgM7#TJ*Ff;68*u$`&;UEJ80~Z4$!y$$v496HG0Z2qAd;kCd

delta 1055
zcmZoxJ+HuZ>ff$?3=9mm3>!9b^|LZ_Fyu{s%PPTEz{6n2P&k>JO^2_Tp@f~Gl!u{=
zp`4LHfARqq(aCkJQj<&A?D@DD_!ufdGF6NWJ{pq~xa22q<WLpQ$xqI<)(qofs9~sO
zXQ<<0sAp(kWKf=L$RV?tontv88*6G=YF^3YST2>xUpccTH*(qX&E;a4$1tCrVF3@r
zLWV_~&v3CYGA@~Xkz1B;8OWC9JPcDArU@{tocxE|jAacYgX-i&9yu-*pZxsn(gJpd
zb(7<Gj2Ss5PvWr@W@5-@$YEe$;ACKAU}b1zU}R{T{EWxSyqST4L5YEbfq}t^frY`D
zfrr6`L4?7TL6X6ZL7u^#L50DKL4(1Yp@o5gL5qQffq_Ajp_QR+ayYLlYdhGKMqV2v
zQ^FWn7{VDi86p_?7$O;j8KM{@8KN1~7~&YT7~;XEm@u$1FfeF?P5HvB8`Hty4>E^=
zfx(@DiGdm9My)LjTGB=`MjIFy7?>D9PGn<XWME*(02`nHQ3Nu80R%W1IvIq(@|+A^
z3_=V{3@i+43?Lm%lWY0PqCxug7`Pc281fk;7z!B_7>XD)7)ls)8Oj)J87dgu87dh9
z8LAi}7%Ca!7%CW27;546cru8BO=n`TXHW&34su5-)b8%de*ChGO_Ou@)9XRDKmxLr
zft#U?L7btJL5-n@L64!I!Gd8DgB!zChG2&2V3X|_*cccX!l0&rSTG-if$d;o;AZe<
z=wXmyU}g|!uxIFHkYQkDP-8G)=ws+-U}s=tn7}X*8fbP5Obkp64D}3bTH0F~GB?75
zj0+^pFrR^yVFB1uF)*JEZYdkXB!(OYMg}H^GKMOsVFe7#;J}sL#GogXtFx0KpP4~x
z3q$5H23`=Qvy-8Si2-B<GdNa+zzJdn12@B}dIlke)eJHWYr%FwqDU5Qmn_3%hAH4E
zk_CH#iJ_Z;fq|KUQAU6Xl!l?!yF#sJ*V576#!vyW2s!$vBALw&H=7-7Vj3u3*cqlX
zOkrSR;9!^miGKzL21T&73=Et?d^;GbH#6vgOl1T+pA9S}#=y)l6C7%@7-lmtFz_%i
gGR$FMW?0Oylwk$KDh37yE(S)1)eLJH)-y-~0Eeil=l}o!

diff --git a/tetrecs/target/classes/uk/ac/soton/comp1206/game/Game.class b/tetrecs/target/classes/uk/ac/soton/comp1206/game/Game.class
index 54142e5420e92cd5cec8ffadb11a9b80935bf8b5..9bee46cc02c85f4d236ae529187f1b0a6ca79b2f 100644
GIT binary patch
literal 7125
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
bN-%;*DKII+2<rQAF)%X9Gb%DFGe`meRPCg>

delta 3721
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

diff --git a/tetrecs/target/classes/uk/ac/soton/comp1206/game/Grid.class b/tetrecs/target/classes/uk/ac/soton/comp1206/game/Grid.class
index 002e059f2affcc6dc3522671f50858fd1a9f5865..915e5a5f1411e7a79631ab290a29233d57d65412 100644
GIT binary patch
delta 1309
zcmaDZ_FRPP)W2Q(7#J9A8KO6Gtz+ckV&GuV<zdic(4V}O$wmysHRNHCVK8E3VAJsQ
z^wbPvXE2$3fLWH$jKQ3p!GedulEI3RL3y$vi_Bzp7B4YQ1`Y-r9tK+mJ4Ob!oc#3k
z)FMU(#mxpRnT(=Aj0~IwIf<14nW@RCj0~*FIjM<7lb5l&GTSoPO@7BJ&CSKY$iT{A
z&%nswFj<5xoY8S|4x7HR69WT-2?HyG5`!#*GXoC;3xgDcHiHWT4+Ap;1A`lbD}&qQ
zeQe^4?vt;vDX@Dmc!Kr+Ws|CRWME)mV_;-pU~psrnW6yJ!3omA00Nv~^FZ>PVDp$5
z7(t!|>1SYIh-6@5U}0cj;MLl~AS7)hV<fwcL24rd0|OHS$Y3r8CI$uuZw4L)AF$zC
z5XErAc^Q;Jt_7LS3pJgAftSGx>~2N|c?NBeJ(G8`Yk=L!2X<RH0}n$41201q+(=#q
zCI)W?AFwPx+;D!l%lV;ZyF>iy!_J_{zzB*#km(Ezj3Nvy3>*v$3=!Jf7?iD8WLQ#c
zWm!_}n9W#4StVKaEn;wB_A+A=Ws_tPWi{Hjh(QO;5M|RdV;5zg#lW_lK}1WGRVG1{
zO*Ua0gXT;IR^~M=Y>dnQpKoDhT>gI_%Nhm-24--G7%}in-pQd_@5PY95WtYk5XO)L
z_C^o`2ZJGlB-rch3^EKRU?;ON2r{^Wy}=4{9^7jY489D0;7E&L@MquwhjIi%065s#
z7=joA8G;yC7(m{YVqp8rAjr-T%=m|ai}4SG4C5~bfnN-2tiKpo85kKt81xw!85|fG
z80Da0kf^0CBfE`3XBNm4lNWKi)f<4latZ8}gQ#9%U|=X<;9)3Y5Mn515MwA|kYXrf
zP-Q4*FlMM=uw|%ZaAl}s2xcf}h-4^Zh-avUd)1af4C+-421BS<Wf`2{UQL91HL)J<
z*F?Bq6QO<;V(>@ut26^U&tC=sRL}DMVu1QJ6r9c-7#J9)GBAPDiY~K_wAK~|-bD<m
z%(gOGTNv0EF^GavvW$_m5%V?%sTL;2<@NtxwlFd-|9=ydtRayo#lXhEz|hFR#n8kc
z!qCj1z|h2?#?Z*1!_WryAt(>=Fi3%Y!^FVEpau3JD3$9%{RN5|0S1;o3`~rF7&sY!
zF-St<BaA@-5+C4r0qJ0!e2R+;ocbZTv=?jx4><B!p{6l1gk$Ohc?^{9AUP4Dc`6po
r91IcgfB|I-aHbbB-oaqHnZX+5XGR7`5lF^iU|<kqU}T5{=kF*0E3vMX

delta 1491
zcmaDZ@?4DT)W2Q(7#J9A8Ok<ttz+ckWZ+=X<zdic(4V}XNmrMH!H|(b+&M8XFTX^g
zASW?7RiPj=H91uwBef`%o57gDgq^{Zhrx`&oRL9(vOSZGLwafvBLhojURpjQgQ$j2
zR$^JAeokUux_)p;QD$DcwPqLxgC!#aM_OWLj$V0U85e^!gAF@_Ef0eo$Po3(`HZ3(
zr6rj;nI)C_zNI-OnYpPcnTd=HoCP_FmBIO?c_~~B91IRT42}#=lMgZ4)N?SnFfvF5
z<Rm6%=A|nn<>V)4D<qaER9IElDpXok*K#qqF}Slcc<?ZIGI%jE2qK*1nOBkub}xHb
zVoqtQe;Ol$n1-h&lB1z2tU+G&0r}IHk--wxSHWO!A?fi?%1TWxvBneudEFo4k>pf}
z-#Hipc^G6Ef*2XtG(0^$HN)5$LKqn&K!G6OSWv*l5XKPB&Je-F5XlfV*@h`|^BpD+
zM&3M#A(a83sF-ZY;>rw)uSOPWZcYY923Ce>21bUM$;(;78Dl5^XVF)VV_;w~VPIuY
zVvuEsXW(IAVUS|bW=LS*VPIxpU~pqdWJsDE$STg5JUNq9fjxyG6|8>(t5kg~0|Nsa
z10w?igChgT6a}yjPLK`;5a0xx2a@Lmo5#e!2=Wd{KLZ0pBm)xz3j+fKuhtd@A!#ES
zBiU^XQX3f<7?>D926HhmF)%QAGw?9@fDPAzD25x(%b*N$Ey#3UsObz0ybNhzcQZ1`
zGiZbCne4-+0d^-J*lpnqJPZ*GybMurBY7E^7}6Osz_R>s!};MZ=ZBh|4DqWEJA)zv
zBPex&OlM$V)Mj8|;9y{2NY~!Rplrn=!;)ev%aUTpY{n|eD#@~M5raQ-q#2tin<R@U
ztI@ti4Ax+VD4U)cyD0lC2Dar4%37kVG6|w=vI*N5G-oo1Y+}$6GT6yr#Ka(Ev4g>i
zc})u&<MRIpT38vE|6k9thJk^B85}@X41AMq*;VU<7%~~c8L}868B!SH8Il=N8S=os
z3u544Fl5jGhX^}^41+V+Cu|IY3?X3Ov4UI;_i;KyCPNlDF4GyZ8F;{fpU#j24ox<O
zM21|3JO&m9P$)<-u>EBaWM{}{{KLS-_{Wt&hVd7J{4WO9Ukt1aj0^<~PSBvSg$7N9
zmbQ%SHU|4y46Mv+K)!R9*}>o{yMw`XCxbT&gOJ}22CK>J9B%aiV4q$B`}82HPZ<~(
ziWqnpN*LrAN*R<H$`~{m${BPSDi{nIsu*k-su{c(Y8XNoY8he}>KL*Zsu>Cxsu;=`
z8sQ-k!XO3>33~=ZXh@haIKe}rf+5kGAqkXf87kmGQ2`H%3TRMhGWa6}g){>@&tC?0
zc82`P|2ah23mFtZUZ1>=Qw*HuAbFz`>;@ii*s?;yn~|XiqLzVyL7#yUBB!;5ffp3L
zkn|7HJQ0g#4u)cIwu@n404GL}qdA3wcQAx*X3zm;AV!8*5lC`nU|<kqU}Pu(=c-Zw
DTdo7L

diff --git a/tetrecs/target/classes/uk/ac/soton/comp1206/scene/ChallengeScene.class b/tetrecs/target/classes/uk/ac/soton/comp1206/scene/ChallengeScene.class
index db3f11b372bf64a21aa8eddfd74d0f4317da0970..307d07de2a84933ea7a5dc80b9504e5c98c000a8 100644
GIT binary patch
delta 4581
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
AaR2}S

delta 3702
zcmbR3^wN^+)W2Q(7#J9A8Ot_ug)+0TGq^A^$W30zA|l|Fn4FznlwX>c!p`8v$e;ug
z%ti>hW#;5?GI%g}vNL$`FnBZgFfvGFC6*<oRp=Kdr{<;V7bIrpmFPR?=j0bLGO#-N
zI68YXGD!L0Qe(}@;K$(4&Je)E5Xcb3$RH20Ps};LC@-}rC^027laYbd)i)p{l954i
zGNX`aJ(83)CqoEBC_6(KNN+eJgBaA3^wj*^)RLk~eb2n&)RJOG1__w;XfoDZ46Y23
zJPfQ1QH%^>8ZcGJ9ti+>#~L9snOlh46xj_hDRiSX!?+ln8Dc<&$1*Z#Xheftua6p#
z)*wap4Dmb+2@Hvh47|muB?twK49XfHJ^lzGsQP3ch7^WWMh5QU)Dqvsitx;ok_<)$
z77Z7Oap^n^86asMh_pv)W_m^m7lS=R77s%<L(b%d%vK6{j12s_sd=TkN$}|AW+-4N
zWM?SiVJK!Onf#mCO{$ECp`0Oyk%2oswZu6iGbg1eHIJR45}Hyb=d<t#RWLY#OfKVM
z;9#ibVW?xMpWMr$&r!k9$ivXY&^-AFi<&_z4?`P6JtG5mQfX#Ric@}KQA#lvLj^+z
z4?`zIGb00MacYTkYF<fd5j#USBZI``g)9=>Tnt?dPV5YQ5WWacm{WcQ7eg;dY~tit
zR%w&Tj0~*7$@xX8TnrN!rh<Z51jNeXV3@(kz@Cwsn39>74ia$!5wm$1<}l2iyoS||
zZ9Wgf0)~Z?Kd|cXEoNB4&ajk+VHv}6Mh3ZwjiQqU*f=M9uvzl0WLU+{u$qTq4Z~VS
z2Gz+2S;Z#jv++;f!e+*|o?!z!!$uy4O$?hE8B``8WL23g!)`3Nm63t97~-B@hB9`B
z9g`E-#W)HWcJVOmX4o^ij$MU&AH#ljh66kd2N@1cUd!&p(aUgzhv6v0vB}@rb?Unq
z$~YKKGBWTvXC&t2q~@ikD){E7q;fEvW@KP3$t=l9<zP6=$iV88S(aMN#ju#+JP*SK
zkh?k3Q%k^t91NEj8CY{b@|+C&7_RUzTxB@K$iU|8>KEb~1Pah2Yz~PH3?KmF-Qr=m
z4N50m#i=F1JOzo#nR)5#40jnBG(aKoo||j(Sq^TF#SHg(7#=V@ocw^pkoOM5V^Byx
zVPsI%@Byh3U}F<1%1p6#anTGDVqj!=#>4QO;l<=G&LWOG46k??UNgLz%);fF!ol#4
zk%7x2Gd)8A9BCX39~c=}4Gjzo3^*7*fg&su9C#cIUqF6LElbS-`SBYM!*{qJL4q6%
zKS6$kFn=>LaCqh==jVbQ{&!-X#N_wfBF0<{e;EGrFt9Q*Ffy=efLzArpaSwV0|O`p
z-C}cygz*+Lvhy%JXXKb{&uhfUHCcd9T9Jd1hmnCVJux>mzPKc@s6?+kv5bq6kCC69
zQGkb0kWpyz0$!cTIehvYe2gMIjG~NUlNa;ZaxgJU@Gwd;N=<&jr^qM6D9g?$$HOSk
zr~t|=`}yQ1oAT?pgM4Jk!Klp0Am*8uS(2HUlUbaZm#zShaW00%jH*10Y782n#00XP
zgHeN#fdk|kkUQ8JwHO&xCN@e>UM;}Qt;49x&Zx)3sLyDy`JliL#>u{%#*?=T1x`M~
z?I6g=z?YX=Q4)}unw$#Ga+BGFt!23~VfLmnG6>})<|d^isuZV|loq%r=1!h2?9La>
z#TdgF%gz|b!=TI<KlzRD5&1+$1}-O1)&;qhk)Mq*nTs)nF_oP$jfXLvF=O+25f(-R
z4t`d~Y%T^4Ms@)P4SqJpJP<pdhcOi77aq^NywoD+oW$bd)M9qVB2bnJ6cL}SFT%&h
z&R7BpBM6s$@?tR|StbT425tr(1_lNeP*!DNVBiErB?B7+BctKuS7M5;MhpxLd<@JC
z3=FCatPE-l{0!=h#taM$Vhl_S3=9H{CXA-wC>CJQWzb__WME`8W6%KUW?*1+W#DFD
zWnf^~J~=?#s(v?vb0o`N2G>aD-3+dgn;6_TG5Bs`2;R;R5xkutdK*KWw$>H~?QIPD
z?b{fVpls9jZ47DJ+ZZy1a(6Q1Gckl~ZDFw9#!#va;q7Lqh}_Ol<*U7op;`wbq_vHq
z#!p9g8$*NEHii}}79B~JZ4B)j85rsrm>5Bxabgf)U|`T=5MeN2kYF%kFk~=cuw*c0
zh-NTjNM|r-C}yx=sAsTb=w+~Gn95+ouz<myVJU+n!zu=6h7Am^4BHso!2w_b@ftJ$
zEE(z;%^58i7#XHAC^1?xS}`y&EM*X4v}UwnU}o6Bz|L52%V@*E!oUbBxuN0nn}L;q
zlYxOzaW_L(<aUN0pWO_-k=q&ieRnfVh!mQ%oneZf?ly*LLen8$1Bq^Dm}$iV^3p7=
zEe!LvF)Z4~uwom-I*`aTp)C+~I@=hwvF>Hq9?7g59LXZd61<aPClf<`xXvbqz1tWL
zYj0yX4h<}ji9#o~Gfc5!m1G6kbP8euNQopXgbj6*6&qOb8IWRjNp_H8G-q*0a%^LO
zI_n(NSsWmB7r_SAhfA`-9VW>Rav2B2W#N)+2$xB+fmMfVZ(_K-jp3TM2*dSl3^%nm
zFfc&lFO5Nxfq}uBfrG)1L4d)ZL5d-OL5(4hL7yRr!I2@D!H*$?A(bJNA%`KHp`0O_
zp@|`ep`Rg^p?)?)BEu4fB!=}2$qaiKQW%ahq%vG*NMm@ykk0UzA%l^XA&XIvA%{_d
zA(v5}As-ycF_8FyMluJZBsfAD83Y&w8SNPD8JHNP7}*&e7#$gy8T1)GF*-3iGO#c-
zG0bOlW^`m=Wmv*c%jm*r&%nm8zMi3o(UsAWft}$fLky!Eqay<c10$%qh9m~2i45!v
z+zbqi-H60+ClV6L_n;YrHK!fH+RpIES4V3X!&3%^Z456#F$RxRa3b;p$Jbj>e1Q{8
zJv<%hZe!Rc$pQ&xu%h=MMbH%R5h4y&15W`HB85JK)Ubh~5Y-%Lbb-tPrA=_sfF^>k
z&_n=EEI;az3}%P0L7IeqfeZ$x9aMukU~2w=LIG4R2(ZEZ4`Oa-_-Dllb|)vMQZ9s2
zuAFv|(t0kiS}urMU2yU`tSiFExQ&q+6neT432m^%HEj__7SwcC#~{zZz);M<!BEPe
z%}~x@%23JRz);QL!BEQ(%uvUWz);VS!_df3&d|ir!qChxfuWUQHbWc33Wj!uEexFu
z^?Mn*7|t_vGu&b5VR*sN$MBh<pOJxK0wWK@Bt~t9$&BU<QyIe;rZFZl%wWu9n8{ec
zFq^TOVJ>46!#u`Lh6UjCSO7^P@buWs2r5+=8MGM_7~L5?7?>D57@ZkC8SNRE8G;!t
z7`?#hv7Awa(Yqd;AZIgtVDw>hWME_1%W#g-7n~kHGt6N01EohsZH5F!f5rd?PR1|>
zQ^r8X00u4wCQz*nNuvyo3{2o+pA(cWwYM>{ZG>k_kU|EA6$~5<E5R;>)DN6cmohML
z)-wh%27@XH26b2k!H~+p%D~9Lz@WW}fnAu9b0-5o6N4}#H}eig-W`m>yBNh87`8D=
zYwc!O9Ld44ol$Ws!#sqIV&DRJ0|Ph1Mg}2<O$@ROTNtz$wt{Wvffx(5osltwL8Bh(
z2Ui9ru%s~9P9?CNAn$BrR6&>xsqywOa5L;>5MtPeWTr60OaU%XeG4^G0URg{3>?CY
z>MT1Lc|qndf-4&~uvMEFI2gk+;J#J@3o<Zp2{UT$VAS5sz>cm#mVucu92~R}jGz{j
t3<EP`6ayn;0%H;bGh-%W7GoxumBX0Jn8R4W0BUYAG8QrxGnO()0stQfXT|^k

diff --git a/tetrecs/tetrecs.iml b/tetrecs/tetrecs.iml
new file mode 100644
index 0000000..34834c2
--- /dev/null
+++ b/tetrecs/tetrecs.iml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="CheckStyle-IDEA-Module">
+    <option name="configuration">
+      <map />
+    </option>
+  </component>
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_17">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <excludeFolder url="file://$MODULE_DIR$/${project.build.directory}/classes" />
+      <excludeFolder url="file://$MODULE_DIR$/${project.build.directory}/test-classes" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Maven: org.openjfx:javafx-controls:17.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.openjfx:javafx-controls:win:17.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.openjfx:javafx-graphics:17.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.openjfx:javafx-graphics:win:17.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.openjfx:javafx-base:17.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.openjfx:javafx-base:win:17.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.openjfx:javafx-fxml:17.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.openjfx:javafx-fxml:win:17.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.openjfx:javafx-media:17.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.openjfx:javafx-media:win:17.0.2" level="project" />
+    <orderEntry type="library" name="Maven: com.neovisionaries:nv-websocket-client:2.14" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.17.1" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-core:2.17.1" level="project" />
+  </component>
+</module>
\ No newline at end of file
-- 
GitLab