Skip to content
Snippets Groups Projects
Commit 1e11e1c5 authored by William Billingsley's avatar William Billingsley
Browse files

Updated for 2022

This switches to just using Swing and no modules for better compatibility with working with VS Code.
parent 53fceabc
Branches
Tags
No related merge requests found
FROM openjdk:17-jdk-slim-bullseye
# Install some basic development utilities
RUN apt-get update && apt-get install -y \
curl \
git \
python3 \
zip \
&& rm -rf /var/lib/apt/lists/* \
&& curl -fsSL https://deb.nodesource.com/setup_17.x | bash \
&& apt-get install -y nodejs \
&& npm install -g yarn \
&& rm -rf /var/lib/apt/lists/*
# User the Java-based launcher as Alpine Linux and Graal native compiled images of CS don't like
# Alpine Linux (due to musl instead of gcc)
RUN bash -c 'curl -fLo /usr/bin/cs https://git.io/coursier-cli' && \
chmod u+x /usr/bin/cs
# Put Coursier-installed artifacts on the path
ENV PATH="$PATH:/root/.local/share/coursier/bin"
# Get sbt
RUN bash -c 'cs install sbt'
# Get http server
RUN bash -c 'npm install -g http-server'
# Precache sbt version as part of the image.
ARG SBT_VERSION=1.7.0
ARG SCALA_VERSION=3.1.3
ARG SCALAJS_VERSION=1.10.1
RUN mkdir -p /sbtprecacheproj/project && \
cd /sbtprecacheproj && \
echo sbt.version=${SBT_VERSION} > project/build.properties && \
echo 'addSbtPlugin("org.scala-js" % "sbt-scalajs" % "'${SCALAJS_VERSION}'")' > project/plugins.sbt && \
echo '@main def hello = println("hello")' > main.scala && \
echo 'enablePlugins(ScalaJSPlugin); scalaVersion := "'${SCALA_VERSION}'"' > build.sbt && \
sbt fastLinkJS && cd / && rm -r -f /tmp/sbt-precompile
{
"name": "Doctacular codespace config",
"build": {
"dockerfile": "Dockerfile"
},
"extensions": [
"vscjava.vscode-java-pack"
]
}
\ No newline at end of file
...@@ -4,3 +4,6 @@ ...@@ -4,3 +4,6 @@
.gradle/ .gradle/
.idea/ .idea/
build/ build/
.vscode
bin/
## A Git, GitLab, and JUnit exercise ## A Git, GitLab, and JUnit exercise
This assignment is intended to give you a relatively small exercise to give you a little familiarisation working with Git, Gradle, This assignment is intended to give you a relatively small exercise to give you a little familiarisation working with Git, Gradle,
JUnit, and JavaFX on a very small codebase - before you and your group do something much more exciting on the much larger JUnit, and some more recent Java language features on a very small codebase -
class-wide project. before you and your group do something much more exciting on the much larger class-wide project.
The example code you have been given is a simple JavaFX "dots and boxes" game. It has a couple of known bugs in it The example code you have been given is a simple Java "dots and boxes" game. It has a couple of known bugs in it
(already commented for you - the task is not really in finding the bugs). Please note: your task **is not simply to fix the bugs**. (already commented for you - the task is not really in finding the bugs). Please note: your task **is not simply to fix the bugs**.
Your task is a series of steps that show you can work with git, gitlab, gradle, and junit. Your task is a series of steps that show you can work with git, gitlab, gradle, and junit.
......
...@@ -7,12 +7,6 @@ buildscript { ...@@ -7,12 +7,6 @@ buildscript {
plugins { plugins {
id 'application' id 'application'
id 'org.openjfx.javafxplugin' version '0.0.10'
}
javafx {
version = "15.0.1"
modules = [ 'javafx.controls' ]
} }
group 'org.example' group 'org.example'
...@@ -26,8 +20,8 @@ repositories { ...@@ -26,8 +20,8 @@ repositories {
dependencies { dependencies {
// Log4J does logging. We'll meet it properly in a later week... // Log4J does logging. We'll meet it properly in a later week...
implementation 'org.apache.logging.log4j:log4j-api:2.12.0' implementation 'org.apache.logging.log4j:log4j-api:2.18.0'
implementation 'org.apache.logging.log4j:log4j-core:2.12.0' implementation 'org.apache.logging.log4j:log4j-core:2.18.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
...@@ -40,3 +34,13 @@ test { ...@@ -40,3 +34,13 @@ test {
application { application {
mainClass = 'dotsandboxes.Main' mainClass = 'dotsandboxes.Main'
} }
task fatJar(type: Jar) {
manifest {
attributes 'Main-Class': 'dotsandboxes.Main'
}
baseName = 'executable-jar'
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
\ No newline at end of file
No preview for this file type
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://turing.une.edu.au/~cosc220/distributions/gradle-7.1.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
#!/usr/bin/env sh #!/bin/sh
# #
# Copyright 2015 the original author or authors. # Copyright © 2015-2021 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
...@@ -17,67 +17,101 @@ ...@@ -17,67 +17,101 @@
# #
############################################################################## ##############################################################################
## #
## Gradle start up script for UN*X # Gradle start up script for POSIX generated by Gradle.
## #
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
PRG="$0" app_path=$0
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do # Need this for daisy-chained symlinks.
ls=`ls -ld "$PRG"` while
link=`expr "$ls" : '.*-> \(.*\)$'` APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
if expr "$link" : '/.*' > /dev/null; then [ -h "$app_path" ]
PRG="$link" do
else ls=$( ls -ld "$app_path" )
PRG=`dirname "$PRG"`"/$link" link=${ls#*' -> '}
fi case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn () { warn () {
echo "$*" echo "$*"
} } >&2
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "`uname`" in case "$( uname )" in #(
CYGWIN* ) CYGWIN* ) cygwin=true ;; #(
cygwin=true Darwin* ) darwin=true ;; #(
;; MSYS* | MINGW* ) msys=true ;; #(
Darwin* ) NONSTOP* ) nonstop=true ;;
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
...@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar ...@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
...@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the ...@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD="java" JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
...@@ -106,80 +140,95 @@ location of your Java installation." ...@@ -106,80 +140,95 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD=$( ulimit -H -n ) ||
MAX_FD="$MAX_FD_LIMIT" warn "Could not query maximum file descriptor limit"
fi esac
ulimit -n $MAX_FD case $MAX_FD in #(
if [ $? -ne 0 ] ; then '' | soft) :;; #(
warn "Could not set maximum file descriptor limit: $MAX_FD" *)
fi ulimit -n "$MAX_FD" ||
else warn "Could not set maximum file descriptor limit to $MAX_FD"
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" esac
fi
fi fi
# For Darwin, add options to specify how the application appears in the dock # Collect all arguments for the java command, stacking in reverse order:
if $darwin; then # * args from the command line
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" # * the main class name
fi # * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then if "$cygwin" || "$msys" ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition JAVACMD=$( cygpath --unix "$JAVACMD" )
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else # Now convert the arguments - kludge to limit ourselves to /bin/sh
eval `echo args$i`="\"$arg\"" for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi fi
i=`expr $i + 1` # Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi fi
# Escape application args # Collect all arguments for the java command;
save () { # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done # shell script including quotes and variable substitutions, so put them in
echo " " # double quotes to make sure that they get re-expanded; and
} # * put everything else in single quotes, so that it's not re-expanded.
APP_ARGS=`save "$@"`
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
# Collect all arguments for the java command, following the shell quoting and substitution rules eval "set -- $(
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"
...@@ -3,7 +3,6 @@ package dotsandboxes; ...@@ -3,7 +3,6 @@ package dotsandboxes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Observer;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
......
package dotsandboxes; package dotsandboxes;
import javafx.scene.control.Label; import java.awt.*;
import javafx.scene.layout.AnchorPane; import java.awt.event.*;
import javafx.scene.paint.Color; import javax.swing.*;
import javafx.scene.shape.Circle; import javax.swing.event.MouseInputAdapter;
import javafx.scene.shape.Line; import java.util.ArrayList;
import javafx.scene.shape.Rectangle;
public class DotsAndBoxesUI { public class DotsAndBoxesUI {
final int lineLength = 32; static final int lineLength = 32;
final int margin = 10; static final int margin = 10;
final int gap = 0; static final int gap = 0;
final int dotDiameter = 6; static final int dotDiameter = 6;
final DotsAndBoxesGrid grid; // The coordinate of the top or left of a the painting area for this row/col
final AnchorPane anchorPane; private static int corner(int col) {
final Label label; return margin + col * (gap + lineLength + gap + dotDiameter);
}
/** Colours for the different players. Only goes up to 5. */ /** Colours for the different players. Only goes up to 5. */
final Color[] playerColours = { Color.WHITE, Color.RED, Color.BLUE, Color.GREEN, Color.PURPLE, Color.ORANGE }; static final Color[] playerColours = { Color.WHITE, Color.RED, Color.BLUE, Color.GREEN, Color.PINK, Color.ORANGE };
final DotsAndBoxesGrid grid;
final JPanel anchorPane;
final Canvas canvas;
final JLabel label;
private void updateLabel() { private void updateLabel() {
label.setTextFill(playerColours[grid.getPlayer()]); label.setForeground(playerColours[grid.getPlayer()]);
label.setText(String.format("Player %d's turn", grid.getPlayer())); label.setText(String.format("Player %d's turn", grid.getPlayer()));
} }
public DotsAndBoxesUI(final DotsAndBoxesGrid grid) { public DotsAndBoxesUI(final DotsAndBoxesGrid grid) {
this.grid = grid; this.grid = grid;
anchorPane = new AnchorPane(); anchorPane = new JPanel(new BorderLayout());
label = new Label(""); label = new JLabel("");
updateLabel(); updateLabel();
grid.addConsumer((g) -> updateLabel());
// Size the anchorPane to just contain the elements canvas = new DABCanvas();
int width = margin + dotDiameter + gap + (grid.width - 1) * (gap + lineLength + gap + dotDiameter) + gap + margin;
int height = margin + dotDiameter + gap + (grid.height - 1) * (gap + lineLength + gap + dotDiameter) + gap + margin;
anchorPane.setPrefSize(width, height);
anchorPane.add(canvas, BorderLayout.CENTER);
// Lay out the boxes anchorPane.add(label, BorderLayout.NORTH);
for (int row = 0; row < grid.height - 1; row++) {
for (int col = 0; col < grid.width - 1; col++) {
final int x = col;
final int y = row;
Rectangle box = new Rectangle(gap, gap, lineLength, lineLength);
box.setFill(Color.WHITE);
grid.addConsumer((g) -> { grid.addConsumer((g) -> {
box.setFill(playerColours[g.getBoxOwner(x, y)]); updateLabel();
canvas.repaint();
}); });
}
/** A component that paints and handles clicks on the game */
class DABCanvas extends Canvas {
final ArrayList<Horizontal> horizontals = new ArrayList<>();
final ArrayList<Vertical> verticals = new ArrayList<>();
final ArrayList<Box> boxes = new ArrayList<>();
AnchorPane.setLeftAnchor(box, gap + dotDiameter + col * (gap + lineLength + gap + dotDiameter) + dotDiameter/2.0); /** Represents a horizontal line. */
AnchorPane.setTopAnchor(box, gap + dotDiameter + row * (gap + lineLength + gap + dotDiameter) + dotDiameter/2.0); record Horizontal(int col, int row) {
anchorPane.getChildren().add(box); Rectangle rect() {
int x = corner(col) + dotDiameter + gap;
int y = corner(row);
return new Rectangle(x, y, lineLength, dotDiameter);
} }
/** Whether or not this line contains this point */
boolean contains(int x, int y) {
return rect().contains(x, y);
} }
// Lay out the horizontals /** Paints this element, based on the passed in grid */
for (int row = 0; row < grid.height; row++) { public void draw(DotsAndBoxesGrid grid, Graphics2D g2d) {
for (int col = 0; col < grid.width - 1; col++) { g2d.setColor(grid.getHorizontal(col, row) ? Color.DARK_GRAY : Color.LIGHT_GRAY);
Line line = new Line(0, dotDiameter/2.0, lineLength, dotDiameter/2.0); g2d.fill(this.rect());
line.setStrokeWidth(dotDiameter); }
line.setStroke(Color.DARKGREY); }
final int x = col; /** Represents a horizontal line. */
final int y = row; record Vertical(int col, int row) {
grid.addConsumer((g) -> { Rectangle rect() {
if (g.getHorizontal(x, y)) { int x = corner(col);
line.setStroke(Color.BLACK); int y = corner(row) + dotDiameter + gap;
} else { return new Rectangle(x, y, dotDiameter, lineLength);
line.setStroke(Color.LIGHTGRAY); }
/** Whether or not this line contains this point */
boolean contains(int x, int y) {
return rect().contains(x, y);
}
/** Paints this element, based on the passed in grid */
public void draw(DotsAndBoxesGrid grid, Graphics2D g2d) {
g2d.setColor(grid.getVertical(col, row) ? Color.DARK_GRAY : Color.LIGHT_GRAY);
g2d.fill(this.rect());
}
}
/** represents a box */
record Box(int col, int row) {
Rectangle rect() {
int x = corner(col) + dotDiameter + gap;
int y = corner(row) + dotDiameter + gap;
return new Rectangle(x, y, lineLength, lineLength);
}
/** Whether or not this line contains this point */
boolean contains(int x, int y) {
return rect().contains(x, y);
} }
});
line.setOnMouseClicked((evt) -> {try { /** Paints this element, based on the passed in grid */
grid.drawHorizontal(x, y, grid.getPlayer()); public void draw(DotsAndBoxesGrid grid, Graphics2D g2d) {
} catch (IllegalStateException ex) { g2d.setColor(playerColours[grid.getBoxOwner(col, row)]);
// do nothing g2d.fill(this.rect());
// This is a little artificial, as normally we'd implement this with a check that the line isn't }
// already "drawn" and then not calling the function. But for the exercise, we wanted students }
// to write a test that would ensure an exception is thrown, so we're relying on an exception
// being thrown!
}}); public DABCanvas() {
// Size the canvas to just contain the elements
int width = corner(grid.width) + margin;
int height = corner(grid.height) + margin;
this.setPreferredSize(new Dimension(width, height));
// Create records for the boxes
for (int row = 0; row < grid.height - 1; row++) {
for (int col = 0; col < grid.width - 1; col++) {
boxes.add(new Box(col, row));
}
}
AnchorPane.setLeftAnchor(line, 0.0 + gap + dotDiameter + col * (gap + lineLength + gap + dotDiameter)); // Create records for horizontals
AnchorPane.setTopAnchor(line, -dotDiameter/2.0 + gap + dotDiameter + row * (gap + lineLength + gap + dotDiameter)); for (int row = 0; row < grid.height; row++) {
anchorPane.getChildren().add(line); for (int col = 0; col < grid.width - 1; col++) {
horizontals.add(new Horizontal(col, row));
} }
} }
// Lay out the verticals // Create records for verticals
for (int row = 0; row < grid.height - 1; row++) { for (int row = 0; row < grid.height - 1; row++) {
for (int col = 0; col < grid.width; col++) { for (int col = 0; col < grid.width; col++) {
Line line = new Line(-dotDiameter/2.0, 0, -dotDiameter/2.0, lineLength); verticals.add(new Vertical(col, row));
line.setStrokeWidth(dotDiameter); }
line.setStroke(Color.DARKGREY); }
final int x = col; addMouseListener(new MouseInputAdapter() {
final int y = row; @Override public void mousePressed(MouseEvent e) {
grid.addConsumer((g) -> { for (Horizontal h : horizontals) {
if (g.getVertical(x, y)) { if (h.contains(e.getX(), e.getY())) {
line.setStroke(Color.BLACK); grid.drawHorizontal(h.col(), h.row(), grid.getPlayer());
} else { }
line.setStroke(Color.LIGHTGRAY); }
for (Vertical v : verticals) {
if (v.contains(e.getX(), e.getY())) {
grid.drawVertical(v.col(), v.row(), grid.getPlayer());
}
}
} }
}); });
line.setOnMouseClicked((evt) -> {try { }
grid.drawVertical(x, y, grid.getPlayer());
} catch (IllegalStateException ex) { @Override public void paint(Graphics g) {
// do nothing Graphics2D g2d = (Graphics2D)g;
}}); g.clearRect(0, 0, this.getWidth(), this.getHeight());
g2d.setColor(Color.WHITE);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
AnchorPane.setTopAnchor(line, 0.0 + gap + dotDiameter + row * (gap + lineLength + gap + dotDiameter)); // Paint the boxes
AnchorPane.setLeftAnchor(line, -dotDiameter/2.0 + gap + dotDiameter + col * (gap + lineLength + gap + dotDiameter)); for (Box b : boxes) {
anchorPane.getChildren().add(line); b.draw(grid, g2d);
} }
// Paint the horizontals
for (Horizontal h : horizontals) {
h.draw(grid, g2d);
}
// Paint the boxes
for (Vertical v : verticals) {
v.draw(grid, g2d);
} }
// Lay out the dots // Draw the dots
for (int row = 0; row < grid.height; row++) { for (int row = 0; row < grid.height; row++) {
for (int col = 0; col < grid.width; col++) { for (int col = 0; col < grid.width; col++) {
Circle dot = new Circle(dotDiameter / 2.0); g2d.setColor(Color.BLACK);
dot.setFill(Color.YELLOW); g2d.fillOval(corner(col), corner(row), dotDiameter, dotDiameter);
}
AnchorPane.setLeftAnchor(dot, gap + col * (gap + lineLength + gap + dotDiameter) + dotDiameter/2.0);
AnchorPane.setTopAnchor(dot, gap + row * (gap + lineLength + gap + dotDiameter) + dotDiameter/2.0);
anchorPane.getChildren().add(dot);
} }
} }
...@@ -136,4 +196,5 @@ public class DotsAndBoxesUI { ...@@ -136,4 +196,5 @@ public class DotsAndBoxesUI {
} }
package dotsandboxes; package dotsandboxes;
import javafx.application.Application; import javax.swing.*;
import javafx.scene.Scene; import java.awt.*;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
/** Our main class that launches the app. */ /** Our main class that launches the app. */
public class Main extends Application { public class Main {
DotsAndBoxesGrid grid = new DotsAndBoxesGrid(15, 8, 2); public static void main(String... args) throws Exception {
@Override JFrame mainWindow = new JFrame("Dots and Boxes");
public void start(Stage primaryStage) throws Exception { DotsAndBoxesGrid grid = new DotsAndBoxesGrid(15, 8, 2);
primaryStage.setTitle("Dots and Boxes");
// FIXME: Update this label to show your name and student number // FIXME: Update this label to show your name and student number
Label label = new Label("Name: (Your name and student number goes here)"); JLabel label = new JLabel("Name: (Your name and student number goes here)");
BorderPane borderPane = new BorderPane(); JPanel borderPane = new JPanel(new BorderLayout());
borderPane.setBottom(label); borderPane.add(label, BorderLayout.SOUTH);
Scene scene = new Scene(borderPane, 600, 400);
DotsAndBoxesUI dbUi = new DotsAndBoxesUI(grid); DotsAndBoxesUI dbUi = new DotsAndBoxesUI(grid);
borderPane.setCenter(dbUi.anchorPane); borderPane.add(dbUi.anchorPane, BorderLayout.CENTER);
borderPane.setTop(dbUi.label); borderPane.add(dbUi.label, BorderLayout.NORTH);
mainWindow.add(borderPane);
primaryStage.setScene(scene);
primaryStage.show(); mainWindow.pack();
mainWindow.setVisible(true);
// This sets what to do when we close the main window. // This sets what to do when we close the main window.
// Notice that we are using a "lambda function" (i.e., an anonymously defined function defined within the mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// call to setOnCloseRequest). These are very useful in GUI code and we'll probably see a lot of them in the
// project.
primaryStage.setOnCloseRequest((evt) -> System.exit(0));
} }
} }
/**
* This is a module file.
*
* Modules were introduced in Java 9, allowing modules to express dependencies on other modules and to publish their
* public packages.
*
* Using modules makes things easier when working with JavaFX, because JavaFX is itself published as a module.
*
* Please note: "module" in Java is not quite the same as "dependency" in Gradle. They are similar concepts, but
* "modules" is part of the Java language (but doesn't express how to find them on the internet) whereas
* "dependencies" is part of the Gradle build system and expresses how to find them on the internet (but does not
* specify the module imports to the Java compiler). We need both.
*/
module dotsAndBoxes {
requires javafx.graphics;
requires javafx.controls;
requires org.apache.logging.log4j;
exports dotsandboxes;
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment