From 9f49ad45d239277a69ed8db4e1e7372f4f29819a Mon Sep 17 00:00:00 2001 From: Martin Schreiber <mschreib@myune.edu.au> Date: Thu, 22 Jul 2021 14:40:24 +0930 Subject: [PATCH] initial upload --- .gitignore | 12 +- README.md | 246 +++++------ build.gradle | 82 ++-- gradle/wrapper/gradle-wrapper.properties | 10 +- gradlew | 370 ++++++++-------- gradlew.bat | 178 ++++---- settings.gradle | 18 +- .../java/dotsandboxes/DotsAndBoxesGrid.java | 396 +++++++++--------- .../java/dotsandboxes/DotsAndBoxesUI.java | 278 ++++++------ src/main/java/dotsandboxes/Main.java | 80 ++-- src/main/java/module-info.java | 36 +- .../dotsandboxes/DotsAndBoxesGridTest.java | 62 +-- src/test/resources/log4j2.xml | 24 +- 13 files changed, 896 insertions(+), 896 deletions(-) diff --git a/.gitignore b/.gitignore index 21f5973..e9cbfcc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -# This file tells git what files to "ignore" -# Typically, these are all the generated files (as we only commit source code, not binary output) - -.gradle/ -.idea/ -build/ +# This file tells git what files to "ignore" +# Typically, these are all the generated files (as we only commit source code, not binary output) + +.gradle/ +.idea/ +build/ diff --git a/README.md b/README.md index 3fde864..2374963 100644 --- a/README.md +++ b/README.md @@ -1,124 +1,124 @@ -## 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, -JUnit, and JavaFX on a very small codebase - 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 -(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. - -You will notice that this is a git repository, but it is provided to you just as a zip that you download. -If you run `git status`, you should see that you are on branch `main`. - -Dots And Boxes is normally a two-player pencil-and-paper game. See https://en.wikipedia.org/wiki/Dots_and_Boxes -In our version, a player "draws" a line by clicking on it. If it completes a square, they automatically claim the -square and get another turn. There are two known bugs in our code-base: - -1. The code for checking if a square is complete is wrong. It just returns `true` at the moment, whereas it should - test if the lines surrounding the square have been "drawn". -2. The grid doesn't complain if you "draw" a line that has already been drawn. It should throw an `IllegalStateException`. - -Remember, your task *isn't* just to fix the bugs. - -Your tasks: - -1. Create a repository on gitlab.une.edu.au and push the main branch of this repository to it **entirely unmodified**. - **You will need to set the visibility on your repository so that the marker can read from it**. - Repositories we cannot access cannot be marked (and will receive 0 until you can fix it) - -2. Set your name and email on the repository for your future commits, using `git config` - -3. Update the "name" label in the application to show **your name**. - Commit this change and push it to your **main** branch. - -3. Create an Issue in your GitLab project for fixing the errors in the assignment. - -4. Create and check out a branch for your bugfix. - This should follow the convention of having the issue number, followed by a hyphenated description of the issue. - e.g. `1-fix-assignment-errors`. - Push this branch to your remote repository. (From here on, I'll refer to this as your "issue branch") - -5. Create **unit tests** that will detect the errors in the code. Particularly: - * That the algorithm for testing whether a box is complete is wrong. - * That the DotsAndBoxesGrid currently permits you to "draw" a line that has already been drawn, whereas it should throw an exception. - -6. Commit these unit tests with an appropriately descriptive commit message. - **Do not fix the bugs in this commit. The unit tests should *fail*.** - Push this commit to your remote repository (on your issue branch) - -7. **Tag** this commit, with the label `testsfail`. Push this tag to your remote repository. - -8. Fix the bugs in the code. Run your unit tests again. The tests should now pass. - -9. Commit your bugfix. Push it to your issue branch. - -10. Check out `main`. Merge your issue branch to main. The tests should still pass. Push this to your gitlab repository. - -11. Close your issue with a reference to the commit hash in which you fixed the bugs. - -12. Paste the link to your gitlab repository into the assignment and submit. - -Marking. - -1. You have submitted a link to your repository on gitlab.une.edu.au and it is visible to the marker (1 point) -2. The first few commits on the main branch on the repository contains the starter code, showing me as author. - The last of my commits has the *same commit hash* as in the original code I gave you. - (i.e. you successfully pushed the original code I gave you to your GitLab repository). (1 point) -3. The next commit on the main branch adds your name to the label and shows you as author - (i.e. you have successfully configured your name & email and you have successfully created and pushed a commit) (1 point) -4. You have at least one issue on your repository for the bugfixes. It is closed referencing the bugfix commit. (1 point) -5. There is a `testsfail` tag in your repository (1 point) -6. Checking out the `testsfail` tag, and running the `gradlew test`, produces two failed tests (one for each bug). (2 points) -7. Checking out the main branch, both tests are present and pass (2 points) -8. The most recent commit on the `main` branch is a merge commit, merging the commit in which you set your name on the label - and the commit in which you fixed the tests. (1 point) - - -## Features of this code base - -As well as being an assignment repository, this codebase is also a sample codebase, showing you a few things: - -* It uses a *gradle wrapper* (a script that will download the correct version of gradle for you.) This means you can - `gradlew build` and it'll go get gradle before it builds. Normally, that would fetch from a URL on the gradle site, - but so this works behind UNE's webproxy, we've got it fetching the gradle jar from a url on turing instead. - -* It is a *modular* application. JavaFX is provided as a set of modules. At the moment, that might seem like an esoteric - distinction, but if we wanted to *distribute* our application, it would be helpful. If the app is written as a - modular app (it has a `module-info.java` file), then the `gradlew distZip` task can generate a distributable zip - for us, containing not only our code but also the modules our app depends on (including JavaFX). - -* It contains Log4J, which is the logging library (basically, super-powered printlns) that we will use. - -* We pass lambdas around, especially as listeners and event handlers. - Lambdas may be familiar to students who've studied Scala, but they are making - their way into Java too. For instance, notice that in `DotsAndBoxesUI` we register a number of listeners onto the dots - and boxes grid by registering lambda functions. - - ```java - grid.addConsumer((g) -> { - if (g.getHorizontal(x, y)) { - line.setStroke(Color.BLACK); - } else { - line.setStroke(Color.LIGHTGRAY); - } - }); - ``` - - In Java, functions aren't "first class citizens" in the way they are in Scala, however. Our code here is really - creating an *anonymous inner class*. - - Instead, Java has the idea of "SAM types" (Single Abstract Method types). `addConsumer` asks for an *object* of type - `Consumer<DotsAndBoxesGrid>`. But `Consumer<>` has only one abstract method. So, Java gives us the shorthand - that if we just describe how we want to implement the abstract method (`(g) -> { ...`) then the Java compiler will - fill in the rest of the code about creating an anonymous inner class of type `Consumer<>` for us. - -* JavaFX uses a lot of observable lists. So for instance notice the line for adding a line to the anchorpane: - - ```java - anchorPane.getChildren().add(line); - ``` - - We get the pane's list of children *and we add the line to it*. That *get ... add* code is something that is fairly - common in JavaFX code. The list of children is a mutable list that has some triggers on it, so that if you add +## 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, +JUnit, and JavaFX on a very small codebase - 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 +(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. + +You will notice that this is a git repository, but it is provided to you just as a zip that you download. +If you run `git status`, you should see that you are on branch `main`. + +Dots And Boxes is normally a two-player pencil-and-paper game. See https://en.wikipedia.org/wiki/Dots_and_Boxes +In our version, a player "draws" a line by clicking on it. If it completes a square, they automatically claim the +square and get another turn. There are two known bugs in our code-base: + +1. The code for checking if a square is complete is wrong. It just returns `true` at the moment, whereas it should + test if the lines surrounding the square have been "drawn". +2. The grid doesn't complain if you "draw" a line that has already been drawn. It should throw an `IllegalStateException`. + +Remember, your task *isn't* just to fix the bugs. + +Your tasks: + +1. Create a repository on gitlab.une.edu.au and push the main branch of this repository to it **entirely unmodified**. + **You will need to set the visibility on your repository so that the marker can read from it**. + Repositories we cannot access cannot be marked (and will receive 0 until you can fix it) + +2. Set your name and email on the repository for your future commits, using `git config` + +3. Update the "name" label in the application to show **your name**. + Commit this change and push it to your **main** branch. + +3. Create an Issue in your GitLab project for fixing the errors in the assignment. + +4. Create and check out a branch for your bugfix. + This should follow the convention of having the issue number, followed by a hyphenated description of the issue. + e.g. `1-fix-assignment-errors`. + Push this branch to your remote repository. (From here on, I'll refer to this as your "issue branch") + +5. Create **unit tests** that will detect the errors in the code. Particularly: + * That the algorithm for testing whether a box is complete is wrong. + * That the DotsAndBoxesGrid currently permits you to "draw" a line that has already been drawn, whereas it should throw an exception. + +6. Commit these unit tests with an appropriately descriptive commit message. + **Do not fix the bugs in this commit. The unit tests should *fail*.** + Push this commit to your remote repository (on your issue branch) + +7. **Tag** this commit, with the label `testsfail`. Push this tag to your remote repository. + +8. Fix the bugs in the code. Run your unit tests again. The tests should now pass. + +9. Commit your bugfix. Push it to your issue branch. + +10. Check out `main`. Merge your issue branch to main. The tests should still pass. Push this to your gitlab repository. + +11. Close your issue with a reference to the commit hash in which you fixed the bugs. + +12. Paste the link to your gitlab repository into the assignment and submit. + +Marking. + +1. You have submitted a link to your repository on gitlab.une.edu.au and it is visible to the marker (1 point) +2. The first few commits on the main branch on the repository contains the starter code, showing me as author. + The last of my commits has the *same commit hash* as in the original code I gave you. + (i.e. you successfully pushed the original code I gave you to your GitLab repository). (1 point) +3. The next commit on the main branch adds your name to the label and shows you as author + (i.e. you have successfully configured your name & email and you have successfully created and pushed a commit) (1 point) +4. You have at least one issue on your repository for the bugfixes. It is closed referencing the bugfix commit. (1 point) +5. There is a `testsfail` tag in your repository (1 point) +6. Checking out the `testsfail` tag, and running the `gradlew test`, produces two failed tests (one for each bug). (2 points) +7. Checking out the main branch, both tests are present and pass (2 points) +8. The most recent commit on the `main` branch is a merge commit, merging the commit in which you set your name on the label + and the commit in which you fixed the tests. (1 point) + + +## Features of this code base + +As well as being an assignment repository, this codebase is also a sample codebase, showing you a few things: + +* It uses a *gradle wrapper* (a script that will download the correct version of gradle for you.) This means you can + `gradlew build` and it'll go get gradle before it builds. Normally, that would fetch from a URL on the gradle site, + but so this works behind UNE's webproxy, we've got it fetching the gradle jar from a url on turing instead. + +* It is a *modular* application. JavaFX is provided as a set of modules. At the moment, that might seem like an esoteric + distinction, but if we wanted to *distribute* our application, it would be helpful. If the app is written as a + modular app (it has a `module-info.java` file), then the `gradlew distZip` task can generate a distributable zip + for us, containing not only our code but also the modules our app depends on (including JavaFX). + +* It contains Log4J, which is the logging library (basically, super-powered printlns) that we will use. + +* We pass lambdas around, especially as listeners and event handlers. + Lambdas may be familiar to students who've studied Scala, but they are making + their way into Java too. For instance, notice that in `DotsAndBoxesUI` we register a number of listeners onto the dots + and boxes grid by registering lambda functions. + + ```java + grid.addConsumer((g) -> { + if (g.getHorizontal(x, y)) { + line.setStroke(Color.BLACK); + } else { + line.setStroke(Color.LIGHTGRAY); + } + }); + ``` + + In Java, functions aren't "first class citizens" in the way they are in Scala, however. Our code here is really + creating an *anonymous inner class*. + + Instead, Java has the idea of "SAM types" (Single Abstract Method types). `addConsumer` asks for an *object* of type + `Consumer<DotsAndBoxesGrid>`. But `Consumer<>` has only one abstract method. So, Java gives us the shorthand + that if we just describe how we want to implement the abstract method (`(g) -> { ...`) then the Java compiler will + fill in the rest of the code about creating an anonymous inner class of type `Consumer<>` for us. + +* JavaFX uses a lot of observable lists. So for instance notice the line for adding a line to the anchorpane: + + ```java + anchorPane.getChildren().add(line); + ``` + + We get the pane's list of children *and we add the line to it*. That *get ... add* code is something that is fairly + common in JavaFX code. The list of children is a mutable list that has some triggers on it, so that if you add an element, JavaFX knows it needs to go and repaint the parent component. \ No newline at end of file diff --git a/build.gradle b/build.gradle index 5705373..b941866 100644 --- a/build.gradle +++ b/build.gradle @@ -1,42 +1,42 @@ -buildscript { - repositories { - maven { url 'https://hopper.une.edu.au/artifactory/libs-release/' } - mavenCentral() - } -} - -plugins { - id 'application' - id 'org.openjfx.javafxplugin' version '0.0.10' -} - -javafx { - version = "15.0.1" - modules = [ 'javafx.controls' ] -} - -group 'org.example' -version '1.0-SNAPSHOT' - - -repositories { - maven { url 'https://hopper.une.edu.au/artifactory/libs-release/' } - mavenCentral() -} - -dependencies { - // 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-core:2.12.0' - - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0' -} - -test { - useJUnitPlatform() -} - -application { - mainClass = 'dotsandboxes.Main' +buildscript { + repositories { + maven { url 'https://hopper.une.edu.au/artifactory/libs-release/' } + mavenCentral() + } +} + +plugins { + id 'application' + id 'org.openjfx.javafxplugin' version '0.0.10' +} + +javafx { + version = "15.0.1" + modules = [ 'javafx.controls' ] +} + +group 'org.example' +version '1.0-SNAPSHOT' + + +repositories { + maven { url 'https://hopper.une.edu.au/artifactory/libs-release/' } + mavenCentral() +} + +dependencies { + // 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-core:2.12.0' + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0' +} + +test { + useJUnitPlatform() +} + +application { + mainClass = 'dotsandboxes.Main' } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 068a225..4975f1c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://turing.une.edu.au/~cosc220/distributions/gradle-7.1.1-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://turing.une.edu.au/~cosc220/distributions/gradle-7.1.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0..02640cb 100755 --- a/gradlew +++ b/gradlew @@ -1,185 +1,185 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# 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"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - 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. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - 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 - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - 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 - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -exec "$JAVACMD" "$@" +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + 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. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + 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 + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + 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 + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 107acd3..ac1b06f 100755 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,89 +1,89 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle index a78f993..f6ebe9c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,9 +1,9 @@ -pluginManagement { - repositories { - maven { url "https://hopper.une.edu.au/artifactory/gradle-plugins/" } - gradlePluginPortal() - } -} - -rootProject.name = 'dotsAndBoxes' - +pluginManagement { + repositories { + maven { url "https://hopper.une.edu.au/artifactory/gradle-plugins/" } + gradlePluginPortal() + } +} + +rootProject.name = 'dotsAndBoxes' + diff --git a/src/main/java/dotsandboxes/DotsAndBoxesGrid.java b/src/main/java/dotsandboxes/DotsAndBoxesGrid.java index 4f774b1..32667af 100644 --- a/src/main/java/dotsandboxes/DotsAndBoxesGrid.java +++ b/src/main/java/dotsandboxes/DotsAndBoxesGrid.java @@ -1,198 +1,198 @@ -package dotsandboxes; - - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Observer; -import java.util.function.Consumer; - -/** - * The state of a dots and boxes grid. - * - * A (4, 3) dots and boxes grid looks like this: - * - * *-*-*-* - * | | | | - * *-*-*-* - * | | | | - * *-*-*-* - * - * Notice that: - * - * - for each row, there is one less horizontal than the number of corner dots - * - for each row, there are as many verticals as there are corner dots - * - for each row, there is one less box than the number of corner dots - * - for each column, there is one less vertical than the number of corner dots. - * - for each column, there are as many horizontals as there are corner dots. - * - for each column, there is one less box than the number of corner dots - * - * For example, in this (4, 3) grid, there are (3, 3) horizontal lines, and (4, 2) vertical lines, and (3, 2) boxes. - * - * We number all lines and boxes by their top-left coordinate. - * - * In Java 14+, we might use a Record class for this, but we're using 11+ as an LTS version, so we don't have that yet. - */ -public class DotsAndBoxesGrid { - - final int width; - final int height; - - /** The horizontal lines in the grid. True if drawn. */ - private boolean[][] horizontals; - - /** The vertical lines in the grid. True if drawn. */ - private boolean[][] verticals; - - /** Which owner (if any) claimed any given box. */ - private int[][] boxOwners; - - /** A list of functions to notify when there is an update */ - private ArrayList<Consumer<DotsAndBoxesGrid>> watchers = new ArrayList<>(); - - final int players; - private int player = 1; - public int getPlayer() { - return player; - } - - /** Moves to the next player */ - private void nextPlayer() { - player++; - if (player > players) { - player = 1; - } - } - - public DotsAndBoxesGrid(int width, int height, int players) { - this.width = width; - this.height = height; - this.players = players; - - this.horizontals = new boolean[width - 1][height]; - this.verticals = new boolean[width][height - 1]; - this.boxOwners = new int[width - 1][height - 1]; - } - - private void notifyObservers() { - for (Consumer<DotsAndBoxesGrid> consumer : watchers) { - consumer.accept(this); - } - } - - /** Listens to this grid for changes */ - public void addConsumer(Consumer<DotsAndBoxesGrid> consumer) { - watchers.add(consumer); - } - - /** Returns whether a horizontal line has been drawn */ - public boolean getHorizontal(int x, int y) { - return horizontals[x][y]; - } - - /** Returns whether a vertical line has been drawn */ - public boolean getVertical(int x, int y) { - return verticals[x][y]; - } - - /** Returns which player owns a box. By convention, 0 is unowned. */ - public int getBoxOwner(int x, int y) { - return boxOwners[x][y]; - } - - - /** - * Checks whether a box has been fully drawn (all four sides) - * @param x coordinate of the left side of the box - * @param y coordinate of the top of the box - * @return true if all four sides have been drawn. - */ - public boolean boxComplete(int x, int y) { - if (x >= width - 1 || x < 0 || y >= height - 1 || y < 0) { - return false; - } - - // A box is complete if the north and south horizontals and the east and west verticals have all been drawn. - // FIXME: You'll need to fix this code (after writing a test first). - return true; - } - - /** Tries to claim a box for a player. If the box is complete, sets the ownership and returns true. */ - private boolean claimBox(int x, int y, int p) { - if (boxComplete(x, y)) { - boxOwners[x][y] = player; - return true; - } else { - return false; - } - } - - /** - * "Draws" a horizontal line, from grid point (x, y) to (x + 1, y). (i.e. sets that line to true) - * @param x - * @param y - * @return true if it completes a box - */ - public boolean drawHorizontal(int x, int y, int player) { - if (x >= width - 1 || x < 0) { - throw new IndexOutOfBoundsException(String.format("x was %d, which is out of range. Range is 0 to %d", x, width - 1)); - } - if (y >= height || y < 0) { - throw new IndexOutOfBoundsException(String.format("y was %d, which is out of range. Range is 0 to %d", y, height)); - } - - // FIXME: You need to throw an exception if the line was already drawn. - - this.horizontals[x][y] = true; - - // Try to claim the north or south boxes - boolean claimN = claimBox(x, y-1, player); - boolean claimS = claimBox(x, y, player); - if (claimN || claimS) { - notifyObservers(); - return true; - } else { - nextPlayer(); - notifyObservers(); - return false; - } - } - - /** - * "Draws" a vertical line, from grid point (x, y) to (x, y + 1). (i.e. sets that line to true) - * @param x - * @param y - * @return true if it completes a box - */ - public boolean drawVertical(int x, int y, int player) { - if (x >= width || x < 0) { - throw new IndexOutOfBoundsException(String.format("x was %d, which is out of range. Range is 0 to %d", x, width)); - } - if (y >= height - 1 || y < 0) { - throw new IndexOutOfBoundsException(String.format("y was %d, which is out of range. Range is 0 to %d", y, height - 1)); - } - - // You need to throw an exception if the line was already drawn. - - this.verticals[x][y] = true; - // Try to claim the north or south boxes - boolean claimE = claimBox(x, y, player); - boolean claimW = claimBox(x-1, y, player); - if (claimE || claimW) { - notifyObservers(); - return true; - } else { - nextPlayer(); - notifyObservers(); - return false; - } - - } - - public boolean gameComplete() { - // Students who took COSC250 might recognise this style of code. This is Java's version of higher order functions. - // The grid is complete if "for all rows, all the boxes in that row have a non-zero owner" - return Arrays.stream(boxOwners).allMatch((row) -> Arrays.stream(row).allMatch((owner) -> owner > 0)); - } - - -} +package dotsandboxes; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Observer; +import java.util.function.Consumer; + +/** + * The state of a dots and boxes grid. + * + * A (4, 3) dots and boxes grid looks like this: + * + * *-*-*-* + * | | | | + * *-*-*-* + * | | | | + * *-*-*-* + * + * Notice that: + * + * - for each row, there is one less horizontal than the number of corner dots + * - for each row, there are as many verticals as there are corner dots + * - for each row, there is one less box than the number of corner dots + * - for each column, there is one less vertical than the number of corner dots. + * - for each column, there are as many horizontals as there are corner dots. + * - for each column, there is one less box than the number of corner dots + * + * For example, in this (4, 3) grid, there are (3, 3) horizontal lines, and (4, 2) vertical lines, and (3, 2) boxes. + * + * We number all lines and boxes by their top-left coordinate. + * + * In Java 14+, we might use a Record class for this, but we're using 11+ as an LTS version, so we don't have that yet. + */ +public class DotsAndBoxesGrid { + + final int width; + final int height; + + /** The horizontal lines in the grid. True if drawn. */ + private boolean[][] horizontals; + + /** The vertical lines in the grid. True if drawn. */ + private boolean[][] verticals; + + /** Which owner (if any) claimed any given box. */ + private int[][] boxOwners; + + /** A list of functions to notify when there is an update */ + private ArrayList<Consumer<DotsAndBoxesGrid>> watchers = new ArrayList<>(); + + final int players; + private int player = 1; + public int getPlayer() { + return player; + } + + /** Moves to the next player */ + private void nextPlayer() { + player++; + if (player > players) { + player = 1; + } + } + + public DotsAndBoxesGrid(int width, int height, int players) { + this.width = width; + this.height = height; + this.players = players; + + this.horizontals = new boolean[width - 1][height]; + this.verticals = new boolean[width][height - 1]; + this.boxOwners = new int[width - 1][height - 1]; + } + + private void notifyObservers() { + for (Consumer<DotsAndBoxesGrid> consumer : watchers) { + consumer.accept(this); + } + } + + /** Listens to this grid for changes */ + public void addConsumer(Consumer<DotsAndBoxesGrid> consumer) { + watchers.add(consumer); + } + + /** Returns whether a horizontal line has been drawn */ + public boolean getHorizontal(int x, int y) { + return horizontals[x][y]; + } + + /** Returns whether a vertical line has been drawn */ + public boolean getVertical(int x, int y) { + return verticals[x][y]; + } + + /** Returns which player owns a box. By convention, 0 is unowned. */ + public int getBoxOwner(int x, int y) { + return boxOwners[x][y]; + } + + + /** + * Checks whether a box has been fully drawn (all four sides) + * @param x coordinate of the left side of the box + * @param y coordinate of the top of the box + * @return true if all four sides have been drawn. + */ + public boolean boxComplete(int x, int y) { + if (x >= width - 1 || x < 0 || y >= height - 1 || y < 0) { + return false; + } + + // A box is complete if the north and south horizontals and the east and west verticals have all been drawn. + // FIXME: You'll need to fix this code (after writing a test first). + return true; + } + + /** Tries to claim a box for a player. If the box is complete, sets the ownership and returns true. */ + private boolean claimBox(int x, int y, int p) { + if (boxComplete(x, y)) { + boxOwners[x][y] = player; + return true; + } else { + return false; + } + } + + /** + * "Draws" a horizontal line, from grid point (x, y) to (x + 1, y). (i.e. sets that line to true) + * @param x + * @param y + * @return true if it completes a box + */ + public boolean drawHorizontal(int x, int y, int player) { + if (x >= width - 1 || x < 0) { + throw new IndexOutOfBoundsException(String.format("x was %d, which is out of range. Range is 0 to %d", x, width - 1)); + } + if (y >= height || y < 0) { + throw new IndexOutOfBoundsException(String.format("y was %d, which is out of range. Range is 0 to %d", y, height)); + } + + // FIXME: You need to throw an exception if the line was already drawn. + + this.horizontals[x][y] = true; + + // Try to claim the north or south boxes + boolean claimN = claimBox(x, y-1, player); + boolean claimS = claimBox(x, y, player); + if (claimN || claimS) { + notifyObservers(); + return true; + } else { + nextPlayer(); + notifyObservers(); + return false; + } + } + + /** + * "Draws" a vertical line, from grid point (x, y) to (x, y + 1). (i.e. sets that line to true) + * @param x + * @param y + * @return true if it completes a box + */ + public boolean drawVertical(int x, int y, int player) { + if (x >= width || x < 0) { + throw new IndexOutOfBoundsException(String.format("x was %d, which is out of range. Range is 0 to %d", x, width)); + } + if (y >= height - 1 || y < 0) { + throw new IndexOutOfBoundsException(String.format("y was %d, which is out of range. Range is 0 to %d", y, height - 1)); + } + + // You need to throw an exception if the line was already drawn. + + this.verticals[x][y] = true; + // Try to claim the north or south boxes + boolean claimE = claimBox(x, y, player); + boolean claimW = claimBox(x-1, y, player); + if (claimE || claimW) { + notifyObservers(); + return true; + } else { + nextPlayer(); + notifyObservers(); + return false; + } + + } + + public boolean gameComplete() { + // Students who took COSC250 might recognise this style of code. This is Java's version of higher order functions. + // The grid is complete if "for all rows, all the boxes in that row have a non-zero owner" + return Arrays.stream(boxOwners).allMatch((row) -> Arrays.stream(row).allMatch((owner) -> owner > 0)); + } + + +} diff --git a/src/main/java/dotsandboxes/DotsAndBoxesUI.java b/src/main/java/dotsandboxes/DotsAndBoxesUI.java index 66010e3..e646cc4 100644 --- a/src/main/java/dotsandboxes/DotsAndBoxesUI.java +++ b/src/main/java/dotsandboxes/DotsAndBoxesUI.java @@ -1,139 +1,139 @@ -package dotsandboxes; - -import javafx.scene.control.Label; -import javafx.scene.layout.AnchorPane; -import javafx.scene.paint.Color; -import javafx.scene.shape.Circle; -import javafx.scene.shape.Line; -import javafx.scene.shape.Rectangle; - -public class DotsAndBoxesUI { - - final int lineLength = 32; - final int margin = 10; - final int gap = 0; - final int dotDiameter = 6; - - final DotsAndBoxesGrid grid; - final AnchorPane anchorPane; - final Label label; - - /** 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 }; - - private void updateLabel() { - label.setTextFill(playerColours[grid.getPlayer()]); - label.setText(String.format("Player %d's turn", grid.getPlayer())); - } - - public DotsAndBoxesUI(final DotsAndBoxesGrid grid) { - this.grid = grid; - anchorPane = new AnchorPane(); - - label = new Label(""); - updateLabel(); - grid.addConsumer((g) -> updateLabel()); - - // Size the anchorPane to just contain the elements - 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); - - - // Lay out the boxes - 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) -> { - box.setFill(playerColours[g.getBoxOwner(x, y)]); - }); - - AnchorPane.setLeftAnchor(box, gap + dotDiameter + col * (gap + lineLength + gap + dotDiameter) + dotDiameter/2.0); - AnchorPane.setTopAnchor(box, gap + dotDiameter + row * (gap + lineLength + gap + dotDiameter) + dotDiameter/2.0); - anchorPane.getChildren().add(box); - } - } - - // Lay out the horizontals - for (int row = 0; row < grid.height; row++) { - for (int col = 0; col < grid.width - 1; col++) { - Line line = new Line(0, dotDiameter/2.0, lineLength, dotDiameter/2.0); - line.setStrokeWidth(dotDiameter); - line.setStroke(Color.DARKGREY); - - final int x = col; - final int y = row; - grid.addConsumer((g) -> { - if (g.getHorizontal(x, y)) { - line.setStroke(Color.BLACK); - } else { - line.setStroke(Color.LIGHTGRAY); - } - }); - - line.setOnMouseClicked((evt) -> {try { - grid.drawHorizontal(x, y, grid.getPlayer()); - } catch (IllegalStateException ex) { - // do nothing - // 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! - }}); - - AnchorPane.setLeftAnchor(line, 0.0 + gap + dotDiameter + col * (gap + lineLength + gap + dotDiameter)); - AnchorPane.setTopAnchor(line, -dotDiameter/2.0 + gap + dotDiameter + row * (gap + lineLength + gap + dotDiameter)); - anchorPane.getChildren().add(line); - } - } - - // Lay out the verticals - for (int row = 0; row < grid.height - 1; row++) { - for (int col = 0; col < grid.width; col++) { - Line line = new Line(-dotDiameter/2.0, 0, -dotDiameter/2.0, lineLength); - line.setStrokeWidth(dotDiameter); - line.setStroke(Color.DARKGREY); - - final int x = col; - final int y = row; - grid.addConsumer((g) -> { - if (g.getVertical(x, y)) { - line.setStroke(Color.BLACK); - } else { - line.setStroke(Color.LIGHTGRAY); - } - }); - - line.setOnMouseClicked((evt) -> {try { - grid.drawVertical(x, y, grid.getPlayer()); - } catch (IllegalStateException ex) { - // do nothing - }}); - - AnchorPane.setTopAnchor(line, 0.0 + gap + dotDiameter + row * (gap + lineLength + gap + dotDiameter)); - AnchorPane.setLeftAnchor(line, -dotDiameter/2.0 + gap + dotDiameter + col * (gap + lineLength + gap + dotDiameter)); - anchorPane.getChildren().add(line); - } - } - - // Lay out the dots - for (int row = 0; row < grid.height; row++) { - for (int col = 0; col < grid.width; col++) { - Circle dot = new Circle(dotDiameter / 2.0); - dot.setFill(Color.YELLOW); - - 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); - } - } - - } - - - -} +package dotsandboxes; + +import javafx.scene.control.Label; +import javafx.scene.layout.AnchorPane; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.scene.shape.Line; +import javafx.scene.shape.Rectangle; + +public class DotsAndBoxesUI { + + final int lineLength = 32; + final int margin = 10; + final int gap = 0; + final int dotDiameter = 6; + + final DotsAndBoxesGrid grid; + final AnchorPane anchorPane; + final Label label; + + /** 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 }; + + private void updateLabel() { + label.setTextFill(playerColours[grid.getPlayer()]); + label.setText(String.format("Player %d's turn", grid.getPlayer())); + } + + public DotsAndBoxesUI(final DotsAndBoxesGrid grid) { + this.grid = grid; + anchorPane = new AnchorPane(); + + label = new Label(""); + updateLabel(); + grid.addConsumer((g) -> updateLabel()); + + // Size the anchorPane to just contain the elements + 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); + + + // Lay out the boxes + 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) -> { + box.setFill(playerColours[g.getBoxOwner(x, y)]); + }); + + AnchorPane.setLeftAnchor(box, gap + dotDiameter + col * (gap + lineLength + gap + dotDiameter) + dotDiameter/2.0); + AnchorPane.setTopAnchor(box, gap + dotDiameter + row * (gap + lineLength + gap + dotDiameter) + dotDiameter/2.0); + anchorPane.getChildren().add(box); + } + } + + // Lay out the horizontals + for (int row = 0; row < grid.height; row++) { + for (int col = 0; col < grid.width - 1; col++) { + Line line = new Line(0, dotDiameter/2.0, lineLength, dotDiameter/2.0); + line.setStrokeWidth(dotDiameter); + line.setStroke(Color.DARKGREY); + + final int x = col; + final int y = row; + grid.addConsumer((g) -> { + if (g.getHorizontal(x, y)) { + line.setStroke(Color.BLACK); + } else { + line.setStroke(Color.LIGHTGRAY); + } + }); + + line.setOnMouseClicked((evt) -> {try { + grid.drawHorizontal(x, y, grid.getPlayer()); + } catch (IllegalStateException ex) { + // do nothing + // 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! + }}); + + AnchorPane.setLeftAnchor(line, 0.0 + gap + dotDiameter + col * (gap + lineLength + gap + dotDiameter)); + AnchorPane.setTopAnchor(line, -dotDiameter/2.0 + gap + dotDiameter + row * (gap + lineLength + gap + dotDiameter)); + anchorPane.getChildren().add(line); + } + } + + // Lay out the verticals + for (int row = 0; row < grid.height - 1; row++) { + for (int col = 0; col < grid.width; col++) { + Line line = new Line(-dotDiameter/2.0, 0, -dotDiameter/2.0, lineLength); + line.setStrokeWidth(dotDiameter); + line.setStroke(Color.DARKGREY); + + final int x = col; + final int y = row; + grid.addConsumer((g) -> { + if (g.getVertical(x, y)) { + line.setStroke(Color.BLACK); + } else { + line.setStroke(Color.LIGHTGRAY); + } + }); + + line.setOnMouseClicked((evt) -> {try { + grid.drawVertical(x, y, grid.getPlayer()); + } catch (IllegalStateException ex) { + // do nothing + }}); + + AnchorPane.setTopAnchor(line, 0.0 + gap + dotDiameter + row * (gap + lineLength + gap + dotDiameter)); + AnchorPane.setLeftAnchor(line, -dotDiameter/2.0 + gap + dotDiameter + col * (gap + lineLength + gap + dotDiameter)); + anchorPane.getChildren().add(line); + } + } + + // Lay out the dots + for (int row = 0; row < grid.height; row++) { + for (int col = 0; col < grid.width; col++) { + Circle dot = new Circle(dotDiameter / 2.0); + dot.setFill(Color.YELLOW); + + 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); + } + } + + } + + + +} diff --git a/src/main/java/dotsandboxes/Main.java b/src/main/java/dotsandboxes/Main.java index cd003d1..cd2bce8 100644 --- a/src/main/java/dotsandboxes/Main.java +++ b/src/main/java/dotsandboxes/Main.java @@ -1,40 +1,40 @@ -package dotsandboxes; - -import javafx.application.Application; -import javafx.scene.Scene; -import javafx.scene.control.Label; -import javafx.scene.layout.BorderPane; -import javafx.stage.Stage; - -/** Our main class that launches the app. */ -public class Main extends Application { - - DotsAndBoxesGrid grid = new DotsAndBoxesGrid(15, 8, 2); - - @Override - public void start(Stage primaryStage) throws Exception { - primaryStage.setTitle("Dots and Boxes"); - - // FIXME: Update this label to show your name and student number - Label label = new Label("Name: (Your name and student number goes here)"); - - BorderPane borderPane = new BorderPane(); - borderPane.setBottom(label); - Scene scene = new Scene(borderPane, 600, 400); - - DotsAndBoxesUI dbUi = new DotsAndBoxesUI(grid); - borderPane.setCenter(dbUi.anchorPane); - borderPane.setTop(dbUi.label); - - primaryStage.setScene(scene); - - primaryStage.show(); - - // 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 - // 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)); - } - -} +package dotsandboxes; + +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.layout.BorderPane; +import javafx.stage.Stage; + +/** Our main class that launches the app. */ +public class Main extends Application { + + DotsAndBoxesGrid grid = new DotsAndBoxesGrid(15, 8, 2); + + @Override + public void start(Stage primaryStage) throws Exception { + primaryStage.setTitle("Dots and Boxes"); + + // FIXME: Update this label to show your name and student number + Label label = new Label("Name: (Your name and student number goes here)"); + + BorderPane borderPane = new BorderPane(); + borderPane.setBottom(label); + Scene scene = new Scene(borderPane, 600, 400); + + DotsAndBoxesUI dbUi = new DotsAndBoxesUI(grid); + borderPane.setCenter(dbUi.anchorPane); + borderPane.setTop(dbUi.label); + + primaryStage.setScene(scene); + + primaryStage.show(); + + // 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 + // 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)); + } + +} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index eb9361f..e51a231 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,19 +1,19 @@ -/** - * 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; +/** + * 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 diff --git a/src/test/java/dotsandboxes/DotsAndBoxesGridTest.java b/src/test/java/dotsandboxes/DotsAndBoxesGridTest.java index 1946bed..1aac7cc 100644 --- a/src/test/java/dotsandboxes/DotsAndBoxesGridTest.java +++ b/src/test/java/dotsandboxes/DotsAndBoxesGridTest.java @@ -1,31 +1,31 @@ -package dotsandboxes; - -import org.junit.jupiter.api.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.Assumptions.*; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class DotsAndBoxesGridTest { - /* - * Because Test classes are classes, they can have fields, and can have static fields. - * This field is a logger. Loggers are like a more advanced println, for writing messages out to the console or a log file. - */ - private static final Logger logger = LogManager.getLogger(DotsAndBoxesGridTest.class); - - /* - * Tests are functions that have an @Test annotation before them. - * The typical format of a test is that it contains some code that does something, and then one - * or more assertions to check that a condition holds. - * - * This is a dummy test just to show that the test suite itself runs - */ - @Test - public void testTestSuiteRuns() { - logger.info("Dummy test to show the test suite runs"); - assertTrue(true); - } - - // FIXME: You need to write tests for the two known bugs in the code. -} +package dotsandboxes; + +import org.junit.jupiter.api.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.*; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class DotsAndBoxesGridTest { + /* + * Because Test classes are classes, they can have fields, and can have static fields. + * This field is a logger. Loggers are like a more advanced println, for writing messages out to the console or a log file. + */ + private static final Logger logger = LogManager.getLogger(DotsAndBoxesGridTest.class); + + /* + * Tests are functions that have an @Test annotation before them. + * The typical format of a test is that it contains some code that does something, and then one + * or more assertions to check that a condition holds. + * + * This is a dummy test just to show that the test suite itself runs + */ + @Test + public void testTestSuiteRuns() { + logger.info("Dummy test to show the test suite runs"); + assertTrue(true); + } + + // FIXME: You need to write tests for the two known bugs in the code. +} diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml index 32c8f6b..fc48a88 100644 --- a/src/test/resources/log4j2.xml +++ b/src/test/resources/log4j2.xml @@ -1,13 +1,13 @@ -<?xml version="1.0" encoding="UTF-8"?> -<Configuration status="WARN"> - <Appenders> - <Console name="Console" target="SYSTEM_OUT"> - <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> - </Console> - </Appenders> - <Loggers> - <Root level="debug"> - <AppenderRef ref="Console"/> - </Root> - </Loggers> +<?xml version="1.0" encoding="UTF-8"?> +<Configuration status="WARN"> + <Appenders> + <Console name="Console" target="SYSTEM_OUT"> + <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> + </Console> + </Appenders> + <Loggers> + <Root level="debug"> + <AppenderRef ref="Console"/> + </Root> + </Loggers> </Configuration> \ No newline at end of file -- GitLab